Add barcode encoding support

- Add new barcodeencoding field to database
- Read barcode encoding from pkpass file
- Add barcodeencoding to import/export
- Add barcodeencoding to share URI
- On default, use zxing's GuessEncoding function in StringUtils (this
  should not use UTF-8 unless needed)
- Allow manually forcing ISO-8859-1 or UTF-8
This commit is contained in:
Sylvia van Os
2025-09-27 00:07:25 +02:00
parent e6d7065461
commit 0c61abf4f0
25 changed files with 409 additions and 153 deletions

View File

@@ -4,17 +4,24 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.util.ArrayMap;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.StringUtils;
import java.lang.ref.WeakReference;
import java.nio.charset.Charset;
import java.util.Map;
import protect.card_locker.async.CompatCallable;
@@ -39,6 +46,7 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
private final WeakReference<TextView> textViewReference;
private String cardId;
private final CatimaBarcode format;
private final Charset encoding;
private final int imageHeight;
private final int imageWidth;
private final int imagePadding;
@@ -48,7 +56,7 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
BarcodeImageWriterTask(
Context context, ImageView imageView, String cardIdString,
CatimaBarcode barcodeFormat, TextView textView,
CatimaBarcode barcodeFormat, @Nullable Charset barcodeEncoding, TextView textView,
boolean showFallback, BarcodeImageWriterResultCallback callback, boolean roundCornerPadding, boolean isFullscreen
) {
mContext = context;
@@ -62,6 +70,7 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
cardId = cardIdString;
format = barcodeFormat;
encoding = barcodeEncoding;
int imageViewHeight = imageView.getHeight();
int imageViewWidth = imageView.getWidth();
@@ -172,10 +181,22 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
}
MultiFormatWriter writer = new MultiFormatWriter();
Map<EncodeHintType, Object> encodeHints = new ArrayMap<>();
// Use charset if defined or guess otherwise
if (encoding != null) {
Log.d(TAG, "Encoding explicitly set, " + encoding.name());
encodeHints.put(EncodeHintType.CHARACTER_SET, encoding);
} else {
String guessedEncoding = StringUtils.guessEncoding(cardId.getBytes(), new ArrayMap<>());
Log.d(TAG, "Guessed encoding: " + guessedEncoding);
encodeHints.put(EncodeHintType.CHARACTER_SET, Charset.forName(guessedEncoding));
}
BitMatrix bitMatrix;
try {
try {
bitMatrix = writer.encode(cardId, format.format(), imageWidth, imageHeight, null);
bitMatrix = writer.encode(cardId, format.format(), imageWidth, imageHeight, encodeHints);
} catch (Exception e) {
// Cast a wider net here and catch any exception, as there are some
// cases where an encoder may fail if the data is invalid for the

View File

@@ -92,13 +92,13 @@ public class BarcodeSelectorAdapter extends ArrayAdapter<CatimaBarcodeWithValue>
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null, true, false);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, null, text, true, null, true, false);
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
});
} else {
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null, true, false);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, null, text, true, null, true, false);
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
}

View File

