From 3fffc83c08d51cd8bbcf5b8e5b7601fa12eb1091 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 7 Sep 2026 20:29:26 +0200 Subject: [PATCH] Fix importing pkpasses file from network location In the scan activity, the mime type info is not always available. This made pkpasses files fail to parse, because they only went down the pkpass path --- .../java/protect/card_locker/MainActivity.kt | 48 ++++--- .../protect/card_locker/PkpassesParser.kt | 10 ++ .../java/protect/card_locker/ScanActivity.kt | 7 + .../main/java/protect/card_locker/Utils.java | 121 +++++++----------- 4 files changed, 92 insertions(+), 94 deletions(-) diff --git a/app/src/main/java/protect/card_locker/MainActivity.kt b/app/src/main/java/protect/card_locker/MainActivity.kt index 352fbcf1c..0fc441cd8 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.kt +++ b/app/src/main/java/protect/card_locker/MainActivity.kt @@ -533,29 +533,41 @@ class MainActivity : CatimaAppCompatActivity(), CardAdapterListener { private fun importFile(data: Uri?, receivedType: String) { lifecycleScope.launch { val parseResultList: MutableList = withContext(Dispatchers.IO) { - when { - receivedType.startsWith("image/") -> - Utils.retrieveBarcodesFromImage(this@MainActivity, data) - receivedType == "application/pdf" -> - Utils.retrieveBarcodesFromPdf(this@MainActivity, data) - receivedType == "application/vnd.apple.pkpass" || - receivedType == "application/vnd-com.apple.pkpass" -> - Utils.retrieveBarcodesFromPkPass(this@MainActivity, data) - // FIXME: espass is not pkpass - // However, several users stated in https://github.com/CatimaLoyalty/Android/issues/2197 that the formats are extremely similar to the point they could rename an .espass file to .pkpass and have it imported - // So it makes sense to "unofficially" treat it as a PKPASS for now, even though not completely correct - receivedType == "application/vnd.espass-espass" -> - Utils.retrieveBarcodesFromPkPass(this@MainActivity, data) - receivedType == "application/vnd.apple.pkpasses" -> - Utils.retrieveBarcodesFromPkPasses(this@MainActivity, data) - else -> { - Log.e(TAG, "Wrong mime-type") - return@withContext null + try { + when { + receivedType.startsWith("image/") -> + Utils.retrieveBarcodesFromImage(this@MainActivity, data) + + receivedType == "application/pdf" -> + Utils.retrieveBarcodesFromPdf(this@MainActivity, data) + + receivedType == "application/vnd.apple.pkpass" || + receivedType == "application/vnd-com.apple.pkpass" -> + Utils.retrieveBarcodesFromPkPass(this@MainActivity, data) + // FIXME: espass is not pkpass + // However, several users stated in https://github.com/CatimaLoyalty/Android/issues/2197 that the formats are extremely similar to the point they could rename an .espass file to .pkpass and have it imported + // So it makes sense to "unofficially" treat it as a PKPASS for now, even though not completely correct + receivedType == "application/vnd.espass-espass" -> + Utils.retrieveBarcodesFromPkPass(this@MainActivity, data) + + receivedType == "application/vnd.apple.pkpasses" -> + Utils.retrieveBarcodesFromPkPasses(this@MainActivity, data) + + else -> { + Log.e(TAG, "Wrong mime-type") + return@withContext null + } } + } catch (e: Exception) { + Log.e(TAG, "Parsing failed: $e"); + e.printStackTrace(); + Utils.showToast(this@MainActivity, R.string.errorReadingFile, Toast.LENGTH_LONG); + return@withContext null; } } ?: return@launch if (parseResultList.isEmpty()) { + Utils.showToast(this@MainActivity, R.string.noBarcodeFound, Toast.LENGTH_LONG); finish() return@launch } diff --git a/app/src/main/java/protect/card_locker/PkpassesParser.kt b/app/src/main/java/protect/card_locker/PkpassesParser.kt index f56e05eaa..3c1a34fb4 100644 --- a/app/src/main/java/protect/card_locker/PkpassesParser.kt +++ b/app/src/main/java/protect/card_locker/PkpassesParser.kt @@ -22,6 +22,8 @@ class PkpassesParser(context: Context, uri: Uri?) { throw IOException(context.getString(R.string.errorReadingFile)) } + var hasPkpassFiles = false + try { mContext.contentResolver.openInputStream(uri).use { inputStream -> ZipInputStream(inputStream).use { zipInputStream -> @@ -42,6 +44,9 @@ class PkpassesParser(context: Context, uri: Uri?) { // Ignore non-pkpass files if (!localFileHeader.fileName.endsWith(".pkpass")) continue + // Mark as found + hasPkpassFiles = true + // Extract .pkpass (.zip) inside .pkpasses to cache directory val tempFileName = "pkpassparser_" + System.currentTimeMillis() + "_" + localFileHeader.fileName val tempFile = Utils.copyToTempFile(mContext, zipInputStream, tempFileName) @@ -61,6 +66,11 @@ class PkpassesParser(context: Context, uri: Uri?) { } catch (e: Exception) { throw e } + + if (!hasPkpassFiles) { + Log.d(TAG, "No pkpass files found") + throw IOException(mContext.getString(R.string.errorReadingFile)) + } } fun getPkpassParsers(): ArrayList { diff --git a/app/src/main/java/protect/card_locker/ScanActivity.kt b/app/src/main/java/protect/card_locker/ScanActivity.kt index fad13b96d..22e882bf6 100644 --- a/app/src/main/java/protect/card_locker/ScanActivity.kt +++ b/app/src/main/java/protect/card_locker/ScanActivity.kt @@ -331,7 +331,14 @@ class ScanActivity : CatimaAppCompatActivity() { Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this@ScanActivity) withContext(Dispatchers.Main) { + if (parseResultList == null) { + Utils.showToast(this@ScanActivity, R.string.errorReadingFile, Toast.LENGTH_LONG) + setScannerActive(true) + return@withContext + } + if (parseResultList.isEmpty()) { + Utils.showToast(this@ScanActivity, R.string.noBarcodeFound, Toast.LENGTH_LONG) setScannerActive(true) return@withContext } diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index e8527d2c0..0c9dbf313 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -121,7 +121,7 @@ public class Utils { static final int BITMAP_SIZE_BIG = 1600; // Displays the toast immediately on the main UI loop, or asks Android to display it there otherwise. - static private void showToast(Context context, int message, int duration) { + static public void showToast(Context context, int message, int duration) { if (Looper.myLooper() == Looper.getMainLooper()) { Toast.makeText(context, message, duration).show(); } else { @@ -160,50 +160,35 @@ public class Utils { tileLetterFontSize, pixelSize, pixelSize, backgroundColor, ForegroundColorHelper.Companion.needsDarkForeground(backgroundColor) ? Color.BLACK : Color.WHITE); } - static public List retrieveBarcodesFromImage(Context context, Uri uri) { + static public List retrieveBarcodesFromImage(Context context, Uri uri) throws FileNotFoundException { Log.i(TAG, "Received image file with possible barcode"); if (uri == null) { - Log.e(TAG, "Uri did not contain any data"); - showToast(context, R.string.errorReadingImage, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new FileNotFoundException("Uri did not contain any data"); } Bitmap bitmap; try { bitmap = retrieveImageFromUri(context, uri); } catch (IOException e) { - Log.e(TAG, "Error getting data from image file"); - e.printStackTrace(); - showToast(context, R.string.errorReadingImage, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error reading image file", e); } - List barcodesFromBitmap = getBarcodesFromBitmap(bitmap); - - if (barcodesFromBitmap.isEmpty()) { - Log.i(TAG, "No barcode found in image file"); - showToast(context, R.string.noBarcodeFound, Toast.LENGTH_LONG); - } - - return barcodesFromBitmap; + // Returns barcodes or an empty list if nothing found + return getBarcodesFromBitmap(bitmap); } - static public List retrieveBarcodesFromPkPass(Context context, Uri uri) { + static public List retrieveBarcodesFromPkPass(Context context, Uri uri) throws FileNotFoundException { Log.i(TAG, "Received Pkpass file with possible barcode"); if (uri == null) { - Log.e(TAG, "Pkpass did not contain any data"); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new FileNotFoundException("Uri did not contain any data"); } PkpassParser pkpassParser; try { pkpassParser = new PkpassParser(context, uri); } catch (Exception e) { - Log.e(TAG, "Error reading pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error reading pkpass file", e); } List locales = pkpassParser.listLocales(); @@ -211,9 +196,7 @@ public class Utils { try { return Collections.singletonList(new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(null))); } catch (Exception e) { - Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error calling toLoyaltyCard on pkpass file", e); } } @@ -223,9 +206,7 @@ public class Utils { try { parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(locale)); } catch (Exception e) { - Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error calling toLoyaltyCard on pkpass file", e); } parseResult.setNote(locale); parseResultList.add(parseResult); @@ -234,21 +215,17 @@ public class Utils { return parseResultList; } - static public List retrieveBarcodesFromPkPasses(Context context, Uri uri) { + static public List retrieveBarcodesFromPkPasses(Context context, Uri uri) throws FileNotFoundException { Log.i(TAG, "Received Pkpasses file with possible barcode"); if (uri == null) { - Log.e(TAG, "Pkpasses did not contain any data"); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new FileNotFoundException("Uri did not contain any data"); } PkpassesParser pkpassesParser; try { pkpassesParser = new PkpassesParser(context, uri); } catch (Exception e) { - Log.e(TAG, "Error reading pkpasses file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error reading pkpasses file", e); } List parseResultList = new ArrayList<>(); @@ -260,9 +237,7 @@ public class Utils { try { parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(null)); } catch (Exception e) { - Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error calling toLoyaltyCard on pkpass file", e); } parseResult.setNote(String.format(context.getString(R.string.cardWithNumber), i+1)); parseResultList.add(parseResult); @@ -271,9 +246,7 @@ public class Utils { try { parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(locale)); } catch (Exception e) { - Log.e(TAG, "Error calling toLoyaltyCard on pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new IllegalArgumentException("Error calling toLoyaltyCard on pkpass file", e); } parseResult.setNote(String.format(context.getString(R.string.cardWithNumberAndLocale), i+1, locale)); parseResultList.add(parseResult); @@ -286,12 +259,10 @@ public class Utils { return parseResultList; } - static public List retrieveBarcodesFromPdf(Context context, Uri uri) { + static public List retrieveBarcodesFromPdf(Context context, Uri uri) throws FileNotFoundException { Log.i(TAG, "Received PDF file with possible barcode"); if (uri == null) { - Log.e(TAG, "Uri did not contain any data"); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + throw new FileNotFoundException("Uri did not contain any data"); } ParcelFileDescriptor parcelFileDescriptor = null; @@ -326,8 +297,7 @@ public class Utils { } } } catch (IOException e) { - Log.e(TAG, "Error reading PDF file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); + throw new IllegalArgumentException("Error reading PDF file", e); } finally { // Resource handling if (renderer != null) { @@ -342,10 +312,7 @@ public class Utils { } } - if (barcodesFromPdfPages.isEmpty()) { - Log.i(TAG, "No barcode found in pdf file"); - showToast(context, R.string.noBarcodeFound, Toast.LENGTH_LONG); - } + // Returns barcodes or an empty list if nothing found return barcodesFromPdfPages; } @@ -360,6 +327,7 @@ public class Utils { * @param context * @return List */ + @Nullable static public List parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) { String contents; String format; @@ -368,34 +336,35 @@ public class Utils { return new ArrayList<>(); } - if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) { - return retrieveBarcodesFromImage(context, intent.getData()); - } - - if (requestCode == Utils.BARCODE_IMPORT_FROM_PDF_FILE) { - return retrieveBarcodesFromPdf(context, intent.getData()); - } - - if (requestCode == Utils.BARCODE_IMPORT_FROM_PKPASS_FILE) { - Uri intentData = intent != null ? intent.getData() : null; - - if (intentData == null) { - Log.e(TAG, "Uri did not contain any data"); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + try { + if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) { + return retrieveBarcodesFromImage(context, intent.getData()); } - try { - if (Objects.equals(context.getContentResolver().getType(intentData), "application/vnd.apple.pkpasses")) { - return retrieveBarcodesFromPkPasses(context, intentData); + if (requestCode == Utils.BARCODE_IMPORT_FROM_PDF_FILE) { + return retrieveBarcodesFromPdf(context, intent.getData()); + } + + if (requestCode == Utils.BARCODE_IMPORT_FROM_PKPASS_FILE) { + Uri intentData = intent != null ? intent.getData() : null; + + if (intentData == null) { + throw new FileNotFoundException("Uri did not contain any data"); } - return retrieveBarcodesFromPkPass(context, intentData); - } catch (Exception e) { - Log.e(TAG, "Error reading pkpass file", e); - showToast(context, R.string.errorReadingFile, Toast.LENGTH_LONG); - return new ArrayList<>(); + // Mime type info is not always available. So we're going to first guess it's a pkpass file and if that fails, try pkpasses + try { + return retrieveBarcodesFromPkPass(context, intentData); + } catch (Exception e) { + Log.e(TAG, "Error reading pkpass file, attempting as pkpasses file", e); + return retrieveBarcodesFromPkPasses(context, intentData); + } } + } catch (Exception e) { + // Return null instead of an empty list to differentiate between error or no result + Log.e(TAG, "Parsing failed: " + e); + e.printStackTrace(); + return null; } if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {