From 48993f0486a1a1711c178fc7c90d2bb057c9d0c3 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 4 Jan 2026 20:48:42 +0100 Subject: [PATCH 1/3] On initial upgrade, force all automatic cards to ISO-8859-1 This fixes old pkpass files imported before Catima 2.41.0 to be forced to "Automatic", which may guess UTF-8. New pkpass files will have the encoding read from the file and newly scanned barcodes will use Automatic. This does have the unfortunate side effect of everyone who already scanned a QR code with UTF-8 data since Catima 2.41.0 to have it forced to ISO-8859-1, but it will fix Deutschlandtickets imported before 2.41.0 which is a rather large amount of the Catima userbase. --- app/src/main/java/protect/card_locker/DBHelper.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 23b59d56b..493e6f329 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -26,7 +26,7 @@ import java.util.Set; public class DBHelper extends SQLiteOpenHelper { public static final String DATABASE_NAME = "Catima.db"; public static final int ORIGINAL_DATABASE_VERSION = 1; - public static final int DATABASE_VERSION = 18; + public static final int DATABASE_VERSION = 19; // NB: changing these values requires a migration public static final int DEFAULT_ZOOM_LEVEL = 100; @@ -345,6 +345,17 @@ public class DBHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + " ADD COLUMN " + LoyaltyCardDbIds.BARCODE_ENCODING + " TEXT"); } + + // On upgrade to 2.41.3, force all existing "Automatic" cards to ISO-8859-1. + // UTF-8 support was only added in 2.42.0, before that, all barcodes were saved without + // any encoding info. As many scanners deal badly with ECI info, automatically guessing + // UTF-8 for old barcodes may break them. + // + // New cards will be saved using either "Automatic" encoding to guess or, for pkpass files, + // whatever encoding is specified. + if (oldVersion < 19 && newVersion >= 19) { + db.execSQL("UPDATE " + LoyaltyCardDbIds.TABLE + " SET " + LoyaltyCardDbIds.BARCODE_ENCODING + " = 'ISO-8859-1' WHERE " + LoyaltyCardDbIds.BARCODE_ENCODING + " IS NULL"); + } } public static Set imageFiles(Context context, final SQLiteDatabase database) { From d3de9c65e673a6121260418ff1d97c2af94a6914 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 4 Jan 2026 21:04:25 +0100 Subject: [PATCH 2/3] Force ISO-8859-1 when importing old backups This will not catch one edge case: use exporting a backup in Catima 2.41.0 - 2.41.2, then uninstall Catima, then installing Catima 2.41.3 and then importing the database. But this seems rare enough to be acceptable. --- .../protect/card_locker/importexport/CatimaImporter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/protect/card_locker/importexport/CatimaImporter.java b/app/src/main/java/protect/card_locker/importexport/CatimaImporter.java index a6567dd44..b819ab858 100644 --- a/app/src/main/java/protect/card_locker/importexport/CatimaImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/CatimaImporter.java @@ -459,8 +459,11 @@ public class CatimaImporter implements Importer { barcodeType = CatimaBarcode.fromName(unparsedBarcodeType); } + // Barcode encoding information is only supported since Catima 2.41.0, so old exports will lack this field + // Database migration 19 forcing ISO-8859-1 on all cards, so we should treat imports from old Catima versions the same and force ISO-8859-1 there too + // This limits the risk of breaking cards when importing an old database backup Charset barcodeEncoding = null; - String unparsedBarcodeEncoding = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING, record, ""); + String unparsedBarcodeEncoding = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING, record, "ISO-8859-1"); if (!unparsedBarcodeEncoding.isEmpty()) { barcodeEncoding = Charset.forName(unparsedBarcodeEncoding); } From 90e6dd87381747ad8fbc2c47ebaa7f956d52daa1 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 4 Jan 2026 21:07:33 +0100 Subject: [PATCH 3/3] Fix tests --- .../test/java/protect/card_locker/DatabaseTest.java | 4 ++-- .../java/protect/card_locker/ImportExportTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/test/java/protect/card_locker/DatabaseTest.java b/app/src/test/java/protect/card_locker/DatabaseTest.java index c84ac1567..5314cccef 100644 --- a/app/src/test/java/protect/card_locker/DatabaseTest.java +++ b/app/src/test/java/protect/card_locker/DatabaseTest.java @@ -497,7 +497,7 @@ public class DatabaseTest { assertEquals("cardId", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.UPC_A, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(null, card.headerColor); assertEquals(0, card.starStatus); assertEquals(0, card.lastUsed); @@ -515,7 +515,7 @@ public class DatabaseTest { assertEquals("cardId", card2.cardId); assertEquals(null, card2.barcodeId); assertEquals(null, card2.barcodeType); // Empty string should've become null - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(null, card2.headerColor); assertEquals(0, card2.starStatus); assertEquals(0, card2.lastUsed); diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index e10d10d85..9f01f517b 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -669,7 +669,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertNull(card.headerColor); assertEquals(0, card.starStatus); @@ -696,7 +696,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertNull(card.headerColor); assertEquals(0, card.starStatus); @@ -735,7 +735,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(null, card.barcodeType); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(1, (long) card.headerColor); assertEquals(0, card.starStatus); @@ -762,7 +762,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(1, (long) card.headerColor); assertEquals(1, card.starStatus); @@ -789,7 +789,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(1, (long) card.headerColor); assertEquals(0, card.starStatus); @@ -823,7 +823,7 @@ public class ImportExportTest { assertEquals("12345", card.cardId); assertEquals(null, card.barcodeId); assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format()); - assertEquals(null, card.barcodeEncoding); + assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1 assertEquals(1, (long) card.headerColor); assertEquals(0, card.starStatus);