@@ -10,8 +10,11 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Currency;
@@ -23,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 = 17;
public static final int DATABASE_VERSION = 18;
// NB: changing these values requires a migration
public static final int DEFAULT_ZOOM_LEVEL = 100;
@@ -49,6 +52,7 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String CARD_ID = "cardid";
public static final String BARCODE_ID = "barcodeid";
public static final String BARCODE_TYPE = "barcodetype";
public static final String BARCODE_ENCODING = "barcodeencoding";
public static final String STAR_STATUS = "starstatus";
public static final String LAST_USED = "lastused";
public static final String ZOOM_LEVEL = "zoomlevel";
@@ -112,6 +116,7 @@ public class DBHelper extends SQLiteOpenHelper {
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_ID + " TEXT," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
LoyaltyCardDbIds.BARCODE_ENCODING + " TEXT," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'," +
LoyaltyCardDbIds.LAST_USED + " INTEGER DEFAULT '0', " +
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '" + DEFAULT_ZOOM_LEVEL + "', " +
@@ -335,6 +340,11 @@ public class DBHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.ZOOM_LEVEL_WIDTH + " INTEGER DEFAULT '100' ");
}
if (oldVersion < 18 && newVersion >= 18) {
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.BARCODE_ENCODING + " TEXT");
}
}
public static Set<String> imageFiles(Context context, final SQLiteDatabase database) {
@@ -396,7 +406,8 @@ public class DBHelper extends SQLiteOpenHelper {
public static long insertLoyaltyCard(
final SQLiteDatabase database, final String store, final String note, final Date validFrom,
final Date expiry, final BigDecimal balance, final Currency balanceType, final String cardId,
final String barcodeId, final CatimaBarcode barcodeType, final Integer headerColor,
final String barcodeId, final CatimaBarcode barcodeType, final @Nullable Charset barcodeEncoding,
final Integer headerColor,
final int starStatus, final Long lastUsed, final int archiveStatus) {
database.beginTransaction();
@@ -411,6 +422,7 @@ public class DBHelper extends SQLiteOpenHelper {
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
contentValues.put(LoyaltyCardDbIds.BARCODE_ENCODING, barcodeEncoding != null ? barcodeEncoding.name() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
@@ -430,7 +442,8 @@ public class DBHelper extends SQLiteOpenHelper {
final SQLiteDatabase database, final int id, final String store, final String note,
final Date validFrom, final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId, final String barcodeId,
final CatimaBarcode barcodeType, final Integer headerColor, final int starStatus,
final CatimaBarcode barcodeType, final @Nullable Charset barcodeEncoding,
final Integer headerColor, final int starStatus,
final Long lastUsed, final int archiveStatus) {
database.beginTransaction();
@@ -446,6 +459,7 @@ public class DBHelper extends SQLiteOpenHelper {
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
contentValues.put(LoyaltyCardDbIds.BARCODE_ENCODING, barcodeEncoding != null ? barcodeEncoding.name() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
@@ -465,7 +479,8 @@ public class DBHelper extends SQLiteOpenHelper {
SQLiteDatabase database, final int id, final String store, final String note,
final Date validFrom, final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId, final String barcodeId,
final CatimaBarcode barcodeType, final Integer headerColor, final int starStatus,
final CatimaBarcode barcodeType, final @Nullable Charset barcodeEncoding,
final Integer headerColor, final int starStatus,
final Long lastUsed, final int archiveStatus) {
database.beginTransaction();
@@ -480,6 +495,7 @@ public class DBHelper extends SQLiteOpenHelper {
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
contentValues.put(LoyaltyCardDbIds.BARCODE_ENCODING, barcodeEncoding != null ? barcodeEncoding.name() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());

View File

@@ -4,11 +4,14 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.Nullable;
import java.io.InvalidObjectException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Currency;
import java.util.Date;
@@ -25,6 +28,7 @@ public class ImportURIHelper {
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_ID = DBHelper.LoyaltyCardDbIds.BARCODE_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
private static final String BARCODE_ENCODING = DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING;
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
private final Context context;
@@ -66,6 +70,7 @@ public class ImportURIHelper {
try {
// These values are allowed to be null
CatimaBarcode barcodeType = null;
Charset barcodeEncoding = null;
Date validFrom = null;
Date expiry = null;
BigDecimal balance = new BigDecimal("0");
@@ -103,6 +108,11 @@ public class ImportURIHelper {
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
}
String unparsedBarcodeEncoding = kv.get(BARCODE_ENCODING);
if (unparsedBarcodeEncoding != null && !unparsedBarcodeEncoding.equals("")) {
barcodeEncoding = Charset.forName(unparsedBarcodeEncoding);
}
String unparsedBalance = kv.get(BALANCE);
if (unparsedBalance != null && !unparsedBalance.equals("")) {
balance = new BigDecimal(unparsedBalance);
@@ -136,6 +146,7 @@ public class ImportURIHelper {
cardId,
barcodeId,
barcodeType,
barcodeEncoding,
headerColor,
0,
Utils.getUnixTime(),
@@ -195,6 +206,9 @@ public class ImportURIHelper {
if (loyaltyCard.barcodeType != null) {
fragment = appendFragment(fragment, BARCODE_TYPE, loyaltyCard.barcodeType.name());
}
if (loyaltyCard.barcodeEncoding != null) {
fragment = appendFragment(fragment, BARCODE_ENCODING, loyaltyCard.barcodeEncoding.name());
}
if (loyaltyCard.headerColor != null) {
fragment = appendFragment(fragment, HEADER_COLOR, loyaltyCard.headerColor.toString());
}

View File

@@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.Currency;
import java.util.Date;
import java.util.List;
@@ -31,6 +32,8 @@ public class LoyaltyCard {
@Nullable
public CatimaBarcode barcodeType;
@Nullable
public Charset barcodeEncoding;
@Nullable
public Integer headerColor;
public int starStatus;
public long lastUsed;
@@ -62,6 +65,7 @@ public class LoyaltyCard {
public static final String BUNDLE_LOYALTY_CARD_CARD_ID = "loyaltyCardCardId";
public static final String BUNDLE_LOYALTY_CARD_BARCODE_ID = "loyaltyCardBarcodeId";
public static final String BUNDLE_LOYALTY_CARD_BARCODE_TYPE = "loyaltyCardBarcodeType";
public static final String BUNDLE_LOYALTY_CARD_BARCODE_ENCODING = "loyaltyCardBarcodeEncoding";
public static final String BUNDLE_LOYALTY_CARD_HEADER_COLOR = "loyaltyCardHeaderColor";
public static final String BUNDLE_LOYALTY_CARD_STAR_STATUS = "loyaltyCardStarStatus";
public static final String BUNDLE_LOYALTY_CARD_LAST_USED = "loyaltyCardLastUsed";
@@ -90,6 +94,7 @@ public class LoyaltyCard {
setCardId("");
setBarcodeId(null);
setBarcodeType(null);
setBarcodeEncoding(null);
setHeaderColor(null);
setStarStatus(0);
setLastUsed(Utils.getUnixTime());
@@ -124,7 +129,7 @@ public class LoyaltyCard {
public LoyaltyCard(final int id, final String store, final String note, @Nullable final Date validFrom,
@Nullable final Date expiry, final BigDecimal balance, @Nullable final Currency balanceType,
final String cardId, @Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
@Nullable final Integer headerColor, final int starStatus,
@Nullable final Charset barcodeEncoding, @Nullable final Integer headerColor, final int starStatus,
final long lastUsed, final int zoomLevel, final int zoomLevelWidth, final int archiveStatus,
@Nullable Bitmap imageThumbnail, @Nullable String imageThumbnailPath,
@Nullable Bitmap imageFront, @Nullable String imageFrontPath,
@@ -139,6 +144,7 @@ public class LoyaltyCard {
setCardId(cardId);
setBarcodeId(barcodeId);
setBarcodeType(barcodeType);
setBarcodeEncoding(barcodeEncoding);
setHeaderColor(headerColor);
setStarStatus(starStatus);
setLastUsed(lastUsed);
@@ -244,6 +250,10 @@ public class LoyaltyCard {
this.barcodeType = barcodeType;
}
public void setBarcodeEncoding(@Nullable Charset barcodeEncoding) {
this.barcodeEncoding = barcodeEncoding;
}
public void setHeaderColor(@Nullable Integer headerColor) {
this.headerColor = headerColor;
}
@@ -379,6 +389,11 @@ public class LoyaltyCard {
} else if (requireFull) {
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BARCODE_TYPE);
}
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_BARCODE_ENCODING)) {
setBarcodeEncoding(Charset.forName(bundle.getString(BUNDLE_LOYALTY_CARD_BARCODE_ENCODING)));
} else if (requireFull) {
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BARCODE_ENCODING);
}
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_HEADER_COLOR)) {
int tmpHeaderColor = bundle.getInt(BUNDLE_LOYALTY_CARD_HEADER_COLOR);
setHeaderColor(tmpHeaderColor != -1 ? tmpHeaderColor : null);
@@ -462,6 +477,9 @@ public class LoyaltyCard {
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BARCODE_TYPE)) {
bundle.putString(BUNDLE_LOYALTY_CARD_BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
}
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BARCODE_ENCODING)) {
bundle.putString(BUNDLE_LOYALTY_CARD_BARCODE_ENCODING, barcodeEncoding != null ? barcodeEncoding.name() : null);
}
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_HEADER_COLOR)) {
bundle.putInt(BUNDLE_LOYALTY_CARD_HEADER_COLOR, headerColor != null ? headerColor : -1);
}
@@ -539,6 +557,9 @@ public class LoyaltyCard {
// barcodeType
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
CatimaBarcode barcodeType = !cursor.isNull(barcodeTypeColumn) ? CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn)) : null;
// barcodeEncoding
int barcodeEncodingColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING);
Charset barcodeEncoding = !cursor.isNull(barcodeEncodingColumn) ? Charset.forName(cursor.getString(barcodeEncodingColumn)) : null;
// headerColor
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
Integer headerColor = !cursor.isNull(headerColorColumn) ? cursor.getInt(headerColorColumn) : null;
@@ -564,6 +585,7 @@ public class LoyaltyCard {
cardId,
barcodeId,
barcodeType,
barcodeEncoding,
headerColor,
starStatus,
lastUsed,
@@ -593,6 +615,7 @@ public class LoyaltyCard {
Utils.equals(a.barcodeId, b.barcodeId) && // nullable String
Utils.equals(a.barcodeType == null ? null : a.barcodeType.format(),
b.barcodeType == null ? null : b.barcodeType.format()) && // nullable CatimaBarcode with no overridden .equals(), so we need to check .format()
Utils.equals(a.barcodeEncoding, b.barcodeEncoding) && // nullable String
Utils.equals(a.headerColor, b.headerColor) && // nullable Integer
a.starStatus == b.starStatus && // non-nullable int
a.archiveStatus == b.archiveStatus && // non-nullable int
@@ -619,7 +642,7 @@ public class LoyaltyCard {
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"
+ " balance=%s,%n balanceType=%s,%n cardId=%s,%n barcodeId=%s,%n barcodeType=%s,%n barcodeEncoding=%s,%n"
+ " headerColor=%s,%n starStatus=%s,%n lastUsed=%s,%n zoomLevel=%s,%n zoomLevelWidth=%s,%n archiveStatus=%s%n"
+ " imageThumbnail=%s,%n imageThumbnailPath=%s,%n imageFront=%s,%n imageFrontPath=%s,%n imageBack=%s,%n imageBackPath=%s,%n}",
this.id,
@@ -632,6 +655,7 @@ public class LoyaltyCard {
this.cardId,
this.barcodeId,
this.barcodeType != null ? this.barcodeType.format() : null,
this.barcodeEncoding != null ? this.barcodeEncoding.name() : null,
this.headerColor,
this.starStatus,
this.lastUsed,

View File

@@ -70,6 +70,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
@@ -128,6 +130,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
TextView cardIdFieldView;
AutoCompleteTextView barcodeIdField;
AutoCompleteTextView barcodeTypeField;
AutoCompleteTextView barcodeEncodingField;
ImageView barcodeImage;
View barcodeImageLayout;
View barcodeCaptureLayout;
@@ -229,6 +232,14 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
viewModel.setHasChanged(true);
}
protected void setLoyaltyCardBarcodeEncoding(@Nullable Charset barcodeEncoding) {
viewModel.getLoyaltyCard().setBarcodeEncoding(barcodeEncoding);
generateBarcode();
viewModel.setHasChanged(true);
}
protected void setLoyaltyCardHeaderColor(@Nullable Integer headerColor) {
viewModel.getLoyaltyCard().setHeaderColor(headerColor);
@@ -334,6 +345,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
cardIdFieldView = binding.cardIdView;
barcodeIdField = binding.barcodeIdField;
barcodeTypeField = binding.barcodeTypeField;
barcodeEncodingField = binding.barcodeEncodingField;
barcodeImage = binding.barcode;
barcodeImage.setClipToOutline(true);
barcodeImageLayout = binding.barcodeLayout;
@@ -577,6 +589,30 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
}
});
barcodeEncodingField.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!s.toString().isEmpty()) {
Log.d(TAG, "Setting barcode encoding to " + s.toString());
if (s.toString().equals(getString(R.string.automatic))) {
setLoyaltyCardBarcodeEncoding(null);
} else {
setLoyaltyCardBarcodeEncoding(Charset.forName(s.toString()));
}
}
}
@Override
public void afterTextChanged(Editable s) {
ArrayList<String> barcodeEncodingList = new ArrayList<>();
barcodeEncodingList.add(getString(R.string.automatic));
barcodeEncodingList.add(StandardCharsets.ISO_8859_1.name());
barcodeEncodingList.add(StandardCharsets.UTF_8.name());
ArrayAdapter<String> barcodeEncodingAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, barcodeEncodingList);
barcodeEncodingField.setAdapter(barcodeEncodingAdapter);
}
});
binding.tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
@@ -773,6 +809,8 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
barcodeIdField.setText(barcodeId != null && !barcodeId.isEmpty() ? barcodeId : getString(R.string.sameAsCardId));
CatimaBarcode barcodeType = viewModel.getLoyaltyCard().barcodeType;
barcodeTypeField.setText(barcodeType != null ? barcodeType.prettyName() : getString(R.string.noBarcode));
Charset barcodeEncoding = viewModel.getLoyaltyCard().barcodeEncoding;
barcodeEncodingField.setText(barcodeEncoding != null ? barcodeEncoding.name() : getString(R.string.automatic));
// We set the balance here (with onResuming/onRestoring == true) to prevent formatBalanceCurrencyField() from setting it (via onTextChanged),
// which can cause issues when switching locale because it parses the balance and e.g. the decimal separator may have changed.
@@ -1479,9 +1517,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
// This makes the DBHelper set it to the current date
// So that new and edited card are always on top when sorting by recently used
if (viewModel.getUpdateLoyaltyCard()) {
DBHelper.updateLoyaltyCard(mDatabase, viewModel.getLoyaltyCardId(), viewModel.getLoyaltyCard().store, viewModel.getLoyaltyCard().note, viewModel.getLoyaltyCard().validFrom, viewModel.getLoyaltyCard().expiry, viewModel.getLoyaltyCard().balance, viewModel.getLoyaltyCard().balanceType, viewModel.getLoyaltyCard().cardId, viewModel.getLoyaltyCard().barcodeId, viewModel.getLoyaltyCard().barcodeType, viewModel.getLoyaltyCard().headerColor, viewModel.getLoyaltyCard().starStatus, null, viewModel.getLoyaltyCard().archiveStatus);
DBHelper.updateLoyaltyCard(mDatabase, viewModel.getLoyaltyCardId(), viewModel.getLoyaltyCard().store, viewModel.getLoyaltyCard().note, viewModel.getLoyaltyCard().validFrom, viewModel.getLoyaltyCard().expiry, viewModel.getLoyaltyCard().balance, viewModel.getLoyaltyCard().balanceType, viewModel.getLoyaltyCard().cardId, viewModel.getLoyaltyCard().barcodeId, viewModel.getLoyaltyCard().barcodeType, viewModel.getLoyaltyCard().barcodeEncoding, viewModel.getLoyaltyCard().headerColor, viewModel.getLoyaltyCard().starStatus, null, viewModel.getLoyaltyCard().archiveStatus);
} else {
viewModel.setLoyaltyCardId((int) DBHelper.insertLoyaltyCard(mDatabase, viewModel.getLoyaltyCard().store, viewModel.getLoyaltyCard().note, viewModel.getLoyaltyCard().validFrom, viewModel.getLoyaltyCard().expiry, viewModel.getLoyaltyCard().balance, viewModel.getLoyaltyCard().balanceType, viewModel.getLoyaltyCard().cardId, viewModel.getLoyaltyCard().barcodeId, viewModel.getLoyaltyCard().barcodeType, viewModel.getLoyaltyCard().headerColor, 0, null, 0));
viewModel.setLoyaltyCardId((int) DBHelper.insertLoyaltyCard(mDatabase, viewModel.getLoyaltyCard().store, viewModel.getLoyaltyCard().note, viewModel.getLoyaltyCard().validFrom, viewModel.getLoyaltyCard().expiry, viewModel.getLoyaltyCard().balance, viewModel.getLoyaltyCard().balanceType, viewModel.getLoyaltyCard().cardId, viewModel.getLoyaltyCard().barcodeId, viewModel.getLoyaltyCard().barcodeType, viewModel.getLoyaltyCard().barcodeEncoding, viewModel.getLoyaltyCard().headerColor, 0, null, 0));
}
try {
@@ -1596,6 +1634,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
String cardIdString = viewModel.getLoyaltyCard().barcodeId != null ? viewModel.getLoyaltyCard().barcodeId : viewModel.getLoyaltyCard().cardId;
CatimaBarcode barcodeFormat = viewModel.getLoyaltyCard().barcodeType;
Charset barcodeEncoding = viewModel.getLoyaltyCard().barcodeEncoding;
if (cardIdString == null || cardIdString.isEmpty() || barcodeFormat == null) {
barcodeImageLayout.setVisibility(View.GONE);
@@ -1615,13 +1654,13 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "ImageView size now known");
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, LoyaltyCardEditActivity.this, true, false);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, barcodeEncoding, null, false, LoyaltyCardEditActivity.this, true, false);
viewModel.getTaskHandler().executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
});
} else {
Log.d(TAG, "ImageView size known known, creating barcode");
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, this, true, false);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, barcodeEncoding, null, false, this, true, false);
viewModel.getTaskHandler().executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
}

View File

@@ -39,6 +39,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
@@ -58,6 +59,7 @@ import com.google.zxing.BarcodeFormat;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
@@ -87,6 +89,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
String cardIdString;
String barcodeIdString;
CatimaBarcode format;
@Nullable
Charset barcodeEncoding;
Bitmap frontImageBitmap;
Bitmap backImageBitmap;
@@ -686,6 +690,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
format = loyaltyCard.barcodeType;
cardIdString = loyaltyCard.cardId;
barcodeIdString = loyaltyCard.barcodeId;
barcodeEncoding = loyaltyCard.barcodeEncoding;
binding.mainImageDescription.setText(loyaltyCard.cardId);
@@ -959,6 +964,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
barcodeRenderTarget,
barcodeIdString != null ? barcodeIdString : cardIdString,
format,
barcodeEncoding,
null,
false,
this,

View File

@@ -14,6 +14,7 @@ import org.json.JSONObject
import java.io.FileNotFoundException
import java.io.IOException
import java.math.BigDecimal
import java.nio.charset.Charset
import java.text.DateFormat
import java.text.ParseException
import java.time.ZonedDateTime
@@ -40,6 +41,7 @@ class PkpassParser(context: Context, uri: Uri?) {
private var cardId: String = context.getString(R.string.noBarcode)
private var barcodeId: String? = null
private var barcodeType: CatimaBarcode? = null
private var barcodeEncoding: Charset? = null
private var headerColor: Int? = null
private val starStatus = 0
private val lastUsed: Long = 0
@@ -134,6 +136,7 @@ class PkpassParser(context: Context, uri: Uri?) {
cardId,
barcodeId,
barcodeType,
barcodeEncoding,
headerColor,
starStatus,
lastUsed,
@@ -342,13 +345,14 @@ class PkpassParser(context: Context, uri: Uri?) {
else -> throw IllegalArgumentException("No valid barcode type")
}
// FIXME: We probably need to do something with the messageEncoding field
try {
cardId = barcodeInfo.getString("altText")
barcodeId = barcodeInfo.getString("message")
barcodeEncoding = Charset.forName(barcodeInfo.getString("messageEncoding"))
} catch (ignored: JSONException) {
cardId = barcodeInfo.getString("message")
barcodeId = null
barcodeEncoding = Charset.forName(barcodeInfo.getString("messageEncoding"))
}
// Don't set barcodeId if it's the same as cardId

View File

@@ -52,6 +52,7 @@ public class CardsContentProvider extends ContentProvider {
LoyaltyCardDbIds.CARD_ID,
LoyaltyCardDbIds.BARCODE_ID,
LoyaltyCardDbIds.BARCODE_TYPE,
// FIXME: Expose BARCODE_ENCODING but without ever exposing the null value (so apps using this don't have to guess)
LoyaltyCardDbIds.STAR_STATUS,
LoyaltyCardDbIds.LAST_USED,
LoyaltyCardDbIds.ARCHIVE_STATUS,

View File

@@ -134,6 +134,7 @@ public class CatimaExporter implements Exporter {
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.BARCODE_ID,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.STAR_STATUS,
DBHelper.LoyaltyCardDbIds.LAST_USED,
@@ -154,6 +155,7 @@ public class CatimaExporter implements Exporter {
card.cardId,
card.barcodeId,
card.barcodeType != null ? card.barcodeType.name() : "",
card.barcodeEncoding != null ? card.barcodeEncoding.name() : "",
card.headerColor,
card.starStatus,
card.lastUsed,

View File

@@ -20,6 +20,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Currency;
@@ -127,10 +128,10 @@ public class CatimaImporter implements Importer {
LoyaltyCard existing = DBHelper.getLoyaltyCard(context, database, card.id);
if (existing == null) {
DBHelper.insertLoyaltyCard(database, card.id, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
card.cardId, card.barcodeId, card.barcodeType, card.barcodeEncoding, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
} else if (!isDuplicate(context, existing, card, existingImages, imageChecksums)) {
long newId = DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
card.cardId, card.barcodeId, card.barcodeType, card.barcodeEncoding, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
idMap.put(card.id, (int) newId);
}
}
@@ -458,6 +459,12 @@ public class CatimaImporter implements Importer {
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
}
Charset barcodeEncoding = null;
String unparsedBarcodeEncoding = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING, record, "");
if (!unparsedBarcodeEncoding.isEmpty()) {
barcodeEncoding = Charset.forName(unparsedBarcodeEncoding);
}
Integer headerColor = null;
try {
headerColor = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record);
@@ -501,6 +508,7 @@ public class CatimaImporter implements Importer {
cardId,
barcodeId,
barcodeType,
barcodeEncoding,
headerColor,
starStatus,
lastUsed,

View File

@@ -160,6 +160,7 @@ public class FidmeImporter implements Importer {
cardId,
null,
barcodeType,
null,
headerColor,
starStatus,
Utils.getUnixTime(),
@@ -181,7 +182,7 @@ public class FidmeImporter implements Importer {
for (LoyaltyCard card : data.cards) {
// Do not use card.id which is set to -1
DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
card.cardId, card.barcodeId, card.barcodeType, card.barcodeEncoding, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
}
}
}

View File

@@ -162,6 +162,7 @@ public class VoucherVaultImporter implements Importer {
cardId,
null,
barcodeType,
null,
headerColor,
0,
Utils.getUnixTime(),
@@ -186,7 +187,7 @@ public class VoucherVaultImporter implements Importer {
for (LoyaltyCard card : data.cards) {
// Do not use card.id which is set to -1
DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
card.cardId, card.barcodeId, card.barcodeType, card.barcodeEncoding, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
}
}
}