From 67701840bb6b4b91d7e64af66fec358495124e4a Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 4 Sep 2025 22:57:18 +0200 Subject: [PATCH] Add support for .pkpasses --- app/src/main/AndroidManifest.xml | 2 + .../protect/card_locker/MainActivity.java | 2 + .../protect/card_locker/PkpassesParser.kt | 73 ++++++++ .../main/java/protect/card_locker/Utils.java | 70 +++++++- app/src/main/res/values/strings.xml | 4 +- .../java/protect/card_locker/PkpassTest.kt | 166 +++++++++++++++++- .../pkpass/Eurowings/Eurowings.pkpasses | Bin 0 -> 50556 bytes 7 files changed, 312 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/protect/card_locker/PkpassesParser.kt create mode 100644 app/src/test/res/protect/card_locker/pkpass/Eurowings/Eurowings.pkpasses diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 346f97f5f..ca3e05b95 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -65,6 +65,7 @@ + @@ -76,6 +77,7 @@ + = ArrayList() + + init { + mContext = context + + Log.i(TAG, "Received Pkpasses file") + if (uri == null) { + Log.e(TAG, "Uri did not contain any data") + throw IOException(context.getString(R.string.errorReadingFile)) + } + + try { + mContext.contentResolver.openInputStream(uri).use { inputStream -> + ZipInputStream(inputStream).use { zipInputStream -> + var localFileHeader: LocalFileHeader? + + while (true) { + // Retrieve the next file + localFileHeader = zipInputStream.nextEntry + + // If no next file, exit loop + if (localFileHeader == null) { + break + } + + // Ignore directories + if (localFileHeader.isDirectory) continue + + // Ignore non-pkpass files + if (!localFileHeader.fileName.endsWith(".pkpass")) continue + + // Extract .pkpass (.zip) inside .pkpasses to cache directory + val tempFileName = "pkpassparser_" + System.currentTimeMillis() + "_" + localFileHeader.fileName + val tempFile = Utils.copyToTempFile(mContext, zipInputStream, tempFileName) + + // Parse temporary file + pkPassParsers.add( + PkpassParser(mContext, tempFile.toUri()) + ) + + // Delete temporary file + tempFile.delete() + } + } + } + } catch (e: FileNotFoundException) { + throw IOException(mContext.getString(R.string.errorReadingFile)) + } catch (e: Exception) { + throw e + } + } + + fun getPkpassParsers(): ArrayList { + return pkPassParsers + } + + companion object { + private const val TAG = "Catima" + } +} diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 87a96371d..2703dca10 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -87,10 +87,10 @@ import java.util.Currency; import java.util.Date; import java.util.EnumMap; import java.util.GregorianCalendar; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -228,6 +228,58 @@ public class Utils { return parseResultList; } + static public List retrieveBarcodesFromPkPasses(Context context, Uri uri) { + Log.i(TAG, "Received Pkpasses file with possible barcode"); + if (uri == null) { + Log.e(TAG, "Pkpasses did not contain any data"); + Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show(); + return new ArrayList<>(); + } + + PkpassesParser pkpassesParser; + try { + pkpassesParser = new PkpassesParser(context, uri); + } catch (Exception e) { + Log.e(TAG, "Error reading pkpasses file", e); + Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show(); + return new ArrayList<>(); + } + + List parseResultList = new ArrayList<>(); + int i = 0; + for (PkpassParser pkpassParser : pkpassesParser.getPkpassParsers()) { + ParseResult parseResult; + List locales = pkpassParser.listLocales(); + if (locales.isEmpty()) { + try { + parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(null)); + } catch (Exception e) { + Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); + Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show(); + return new ArrayList<>(); + } + parseResult.setNote(String.format(context.getString(R.string.cardWithNumber), i+1)); + parseResultList.add(parseResult); + } else { + for (String locale : locales) { + try { + parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(locale)); + } catch (Exception e) { + Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); + Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show(); + return new ArrayList<>(); + } + parseResult.setNote(String.format(context.getString(R.string.cardWithNumberAndLocale), i+1, locale)); + parseResultList.add(parseResult); + } + } + + i++; + } + + return parseResultList; + } + static public List retrieveBarcodesFromPdf(Context context, Uri uri) { Log.i(TAG, "Received PDF file with possible barcode"); if (uri == null) { @@ -319,7 +371,19 @@ public class Utils { } if (requestCode == Utils.BARCODE_IMPORT_FROM_PKPASS_FILE) { - return retrieveBarcodesFromPkPass(context, intent.getData()); + Uri intentData = intent.getData(); + + if (intentData == null) { + Log.e(TAG, "Uri did not contain any data"); + Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show(); + return new ArrayList<>(); + } + + if (Objects.equals(context.getContentResolver().getType(intentData), "application/vnd.apple.pkpasses")) { + return retrieveBarcodesFromPkPasses(context, intentData); + } + + return retrieveBarcodesFromPkPass(context, intentData); } if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) { @@ -850,7 +914,7 @@ public class Utils { public static File copyToTempFile(Context context, InputStream input, String name) throws IOException { File file = createTempFile(context, name); - try (input; FileOutputStream out = new FileOutputStream(file)) { + try (FileOutputStream out = new FileOutputStream(file)) { byte[] buf = new byte[4096]; int len; while ((len = input.read(buf)) != -1) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 95d450e9a..1fee47bc2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -358,11 +358,13 @@ Export cancelled Use front image Use back image - Select a Passbook file (.pkpass) + Select a Passbook file (.pkpass / .pkpasses) This file is not supported Sorry, something went wrong, please try again... Width Card list Set Barcode Width After you add some loyalty cards in Catima, they will appear here. If you have cards, make sure they are not all archived. + Card %d + Card %d (%s) diff --git a/app/src/test/java/protect/card_locker/PkpassTest.kt b/app/src/test/java/protect/card_locker/PkpassTest.kt index 953a1c792..884b85c31 100644 --- a/app/src/test/java/protect/card_locker/PkpassTest.kt +++ b/app/src/test/java/protect/card_locker/PkpassTest.kt @@ -38,7 +38,7 @@ class PkpassTest { val parser = PkpassParser(context, pkpassUri) val imageBitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(imageUri)) - // Confirm this does not have languages + // Confirm this has two languages Assert.assertEquals(listOf("de", "en"), parser.listLocales()) // Confirm correct parsing (en) @@ -178,6 +178,170 @@ class PkpassTest { Assert.assertTrue(imageBitmap.sameAs(parser.image)) } + @Test + fun testEurowingsPassPkpasses() { + // This test is basically the exact same as testEurowingsPass, just zipped a second time to test pkpasses support + + // Prepare + val context: Context = ApplicationProvider.getApplicationContext() + val pkpasses = "pkpass/Eurowings/Eurowings.pkpasses" + val image = "pkpass/Eurowings/logo@2x.png" + + val pkpassesUri = Uri.parse(pkpasses) + val imageUri = Uri.parse(image) + ShadowContentResolver().registerInputStream(pkpassesUri, javaClass.getResourceAsStream(pkpasses)) + ShadowContentResolver().registerInputStream(imageUri, javaClass.getResourceAsStream(image)) + + val pkpassesParser = PkpassesParser(context, pkpassesUri) + val imageBitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(imageUri)) + + // Confirm there are exactly two passes in it (these are duplicated for simplicity, might not be the best test) + val pkpassParsers = pkpassesParser.getPkpassParsers() + Assert.assertEquals(2, pkpassParsers.count()) + repeat(2) { index -> + val parser = pkpassParsers[index] + + // Confirm this has two languages + Assert.assertEquals(listOf("de", "en"), parser.listLocales()) + + // Confirm correct parsing (en) + var parsedCard = parser.toLoyaltyCard("de") + + Assert.assertEquals(-1, parsedCard.id) + Assert.assertEquals("EUROWINGS", parsedCard.store) + Assert.assertEquals("Eurowings Boarding Pass\n" + + "\n" + + "Gate: B61\n" + + "Sitz: 12D\n" + + "\n" + + "Cologne-Bonn: CGN\n" + + "Dubrovnik: DBV\n" + + "\n" + + "Name: John Doe\n" + + "Status: -\n" + + "Gruppe: GROUP 1\n" + + "Tarif: SMART\n" + + "\n" + + "Flug: EW 954\n" + + "Datum: 08/09/2019\n" + + "Boarding: 05:00\n" + + "Gate Schließt: 05:15\n" + + "\n" + + "Eurowings wünscht Ihnen einen angenehmen Flug.\n" + + "\n" + + "Wir bitten Sie, sich zur angegeben Boarding Zeit am Gate einzufinden.\n" + + "Buchungscode: JBZPPP\n" + + "Sequenz: 73\n" + + "Hinweis: Bitte beachten Sie, dass obwohl Ihr Flug verspätet sein mag, Sie dennoch wie geplant pünktlich am Check-in und am Abfluggate erscheinen müssen.\n" + + "\n" + + "Kostenlose Mitnahme eines Handgepäckstücks (8 Kg, 55 x 40 x 23cm).\n" + + "Mitnahme von Flüssigkeiten im Handgepäck: Neben den sonstigen Beschränkungen für das Handgepäck ist für alle Abflüge innerhalb der Europäischen Union sowie vielen weiteren Ländern (u.a. Schweiz, Russland, Island, Kroatien, Israel, Ägypten, Marokko, Tunesien, Norwegen) die Mitnahme von vor der Fluggastkontrolle erworbenen bzw. mitgebrachten Flüssigkeiten und Gels nur noch eingeschränkt erlaubt:\n" + + "\n" + + "- Sämtliche Flüssigkeiten (wie Kosmetik- und Toilettenartikel, Gels, Pasten, Cremes, Lotionen, Gemische aus flüssigen und festen Stoffen, Parfums, Behälter unter Druck, Dosen, Wasserflaschen etc.) sowie wachs- oder gelartige Stoffe dürfen nur noch in Behältnissen bis zu 100 ml bzw. 100 g mit an Bord genommen werden.\n" + + "\n" + + "- Diese Flüssigkeiten bzw. Stoffe müssen in einem transparenten, wiederverschließbaren Plastikbeutel (max. 1 kg Inhalt) vollständig geschlossen, verpackt sein.\n" + + "\n" + + "- Diese Beutel müssen Fluggäste selbst vor dem Abflug erwerben. Sie sind in vielen Supermärkten z. B. als Gefrierbeutel erhältlich. Es besteht zurzeit keine Möglichkeit, entsprechende Plastikbeutel am Eurowings Check-In zu erwerben bzw. auszugeben.\n" + + "\n" + + "- Verschreibungspflichtige Medikamente sowie Babynahrung dürfen weiterhin im Handgepäck transportiert werden. Der Fluggast muss nachweisen, dass die Medikamente und Babynahrung während des Fluges benötigt werden.\n" + + "\n" + + "- Produkte und Beutel, die nicht den Maßgaben entsprechen oder die nur mit Gummiband oder ähnlichem verschlossen sind, müssen leider abgegeben werden.\n" + + "\n" + + "Flüssigkeiten und Gels, die Sie nicht zwingend während Ihres Aufenthalts an Bord benötigen, sollten zur raschen Fluggastabfertigung nach Möglichkeit im aufzugebenden Gepäck untergebracht werden.\n" + + "\n" + + "Selbstverständlich ist die Mitnahme von allen Flüssigkeiten/Gels/Getränken aus Travel-Value oder Duty Free-Shops, die nach der Fluggastkontrolle erworben werden, weiterhin erlaubt.\n" + + "\n" + + "Eurowings übernimmt keine Haftung für Gegenstände, die der Fluggast nicht im Handgepäck mitführen darf und deshalb aus Sicherheitsgründen an der Fluggastkontrolle abgeben muss.\n" + + "Kontakt: Sie erreichen das deutsche Call Center unter der Telefonnummer\n" + + "\n" + + "0180 6 320 320 ( 0:00 Uhr - 24:00 Uhr )\n" + + "\n" + + "(0,20 € pro Anruf aus dem Festnetz der Deutschen Telekom - Mobilfunk maximal 0,60 € pro Anruf).", parsedCard.note) + Assert.assertEquals(Date(1567911600000), parsedCard.validFrom) + Assert.assertEquals(null, parsedCard.expiry) + Assert.assertEquals(BigDecimal(0), parsedCard.balance) + Assert.assertEquals(null, parsedCard.balanceType) + Assert.assertEquals("M1DOE/JOHN JBZPPP CGNDBVEW 0954 251A012D0073 148>5181W 9250BEW 00000000000002A0000000000000 0 N", parsedCard.cardId) + Assert.assertEquals(null, parsedCard.barcodeId) + Assert.assertEquals(BarcodeFormat.AZTEC, parsedCard.barcodeType!!.format()) + Assert.assertEquals(Color.parseColor("#FFFFFF"), parsedCard.headerColor) + Assert.assertEquals(0, parsedCard.starStatus) + Assert.assertEquals(0, parsedCard.archiveStatus) + Assert.assertEquals(0, parsedCard.lastUsed) + Assert.assertEquals(DBHelper.DEFAULT_ZOOM_LEVEL, parsedCard.zoomLevel) + Assert.assertEquals(DBHelper.DEFAULT_ZOOM_LEVEL_WIDTH, parsedCard.zoomLevelWidth) + + // Confirm correct image is used + Assert.assertTrue(imageBitmap.sameAs(parser.image)) + + // Confirm correct parsing (en) + parsedCard = parser.toLoyaltyCard("en") + + Assert.assertEquals(-1, parsedCard.id) + Assert.assertEquals("EUROWINGS", parsedCard.store) + Assert.assertEquals("Eurowings Boarding Pass\n" + + "\n" + + "Gate: B61\n" + + "Seat: 12D\n" + + "\n" + + "Cologne-Bonn: CGN\n" + + "Dubrovnik: DBV\n" + + "\n" + + "Name: John Doe\n" + + "Status: -\n" + + "Group: GROUP 1\n" + + "Fare: SMART\n" + + "\n" + + "Flight: EW 954\n" + + "Date: 08/09/2019\n" + + "Boarding: 05:00\n" + + "Gate closure: 05:15\n" + + "\n" + + "Eurowings wishes you a pleasant flight .\n" + + "\n" + + "We kindly ask you to be present at your departure gate on time.\n" + + "Booking code: JBZPPP\n" + + "Sequence: 73\n" + + "Notice: Please note that although your flight may be delayed, you will still need to check in and go to your departure gate on time as scheduled.\n" + + "\n" + + "Carry on one item of hand luggage (8 kg, 55 x 40 x 23 cm) for free.\n" + + "Carrying liquids in hand luggage: In addition to other restrictions on hand luggage, there are still restrictions on liquids and gels brought by the passenger or purchased before the security control on all departures within the European Union, as well as to many other countries (including Switzerland, Russia, Iceland, Croatia, Israel, Egypt, Morocco, Tunisia and Norway):\n" + + "\n" + + "- All liquids (such as toiletries and cosmetics, gels, pastes, creams, lotions, mixtures of liquids and solids, perfumes, pressurised containers, cans, water bottles etc) as well as wax and gel-like substances may only be carried on board in amounts less than 100ml or 100g.\n" + + "\n" + + "- These liquids or substances must be packed in closed containers in a transparent, re-sealable plastic bag (max. contents 1 kg).\n" + + "\n" + + "- It is the passenger’s responsibility to purchase this bag before departure. They are available in many supermarkets, e.g. as freezer bags. It is currently not possible for passengers to obtain or purchase the required bags from Eurowings check-in.\n" + + "\n" + + "- Prescription medicines and baby food may still be carried in hand baggage. The passenger must prove that such medicines and/or baby food are needed during the flight.\n" + + "\n" + + "- Products and bags which do not meet the requirements or are only sealed with a rubber band or similar will unfortunately have to be surrendered by passengers\n" + + "\n" + + "In order to pass through the airport as quickly as possible, you are strongly advised to pack any liquids or gels which are not essential for your journey on board the aircraft in your checked baggage if possible.\n" + + "\n" + + "As a matter of course, liquids from the Travel Value / Duty Free shops which have been purchased after you have passed through security are still allowed on board.\n" + + "\n" + + "Eurowings shall not be liable for any items which passengers are prohibited from carrying in their hand baggage for security reasons and are required to surrender at the security checkpoint.\n" + + "Contact: https://mobile.eurowings.com/booking/StaticContactInfo.aspx?culture=en-GB&back=home", parsedCard.note) + Assert.assertEquals(Date(1567911600000), parsedCard.validFrom) + Assert.assertEquals(null, parsedCard.expiry) + Assert.assertEquals(BigDecimal(0), parsedCard.balance) + Assert.assertEquals(null, parsedCard.balanceType) + Assert.assertEquals("M1DOE/JOHN JBZPPP CGNDBVEW 0954 251A012D0073 148>5181W 9250BEW 00000000000002A0000000000000 0 N", parsedCard.cardId) + Assert.assertEquals(null, parsedCard.barcodeId) + Assert.assertEquals(BarcodeFormat.AZTEC, parsedCard.barcodeType!!.format()) + Assert.assertEquals(Color.parseColor("#FFFFFF"), parsedCard.headerColor) + Assert.assertEquals(0, parsedCard.starStatus) + Assert.assertEquals(0, parsedCard.archiveStatus) + Assert.assertEquals(0, parsedCard.lastUsed) + Assert.assertEquals(DBHelper.DEFAULT_ZOOM_LEVEL, parsedCard.zoomLevel) + Assert.assertEquals(DBHelper.DEFAULT_ZOOM_LEVEL_WIDTH, parsedCard.zoomLevelWidth) + + // Confirm correct image is used + Assert.assertTrue(imageBitmap.sameAs(parser.image)) + } + } + @Test fun testFakeEmptyEurowingsPassForGitHubIssue2317() { // Prepare diff --git a/app/src/test/res/protect/card_locker/pkpass/Eurowings/Eurowings.pkpasses b/app/src/test/res/protect/card_locker/pkpass/Eurowings/Eurowings.pkpasses new file mode 100644 index 0000000000000000000000000000000000000000..971228586ea53847d16b6613f535c41c29baf7da GIT binary patch literal 50556 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