From a35c0c17b7cf1d23444658f5c2c91240699d302e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:16:25 +0100 Subject: [PATCH 1/7] Fix database migration - Previous version did not escape strings correctly - Use SQLiteStatement to pass the values --- .../room/NoteDatabase.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java index 732864c..ec58c90 100644 --- a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java +++ b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java @@ -28,6 +28,7 @@ import androidx.room.migration.Migration; import androidx.sqlite.db.SimpleSQLiteQuery; import androidx.sqlite.db.SupportSQLiteDatabase; +import androidx.sqlite.db.SupportSQLiteStatement; import org.secuso.privacyfriendlynotes.room.dao.CategoryDao; import org.secuso.privacyfriendlynotes.room.dao.NoteDao; @@ -37,12 +38,7 @@ import org.secuso.privacyfriendlynotes.room.model.Notification; import java.io.File; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Date; -import java.util.Objects; /** * The database that includes all used information like notes, notifications and categories. @@ -57,7 +53,7 @@ public abstract class NoteDatabase extends RoomDatabase { public static final int VERSION = 7; public static final String DATABASE_NAME = "allthenotes"; - static final Migration MIGRATION_6_7 = new Migration(6,7) { + static final Migration MIGRATION_6_7 = new Migration(6, 7) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE notes ADD COLUMN readonly INTEGER NOT NULL DEFAULT 0;"); @@ -76,6 +72,11 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { + "last_modified INTEGER NOT NULL DEFAULT(unixepoch('subsec') * 1000)," + "custom_order INTEGER NOT NULL DEFAULT 0," + "PRIMARY KEY(_id));"); + // Prepare INSERT statement + SupportSQLiteStatement stmt = database.compileStatement( + "INSERT INTO notes_new(_id, in_trash, name, type, category, content, last_modified, custom_order)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?);" + ); try (Cursor cursor = database.query(new SimpleSQLiteQuery("SELECT * FROM notes;"))) { while (cursor.moveToNext()) { @SuppressLint("Range") String lastModified = cursor.getString(cursor.getColumnIndex("last_modified")); @@ -87,8 +88,20 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { @SuppressLint("Range") int category = cursor.getInt(cursor.getColumnIndex("category")); @SuppressLint("Range") String content = cursor.getString(cursor.getColumnIndex("content")); @SuppressLint("Range") int custom_order = cursor.getInt(cursor.getColumnIndex("custom_order")); - String value = String.format("(%s,%s,'%s',%s,%s,'%s',%s,%s)", _id, in_trash, name, type, category, content, lastModifiedMillis, custom_order); - database.execSQL("INSERT INTO notes_new(_id, in_trash,name,type,category,content,last_modified,custom_order) VALUES" + value + ";"); + stmt.bindLong(1, _id); + stmt.bindLong(2, in_trash); + stmt.bindString(3, name); + stmt.bindLong(4, type); + stmt.bindLong(5, category); + stmt.bindString(6, content); + stmt.bindLong(7, lastModifiedMillis); + stmt.bindLong(8, custom_order); + + // Execute insert + stmt.executeInsert(); + + // Clear bindings to be ready for next row + stmt.clearBindings(); } } database.execSQL("DROP TABLE notes;"); From 35416440108c999567b84df08ecfaa294a6e1167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:27:06 +0100 Subject: [PATCH 2/7] Add missing database schemas --- .../6.json | 134 +++++++++++++++++ .../7.json | 140 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json create mode 100644 app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json diff --git a/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json new file mode 100644 index 0000000..5e16162 --- /dev/null +++ b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json @@ -0,0 +1,134 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "dfaa34068d0a54f9cf797b7c441c2d92", + "entities": [ + { + "tableName": "notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `content` TEXT NOT NULL, `type` INTEGER NOT NULL, `category` INTEGER NOT NULL, `in_trash` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, `custom_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "in_trash", + "columnName": "in_trash", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_modified", + "columnName": "last_modified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "custom_order", + "columnName": "custom_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` TEXT)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_noteId` INTEGER NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`_noteId`))", + "fields": [ + { + "fieldPath": "_noteId", + "columnName": "_noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_noteId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dfaa34068d0a54f9cf797b7c441c2d92')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json new file mode 100644 index 0000000..74af8c8 --- /dev/null +++ b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json @@ -0,0 +1,140 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "8143c5ddeca8ab31795f588769331b34", + "entities": [ + { + "tableName": "notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `content` TEXT NOT NULL, `type` INTEGER NOT NULL, `category` INTEGER NOT NULL, `in_trash` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, `custom_order` INTEGER NOT NULL, `readonly` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "in_trash", + "columnName": "in_trash", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_modified", + "columnName": "last_modified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "custom_order", + "columnName": "custom_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readonly", + "columnName": "readonly", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` TEXT)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_noteId` INTEGER NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`_noteId`))", + "fields": [ + { + "fieldPath": "_noteId", + "columnName": "_noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_noteId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8143c5ddeca8ab31795f588769331b34')" + ] + } +} \ No newline at end of file From 43de9aa265e42f67bc2df7006468551c45e8c2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:59:58 +0100 Subject: [PATCH 3/7] Add fallback if we can not parse the date - Resolves #216 --- .../privacyfriendlynotes/room/NoteDatabase.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java index ec58c90..16504fb 100644 --- a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java +++ b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java @@ -38,7 +38,10 @@ import org.secuso.privacyfriendlynotes.room.model.Notification; import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; /** * The database that includes all used information like notes, notifications and categories. @@ -77,10 +80,22 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { "INSERT INTO notes_new(_id, in_trash, name, type, category, content, last_modified, custom_order)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?);" ); + // Previous date format: Calendar.getInstance().time.toString() -> "Thu Jul 08 12:34:56 UTC 2023" + SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.getDefault()); try (Cursor cursor = database.query(new SimpleSQLiteQuery("SELECT * FROM notes;"))) { while (cursor.moveToNext()) { @SuppressLint("Range") String lastModified = cursor.getString(cursor.getColumnIndex("last_modified")); - long lastModifiedMillis = Date.parse(lastModified); + long lastModifiedMillis; + try { + Date parsed = sdf.parse(lastModified); + lastModifiedMillis = parsed.getTime(); + } catch (ParseException | NullPointerException e) { + try { + lastModifiedMillis = Date.parse(lastModified); + } catch (IllegalArgumentException iae) { + lastModifiedMillis = 0L; + } + } @SuppressLint("Range") int _id = cursor.getInt(cursor.getColumnIndex("_id")); @SuppressLint("Range") int in_trash = cursor.getInt(cursor.getColumnIndex("in_trash")); @SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex("name")); From 62f8a6f7271a5a8260ad829adc968ec53f7681f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:16:25 +0100 Subject: [PATCH 4/7] Fix database migration - Previous version did not escape strings correctly - Use SQLiteStatement to pass the values --- .../room/NoteDatabase.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java index 732864c..ec58c90 100644 --- a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java +++ b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java @@ -28,6 +28,7 @@ import androidx.room.migration.Migration; import androidx.sqlite.db.SimpleSQLiteQuery; import androidx.sqlite.db.SupportSQLiteDatabase; +import androidx.sqlite.db.SupportSQLiteStatement; import org.secuso.privacyfriendlynotes.room.dao.CategoryDao; import org.secuso.privacyfriendlynotes.room.dao.NoteDao; @@ -37,12 +38,7 @@ import org.secuso.privacyfriendlynotes.room.model.Notification; import java.io.File; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Date; -import java.util.Objects; /** * The database that includes all used information like notes, notifications and categories. @@ -57,7 +53,7 @@ public abstract class NoteDatabase extends RoomDatabase { public static final int VERSION = 7; public static final String DATABASE_NAME = "allthenotes"; - static final Migration MIGRATION_6_7 = new Migration(6,7) { + static final Migration MIGRATION_6_7 = new Migration(6, 7) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE notes ADD COLUMN readonly INTEGER NOT NULL DEFAULT 0;"); @@ -76,6 +72,11 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { + "last_modified INTEGER NOT NULL DEFAULT(unixepoch('subsec') * 1000)," + "custom_order INTEGER NOT NULL DEFAULT 0," + "PRIMARY KEY(_id));"); + // Prepare INSERT statement + SupportSQLiteStatement stmt = database.compileStatement( + "INSERT INTO notes_new(_id, in_trash, name, type, category, content, last_modified, custom_order)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?);" + ); try (Cursor cursor = database.query(new SimpleSQLiteQuery("SELECT * FROM notes;"))) { while (cursor.moveToNext()) { @SuppressLint("Range") String lastModified = cursor.getString(cursor.getColumnIndex("last_modified")); @@ -87,8 +88,20 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { @SuppressLint("Range") int category = cursor.getInt(cursor.getColumnIndex("category")); @SuppressLint("Range") String content = cursor.getString(cursor.getColumnIndex("content")); @SuppressLint("Range") int custom_order = cursor.getInt(cursor.getColumnIndex("custom_order")); - String value = String.format("(%s,%s,'%s',%s,%s,'%s',%s,%s)", _id, in_trash, name, type, category, content, lastModifiedMillis, custom_order); - database.execSQL("INSERT INTO notes_new(_id, in_trash,name,type,category,content,last_modified,custom_order) VALUES" + value + ";"); + stmt.bindLong(1, _id); + stmt.bindLong(2, in_trash); + stmt.bindString(3, name); + stmt.bindLong(4, type); + stmt.bindLong(5, category); + stmt.bindString(6, content); + stmt.bindLong(7, lastModifiedMillis); + stmt.bindLong(8, custom_order); + + // Execute insert + stmt.executeInsert(); + + // Clear bindings to be ready for next row + stmt.clearBindings(); } } database.execSQL("DROP TABLE notes;"); From 8b2062f54e088c3d7dc5dc840776e05f5eed2a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:27:06 +0100 Subject: [PATCH 5/7] Add missing database schemas --- .../6.json | 134 +++++++++++++++++ .../7.json | 140 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json create mode 100644 app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json diff --git a/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json new file mode 100644 index 0000000..5e16162 --- /dev/null +++ b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/6.json @@ -0,0 +1,134 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "dfaa34068d0a54f9cf797b7c441c2d92", + "entities": [ + { + "tableName": "notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `content` TEXT NOT NULL, `type` INTEGER NOT NULL, `category` INTEGER NOT NULL, `in_trash` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, `custom_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "in_trash", + "columnName": "in_trash", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_modified", + "columnName": "last_modified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "custom_order", + "columnName": "custom_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` TEXT)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_noteId` INTEGER NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`_noteId`))", + "fields": [ + { + "fieldPath": "_noteId", + "columnName": "_noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_noteId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dfaa34068d0a54f9cf797b7c441c2d92')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json new file mode 100644 index 0000000..74af8c8 --- /dev/null +++ b/app/schemas/org.secuso.privacyfriendlynotes.room.NoteDatabase/7.json @@ -0,0 +1,140 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "8143c5ddeca8ab31795f588769331b34", + "entities": [ + { + "tableName": "notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `content` TEXT NOT NULL, `type` INTEGER NOT NULL, `category` INTEGER NOT NULL, `in_trash` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, `custom_order` INTEGER NOT NULL, `readonly` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "in_trash", + "columnName": "in_trash", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_modified", + "columnName": "last_modified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "custom_order", + "columnName": "custom_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readonly", + "columnName": "readonly", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` TEXT)", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_noteId` INTEGER NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`_noteId`))", + "fields": [ + { + "fieldPath": "_noteId", + "columnName": "_noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_noteId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8143c5ddeca8ab31795f588769331b34')" + ] + } +} \ No newline at end of file From b6837e2ba2ffed8276efa53c98e3c9b904378d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Wed, 5 Nov 2025 12:59:58 +0100 Subject: [PATCH 6/7] Add fallback if we can not parse the date - Resolves #216 --- .../privacyfriendlynotes/room/NoteDatabase.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java index ec58c90..16504fb 100644 --- a/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java +++ b/app/src/main/java/org/secuso/privacyfriendlynotes/room/NoteDatabase.java @@ -38,7 +38,10 @@ import org.secuso.privacyfriendlynotes.room.model.Notification; import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; /** * The database that includes all used information like notes, notifications and categories. @@ -77,10 +80,22 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { "INSERT INTO notes_new(_id, in_trash, name, type, category, content, last_modified, custom_order)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?);" ); + // Previous date format: Calendar.getInstance().time.toString() -> "Thu Jul 08 12:34:56 UTC 2023" + SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.getDefault()); try (Cursor cursor = database.query(new SimpleSQLiteQuery("SELECT * FROM notes;"))) { while (cursor.moveToNext()) { @SuppressLint("Range") String lastModified = cursor.getString(cursor.getColumnIndex("last_modified")); - long lastModifiedMillis = Date.parse(lastModified); + long lastModifiedMillis; + try { + Date parsed = sdf.parse(lastModified); + lastModifiedMillis = parsed.getTime(); + } catch (ParseException | NullPointerException e) { + try { + lastModifiedMillis = Date.parse(lastModified); + } catch (IllegalArgumentException iae) { + lastModifiedMillis = 0L; + } + } @SuppressLint("Range") int _id = cursor.getInt(cursor.getColumnIndex("_id")); @SuppressLint("Range") int in_trash = cursor.getInt(cursor.getColumnIndex("in_trash")); @SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex("name")); From 4067220c98ac4ead925150ea2498d55516e7b605 Mon Sep 17 00:00:00 2001 From: Patrick Schneider Date: Wed, 5 Nov 2025 18:12:36 +0100 Subject: [PATCH 7/7] bumps version. --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9d800c2..2a22e67 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,8 +24,8 @@ android { minSdkVersion 21 compileSdk 34 targetSdkVersion 34 - versionCode 101 - versionName "2.1.0" + versionCode 102 + versionName "2.1.1" } applicationVariants.configureEach { variant ->