diff --git a/app/src/main/java/protect/card_locker/LoyaltyCard.java b/app/src/main/java/protect/card_locker/LoyaltyCard.java index 100e17a94..0559890b0 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCard.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCard.java @@ -8,6 +8,7 @@ import java.math.BigDecimal; import java.util.Currency; import java.util.Date; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; public class LoyaltyCard implements Parcelable { @@ -166,6 +167,31 @@ public class LoyaltyCard implements Parcelable { return id; } + @NonNull + @Override + public String toString() { + return String.format( + "LoyaltyCard{%n id=%s,%n store=%s,%n note=%s,%n validFrom=%s,%n expiry=%s,%n" + + " balance=%s,%n balanceType=%s,%n cardId=%s,%n barcodeId=%s,%n barcodeType=%s,%n" + + " headerColor=%s,%n starStatus=%s,%n lastUsed=%s,%n zoomLevel=%s,%n archiveStatus=%s%n}", + this.id, + this.store, + this.note, + this.validFrom, + this.expiry, + this.balance, + this.balanceType, + this.cardId, + this.barcodeId, + this.barcodeType != null ? this.barcodeType.format() : null, + this.headerColor, + this.starStatus, + this.lastUsed, + this.zoomLevel, + this.archiveStatus + ); + } + public static final Creator CREATOR = new Creator() { @Override public LoyaltyCard createFromParcel(Parcel in) { diff --git a/app/src/main/java/protect/card_locker/importexport/StocardImporter.java b/app/src/main/java/protect/card_locker/importexport/StocardImporter.java index 8269aa724..263719d03 100644 --- a/app/src/main/java/protect/card_locker/importexport/StocardImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/StocardImporter.java @@ -5,6 +5,8 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.zxing.BarcodeFormat; import net.lingala.zip4j.io.inputstream.ZipInputStream; @@ -27,6 +29,7 @@ import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,12 +51,48 @@ import protect.card_locker.ZipUtils; * A header is expected for the each table showing the names of the columns. */ public class StocardImporter implements Importer { - public static class ZIPData { - public final HashMap> loyaltyCardHashMap; - public final HashMap> providers; + public static class StocardProvider { + public String name = null; + public String barcodeFormat = null; + public Bitmap logo = null; + } - ZIPData(final HashMap> loyaltyCardHashMap, final HashMap> providers) { - this.loyaltyCardHashMap = loyaltyCardHashMap; + public static class StocardRecord { + public String providerId = null; + public String store = null; + public String label = null; + public String note = null; + public String cardId = null; + public String barcodeType = null; + public Long lastUsed = null; + public Bitmap frontImage = null; + public Bitmap backImage = null; + + @NonNull + @Override + public String toString() { + return String.format( + "StocardRecord{%n providerId=%s,%n store=%s,%n label=%s,%n note=%s,%n cardId=%s,%n" + + " barcodeType=%s,%n lastUsed=%s,%n frontImage=%s,%n backImage=%s%n}", + this.providerId, + this.store, + this.label, + this.note, + this.cardId, + this.barcodeType, + this.lastUsed, + this.frontImage, + this.backImage + ); + } + } + + public static class ZIPData { + public final Map cards; + public final Map providers; + + ZIPData(final Map cards, final Map providers) { + this.cards = cards; this.providers = providers; } } @@ -68,6 +107,8 @@ public class StocardImporter implements Importer { } } + public static final String PROVIDER_PREFIX = "/loyalty-card-providers/"; + private static final String TAG = "Catima"; public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, JSONException, ParseException { @@ -77,11 +118,11 @@ public class StocardImporter implements Importer { try { for (CSVRecord record : parser) { - HashMap recordData = new HashMap<>(); - recordData.put("name", record.get("name")); - recordData.put("barcodeFormat", record.get("barcodeFormat")); + StocardProvider provider = new StocardProvider(); + provider.name = record.get("name").trim(); + provider.barcodeFormat = record.get("barcodeFormat").trim(); - zipData.providers.put(record.get("_id"), recordData); + zipData.providers.put(record.get("_id").trim(), provider); } parser.close(); @@ -95,7 +136,7 @@ public class StocardImporter implements Importer { zipInputStream.close(); input.close(); - if (zipData.loyaltyCardHashMap.keySet().size() == 0) { + if (zipData.cards.keySet().size() == 0) { throw new FormatException("Couldn't find any loyalty cards in this Stocard export."); } @@ -103,14 +144,13 @@ public class StocardImporter implements Importer { saveAndDeduplicate(context, database, importedData); } - public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, JSONException { - HashMap> loyaltyCardHashMap = zipData.loyaltyCardHashMap; - HashMap> providers = zipData.providers; + public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, FormatException, JSONException { + Map cards = zipData.cards; + Map providers = zipData.providers; - String[] providersFileName = null; String[] customProvidersBaseName = null; - String customProviderId = ""; String[] cardBaseName = null; + String customProviderId = ""; String cardName = ""; LocalFileHeader localFileHeader; while ((localFileHeader = zipInputStream.getNextEntry()) != null) { @@ -121,173 +161,182 @@ public class StocardImporter implements Importer { continue; } - if (providersFileName == null) { - providersFileName = new String[]{ - "extracts", - nameParts[1], - "users", - nameParts[1], - "analytics-properties", - "content.json" - }; + String userId = nameParts[1]; + + if (customProvidersBaseName == null) { + // FIXME: can we use the points-account/statement/content.json balance info somehow? + /* + Known files: + extracts//users// + analytics-properties/content.json + devices// + analytics-properties/content.json + content.json + ip-location-wifi/content.json + enabled-regions//content.json + loyalty-card-custom-providers//content.json - custom providers + loyalty-cards// + card-linked-coupons/accounts/default/ + content.json + user-coupons//content.json + content.json - card itself + images/back.png - back image (legacy) + images/back/back.jpg - back image + images/back/content.json + images/front.png - front image (legacy) + images/front/content.json + images/front/front.jpg - front image + notes/default/content.json - note + points-account/ + content.json + statement/content.json + usages//content.json - timestamps + usage-statistics/content.json - timestamps + reward-program-balances//content.json + */ customProvidersBaseName = new String[]{ "extracts", - nameParts[1], + userId, "users", - nameParts[1], + userId, "loyalty-card-custom-providers" }; cardBaseName = new String[]{ "extracts", - nameParts[1], + userId, "users", - nameParts[1], + userId, "loyalty-cards" }; } if (startsWith(nameParts, customProvidersBaseName, 1)) { // Extract providerId - customProviderId = nameParts[customProvidersBaseName.length].split("\\.", 2)[0]; + customProviderId = nameParts[customProvidersBaseName.length]; + + StocardProvider provider = providers.get(customProviderId); + if (provider == null) { + provider = new StocardProvider(); + providers.put(customProviderId, provider); + } // Name file if (fileName.endsWith(customProviderId + "/content.json")) { JSONObject jsonObject = ZipUtils.readJSON(zipInputStream); - - providers = appendToHashMap( - providers, - customProviderId, - "name", - jsonObject.getString("name") - ); + provider.name = jsonObject.getString("name"); } else if (fileName.endsWith("logo.png")) { - providers = appendToHashMap( - providers, - customProviderId, - "logo", - ZipUtils.readImage(zipInputStream) - ); + provider.logo = ZipUtils.readImage(zipInputStream); + } else if (!fileName.endsWith("/")) { + Log.d(TAG, "Unknown or unused loyalty-card-custom-providers file " + fileName + ", skipping..."); } - } - - if (startsWith(nameParts, cardBaseName, 1)) { + } else if (startsWith(nameParts, cardBaseName, 1)) { // Extract cardName - cardName = nameParts[cardBaseName.length].split("\\.", 2)[0]; + cardName = nameParts[cardBaseName.length]; + + StocardRecord record = cards.get(cardName); + if (record == null) { + record = new StocardRecord(); + cards.put(cardName, record); + } // This is the card itself if (fileName.endsWith(cardName + "/content.json")) { JSONObject jsonObject = ZipUtils.readJSON(zipInputStream); - - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "cardId", - jsonObject.getString("input_id") - ); + record.cardId = jsonObject.getString("input_id"); if (jsonObject.has("input_provider_name")) { - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "store", - jsonObject.getString("input_provider_name") - ); + record.store = jsonObject.getString("input_provider_name"); + } + + if (jsonObject.has("label")) { + String label = jsonObject.getString("label"); + if (!label.isBlank()) { + record.label = label; + } } // Provider ID can be either custom or not, extract whatever version is relevant - String customProviderPrefix = "/users/" + nameParts[1] + "/loyalty-card-custom-providers/"; + String customProviderPrefix = "/users/" + userId + "/loyalty-card-custom-providers/"; String providerId = jsonObject .getJSONObject("input_provider_reference") .getString("identifier"); if (providerId.startsWith(customProviderPrefix)) { providerId = providerId.substring(customProviderPrefix.length()); + } else if (providerId.startsWith(PROVIDER_PREFIX)) { + providerId = providerId.substring(PROVIDER_PREFIX.length()); } else { - providerId = providerId.substring("/loyalty-card-providers/".length()); + throw new FormatException("Unsupported provider ID format: " + providerId); } - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "_providerId", - providerId - ); + record.providerId = providerId; if (jsonObject.has("input_barcode_format")) { - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "barcodeType", - jsonObject.getString("input_barcode_format") - ); + record.barcodeType = jsonObject.getString("input_barcode_format"); } } else if (fileName.endsWith("notes/default/content.json")) { - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "note", - ZipUtils.readJSON(zipInputStream) - .getString("content") - ); + record.note = ZipUtils.readJSON(zipInputStream).getString("content"); } else if (fileName.endsWith("usage-statistics/content.json")) { JSONArray usages = ZipUtils.readJSON(zipInputStream).getJSONArray("usages"); - if (usages.length() > 0) { - JSONObject lastUsedObject = usages.getJSONObject(usages.length() - 1); + for (int i = 0; i < usages.length(); i++) { + JSONObject lastUsedObject = usages.getJSONObject(i); String lastUsedString = lastUsedObject.getJSONObject("time").getString("value"); long timeStamp = Instant.parse(lastUsedString).getEpochSecond(); - - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "lastUsed", - timeStamp - ); + if (record.lastUsed == null || timeStamp > record.lastUsed) { + record.lastUsed = timeStamp; + } + } + } else if (fileName.matches(".*/usages/[^/]+/content.json")) { + JSONObject lastUsedObject = ZipUtils.readJSON(zipInputStream); + String lastUsedString = lastUsedObject.getJSONObject("time").getString("value"); + long timeStamp = Instant.parse(lastUsedString).getEpochSecond(); + if (record.lastUsed == null || timeStamp > record.lastUsed) { + record.lastUsed = timeStamp; } } else if (fileName.endsWith("/images/front.png") || fileName.endsWith("/images/front/front.jpg")) { - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "frontImage", - ZipUtils.readImage(zipInputStream) - ); + record.frontImage = ZipUtils.readImage(zipInputStream); } else if (fileName.endsWith("/images/back.png") || fileName.endsWith("/images/back/back.jpg")) { - loyaltyCardHashMap = appendToHashMap( - loyaltyCardHashMap, - cardName, - "backImage", - ZipUtils.readImage(zipInputStream) - ); + record.backImage = ZipUtils.readImage(zipInputStream); + } else if (!fileName.endsWith("/")) { + Log.d(TAG, "Unknown or unused loyalty-cards file " + fileName + ", skipping..."); } + } else if (!fileName.endsWith("/")) { + Log.d(TAG, "Unknown or unused file " + fileName + ", skipping..."); } } - return new ZIPData(loyaltyCardHashMap, providers); + return new ZIPData(cards, providers); } - public ImportedData importLoyaltyCardHashMap(Context context, final ZIPData zipData) { + public ImportedData importLoyaltyCardHashMap(Context context, final ZIPData zipData) throws FormatException { ImportedData importedData = new ImportedData(new ArrayList<>(), new HashMap<>()); int tempID = 0; - for (Map loyaltyCardData : zipData.loyaltyCardHashMap.values()) { - String providerId = (String) loyaltyCardData.get("_providerId"); + List cardKeys = new ArrayList<>(zipData.cards.keySet()); + Collections.sort(cardKeys); - if (providerId == null) { - Log.d(TAG, "Missing providerId for card " + loyaltyCardData + ", ignoring..."); + for (String key : cardKeys) { + StocardRecord record = zipData.cards.get(key); + + if (record.providerId == null) { + Log.d(TAG, "Missing providerId for card " + record + ", ignoring..."); continue; } - HashMap providerData = zipData.providers.get(providerId); - - // Read store from card, if not available (old export), fall back to providerData - String store; - if (loyaltyCardData.containsKey("store")) { - store = (String) loyaltyCardData.get("store"); - } else { - store = providerData != null ? providerData.get("name").toString() : providerId; + if (record.cardId == null) { + throw new FormatException("No card ID listed, but is required"); + } + + StocardProvider provider = zipData.providers.get(record.providerId); + + // Read store from card, if not available (old export), fall back to providerData + String store = record.store != null ? record.store : provider != null ? provider.name : record.providerId; + String note = record.note != null ? record.note : ""; + String barcodeTypeString = record.barcodeType != null ? record.barcodeType : provider != null ? provider.barcodeFormat : null; + + if (record.label != null && !record.label.equals(store) && !record.label.equals(note)) { + note = note.isEmpty() ? record.label : note + "\n" + record.label; } - String note = (String) Utils.mapGetOrDefault(loyaltyCardData, "note", ""); - String cardId = (String) loyaltyCardData.get("cardId"); - String barcodeTypeString = (String) Utils.mapGetOrDefault(loyaltyCardData, "barcodeType", providerData != null ? providerData.get("barcodeFormat") : null); CatimaBarcode barcodeType = null; if (barcodeTypeString != null && !barcodeTypeString.isEmpty()) { if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) { @@ -300,32 +349,25 @@ public class StocardImporter implements Importer { } int headerColor = Utils.getRandomHeaderColor(context); - Bitmap cardIcon = null; - if (providerData != null && providerData.containsKey("logo")) { - cardIcon = (Bitmap) providerData.get("logo"); - headerColor = Utils.getHeaderColorFromImage(cardIcon, headerColor); + if (provider != null && provider.logo != null) { + headerColor = Utils.getHeaderColorFromImage(provider.logo, headerColor); } - long lastUsed; - if (loyaltyCardData.containsKey("lastUsed")) { - lastUsed = (long) loyaltyCardData.get("lastUsed"); - } else { - lastUsed = Utils.getUnixTime(); - } + long lastUsed = record.lastUsed != null ? record.lastUsed : Utils.getUnixTime(); - LoyaltyCard card = new LoyaltyCard(tempID, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, 0, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, 0); + LoyaltyCard card = new LoyaltyCard(tempID, store, note, null, null, BigDecimal.valueOf(0), null, record.cardId, null, barcodeType, headerColor, 0, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, 0); importedData.cards.add(card); Map images = new HashMap<>(); - if (cardIcon != null) { - images.put(ImageLocationType.icon, cardIcon); + if (provider != null && provider.logo != null) { + images.put(ImageLocationType.icon, provider.logo); } - if (loyaltyCardData.containsKey("frontImage")) { - images.put(ImageLocationType.front, (Bitmap) loyaltyCardData.get("frontImage")); + if (record.frontImage != null) { + images.put(ImageLocationType.front, record.frontImage); } - if (loyaltyCardData.containsKey("backImage")) { - images.put(ImageLocationType.back, (Bitmap) loyaltyCardData.get("backImage")); + if (record.backImage != null) { + images.put(ImageLocationType.back, record.backImage); } importedData.images.put(tempID, images); @@ -361,16 +403,4 @@ public class StocardImporter implements Importer { return true; } - - private HashMap> appendToHashMap(HashMap> loyaltyCardHashMap, String cardID, String key, Object value) { - HashMap loyaltyCardData = loyaltyCardHashMap.get(cardID); - if (loyaltyCardData == null) { - loyaltyCardData = new HashMap<>(); - } - - loyaltyCardData.put(key, value); - loyaltyCardHashMap.put(cardID, loyaltyCardData); - - return loyaltyCardHashMap; - } } diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index 6411c7cee..8ef546371 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -1140,24 +1140,6 @@ public class ImportExportTest { LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, 1); - assertEquals("GAMMA", card.store); - assertEquals("", card.note); - assertEquals(null, card.validFrom); - assertEquals(null, card.expiry); - assertEquals(new BigDecimal("0"), card.balance); - assertEquals(null, card.balanceType); - assertEquals("55555", card.cardId); - assertEquals(null, card.barcodeId); - assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format()); - assertEquals(0, card.starStatus); - assertEquals(1625600883, card.lastUsed); - - assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front)); - assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back)); - assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon)); - - card = DBHelper.getLoyaltyCard(mDatabase, 2); - assertEquals("Air Miles", card.store); assertEquals("szjsbs", card.note); assertEquals(null, card.validFrom); @@ -1170,8 +1152,26 @@ public class ImportExportTest { assertEquals(0, card.starStatus); assertEquals(1625690099, card.lastUsed); - assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front))); - assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back))); + assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front))); + assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back))); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon)); + + card = DBHelper.getLoyaltyCard(mDatabase, 2); + + assertEquals("GAMMA", card.store); + assertEquals("", card.note); + assertEquals(null, card.validFrom); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals("55555", card.cardId); + assertEquals(null, card.barcodeId); + assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format()); + assertEquals(0, card.starStatus); + assertEquals(1625600883, card.lastUsed); + + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back)); assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.icon)); card = DBHelper.getLoyaltyCard(mDatabase, 3); @@ -1195,6 +1195,91 @@ public class ImportExportTest { TestHelpers.getEmptyDb(activity); } + @Test + public void importStocard2() { + // Copy of stocard.zip, but with an extra card using a custom provider, a label for "Miles", and /usages/ timestamp + InputStream inputStream = getClass().getResourceAsStream("stocard2.zip"); + + // Import the Stocard data + ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), mDatabase, inputStream, DataFormat.Stocard, null); + assertEquals(ImportExportResultType.Success, result.resultType()); + assertEquals(4, DBHelper.getLoyaltyCardCount(mDatabase)); + + LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, 1); + + assertEquals("Foo", card.store); + assertEquals("", card.note); + assertEquals(null, card.validFrom); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals("1234567895", card.cardId); + assertEquals(null, card.barcodeId); + assertEquals(BarcodeFormat.ITF, card.barcodeType.format()); + assertEquals(0, card.starStatus); + assertEquals(1624991439, card.lastUsed); + + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon)); + + card = DBHelper.getLoyaltyCard(mDatabase, 2); + + assertEquals("Air Miles", card.store); + assertEquals("szjsbs\nMiles", card.note); + assertEquals(null, card.validFrom); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals("7649484", card.cardId); + assertEquals(null, card.barcodeId); + assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format()); + assertEquals(0, card.starStatus); + assertEquals(1625690099, card.lastUsed); + + assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front))); + assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back))); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.icon)); + + card = DBHelper.getLoyaltyCard(mDatabase, 3); + + assertEquals("GAMMA", card.store); + assertEquals("", card.note); + assertEquals(null, card.validFrom); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals("55555", card.cardId); + assertEquals(null, card.barcodeId); + assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format()); + assertEquals(0, card.starStatus); + assertEquals(1625600883, card.lastUsed); + + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.front)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.back)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.icon)); + + card = DBHelper.getLoyaltyCard(mDatabase, 4); + + assertEquals("jö", card.store); + assertEquals("", card.note); + assertEquals(null, card.validFrom); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals("(01)09010374000019(21)02097564604859211217(10)01231287693", card.cardId); + assertEquals(null, card.barcodeId); + assertEquals(BarcodeFormat.RSS_EXPANDED, card.barcodeType.format()); + assertEquals(0, card.starStatus); + assertEquals(1625600120, card.lastUsed); + + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.front)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.back)); + assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.icon)); + + TestHelpers.getEmptyDb(activity); + } + @Test public void importVoucherVault() { InputStream inputStream = getClass().getResourceAsStream("vouchervault.json"); diff --git a/app/src/test/res/protect/card_locker/stocard2.zip b/app/src/test/res/protect/card_locker/stocard2.zip new file mode 100644 index 000000000..c61bcbc7e Binary files /dev/null and b/app/src/test/res/protect/card_locker/stocard2.zip differ