Compare commits

..
Author SHA1 Message Date
Sylvia van Os 3fffc83c08 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
2026-09-07 21:23:34 +02:00
4 changed files with 92 additions and 94 deletions

No files matched your search

@@ -533,29 +533,41 @@ class MainActivity : CatimaAppCompatActivity(), CardAdapterListener {
private fun importFile(data: Uri?, receivedType: String) {
lifecycleScope.launch {
val parseResultList: MutableList<ParseResult?> = 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
}
@@ -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<PkpassParser> {
@@ -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
}
@@ -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<ParseResult> retrieveBarcodesFromImage(Context context, Uri uri) {
static public List<ParseResult> 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<ParseResult> 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<ParseResult> retrieveBarcodesFromPkPass(Context context, Uri uri) {
static public List<ParseResult> 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<String> 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<ParseResult> retrieveBarcodesFromPkPasses(Context context, Uri uri) {
static public List<ParseResult> 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<ParseResult> 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<ParseResult> retrieveBarcodesFromPdf(Context context, Uri uri) {
static public List<ParseResult> 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<ParseResult>
*/
@Nullable
static public List<ParseResult> 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) {