From 2d6dfdcfdf3bd58866156376f78ae26c87c4a248 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 14 Feb 2021 23:32:07 +0100 Subject: [PATCH 01/28] Initial balance support --- CHANGELOG.md | 6 + .../card_locker/CsvDatabaseExporter.java | 4 + .../card_locker/CsvDatabaseImporter.java | 14 +- .../java/protect/card_locker/DBHelper.java | 29 ++- .../protect/card_locker/ImportURIHelper.java | 18 +- .../java/protect/card_locker/LoyaltyCard.java | 18 +- .../card_locker/LoyaltyCardCursorAdapter.java | 11 ++ .../card_locker/LoyaltyCardEditActivity.java | 171 ++++++++++++++++-- .../card_locker/LoyaltyCardViewActivity.java | 14 +- .../main/java/protect/card_locker/Utils.java | 42 +++++ .../res/layout/loyalty_card_edit_activity.xml | 51 +++++- .../main/res/layout/loyalty_card_layout.xml | 8 + .../res/layout/loyalty_card_view_layout.xml | 11 ++ app/src/main/res/values/strings.xml | 7 + 14 files changed, 380 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc08c7ec..9e1c65227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +Changes: + +- Add balance support + ## v1.8.1 (2021-02-12) Changes: diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java index a4c8fdb28..dd8a3037c 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java @@ -50,6 +50,8 @@ public class CsvDatabaseExporter implements DatabaseExporter DBHelper.LoyaltyCardDbIds.STORE, DBHelper.LoyaltyCardDbIds.NOTE, DBHelper.LoyaltyCardDbIds.EXPIRY, + DBHelper.LoyaltyCardDbIds.BALANCE, + DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, DBHelper.LoyaltyCardDbIds.CARD_ID, DBHelper.LoyaltyCardDbIds.HEADER_COLOR, DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, @@ -65,6 +67,8 @@ public class CsvDatabaseExporter implements DatabaseExporter card.store, card.note, card.expiry != null ? card.expiry.getTime() : "", + card.balance, + card.balanceType, card.cardId, card.headerColor, card.barcodeType, diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java index 5040ddfcd..c7aae8ef2 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java @@ -10,6 +10,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; +import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -301,6 +302,17 @@ public class CsvDatabaseImporter implements DatabaseImporter expiry = new Date(extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true)); } catch (NullPointerException | FormatException e) { } + BigDecimal balance; + String balanceType = null; + try { + balance = new BigDecimal(extractString(DBHelper.LoyaltyCardDbIds.BALANCE, record, null)); + balanceType = extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, null); + } catch (FormatException _e ) { + // These fields did not exist in versions 1.8.1 and before + // We catch this exception so we can still import old backups + balance = new BigDecimal("0.0"); + } + String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, ""); if(cardId.isEmpty()) { @@ -324,7 +336,7 @@ public class CsvDatabaseImporter implements DatabaseImporter // We catch this exception so we can still import old backups } if (starStatus != 1) starStatus = 0; - helper.insertLoyaltyCard(database, id, store, note, expiry, cardId, barcodeType, headerColor, starStatus); + helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, starStatus); } /** diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 1c91b48e6..402585ee2 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -8,6 +8,7 @@ import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Color; +import java.math.BigDecimal; import java.util.Date; import java.util.ArrayList; import java.util.List; @@ -16,7 +17,7 @@ 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 = 7; + public static final int DATABASE_VERSION = 8; static class LoyaltyCardDbGroups { @@ -31,6 +32,8 @@ public class DBHelper extends SQLiteOpenHelper public static final String ID = "_id"; public static final String STORE = "store"; public static final String EXPIRY = "expiry"; + public static final String BALANCE = "balance"; + public static final String BALANCE_TYPE = "balance_type"; public static final String NOTE = "note"; public static final String HEADER_COLOR = "headercolor"; public static final String HEADER_TEXT_COLOR = "headertextcolor"; @@ -60,11 +63,14 @@ public class DBHelper extends SQLiteOpenHelper LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0')"); // create table for cards + // Balance is TEXT and not REAL to be able to store a BigDecimal without precision loss db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" + LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," + LoyaltyCardDbIds.STORE + " TEXT not null," + LoyaltyCardDbIds.NOTE + " TEXT not null," + LoyaltyCardDbIds.EXPIRY + " INTEGER," + + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0.0'," + + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," + LoyaltyCardDbIds.CARD_ID + " TEXT not null," + @@ -128,9 +134,18 @@ public class DBHelper extends SQLiteOpenHelper db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + " ADD COLUMN " + LoyaltyCardDbIds.EXPIRY + " INTEGER"); } + + if(oldVersion < 8 && newVersion >= 8) + { + db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + + " ADD COLUMN " + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0.0'"); + db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + + " ADD COLUMN " + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT"); + } } public long insertLoyaltyCard(final String store, final String note, final Date expiry, + final BigDecimal balance, final String balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -139,6 +154,8 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.STORE, store); contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); + contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -149,7 +166,8 @@ public class DBHelper extends SQLiteOpenHelper } public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store, - final String note, final Date expiry, final String cardId, + final String note, final Date expiry, final BigDecimal balance, + final String balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -158,6 +176,8 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.STORE, store); contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); + contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -168,7 +188,8 @@ public class DBHelper extends SQLiteOpenHelper } public boolean updateLoyaltyCard(final int id, final String store, final String note, - final Date expiry, final String cardId, + final Date expiry, final BigDecimal balance, + final String balanceType, final String cardId, final String barcodeType, final Integer headerColor) { SQLiteDatabase db = getWritableDatabase(); @@ -176,6 +197,8 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.STORE, store); contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); + contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); diff --git a/app/src/main/java/protect/card_locker/ImportURIHelper.java b/app/src/main/java/protect/card_locker/ImportURIHelper.java index ba77d83cf..9e276e630 100644 --- a/app/src/main/java/protect/card_locker/ImportURIHelper.java +++ b/app/src/main/java/protect/card_locker/ImportURIHelper.java @@ -4,12 +4,15 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import java.io.InvalidObjectException; +import java.math.BigDecimal; import java.util.Date; public class ImportURIHelper { private static final String STORE = DBHelper.LoyaltyCardDbIds.STORE; private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE; private static final String EXPIRY = DBHelper.LoyaltyCardDbIds.EXPIRY; + private static final String BALANCE = DBHelper.LoyaltyCardDbIds.BALANCE; + private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE; private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID; private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE; @@ -43,6 +46,8 @@ public class ImportURIHelper { try { // These values are allowed to be null Date expiry = null; + BigDecimal balance = new BigDecimal("0.0"); + String balanceType = null; Integer headerColor = null; Integer headerTextColor = null; @@ -57,13 +62,24 @@ public class ImportURIHelper { { expiry = new Date(Long.parseLong(unparsedExpiry)); } + String unparsedBalance = uri.getQueryParameter(BALANCE); + if(unparsedBalance != null && !unparsedBalance.equals("")) + { + balance = new BigDecimal(unparsedBalance); + } + String unparsedBalanceType = uri.getQueryParameter(BALANCE_TYPE); + if (unparsedBalanceType != null && !unparsedBalanceType.equals("")) + { + balanceType = unparsedBalanceType; + } + String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR); if(unparsedHeaderColor != null) { headerColor = Integer.parseInt(unparsedHeaderColor); } - return new LoyaltyCard(-1, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, 0); + return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, 0); } catch (NullPointerException | NumberFormatException ex) { throw new InvalidObjectException("Not a valid import URI"); } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCard.java b/app/src/main/java/protect/card_locker/LoyaltyCard.java index b8dcaf1d8..39e22f05a 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCard.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCard.java @@ -2,7 +2,8 @@ package protect.card_locker; import android.database.Cursor; -import java.text.DateFormat; +import java.math.BigDecimal; +import java.util.Currency; import java.util.Date; import androidx.annotation.Nullable; @@ -13,6 +14,8 @@ public class LoyaltyCard public final String store; public final String note; public final Date expiry; + public final BigDecimal balance; + public final Currency balanceType; public final String cardId; public final String barcodeType; @@ -24,7 +27,8 @@ public class LoyaltyCard public final int starStatus; - public LoyaltyCard(final int id, final String store, final String note, final Date expiry, final String cardId, + public LoyaltyCard(final int id, final String store, final String note, final Date expiry, + final BigDecimal balance, final String balanceType, final String cardId, final String barcodeType, final Integer headerColor, final Integer headerTextColor, final int starStatus) { @@ -32,6 +36,12 @@ public class LoyaltyCard this.store = store; this.note = note; this.expiry = expiry; + this.balance = balance; + if (balanceType != null) { + this.balanceType = Currency.getInstance(balanceType); + } else { + this.balanceType = null; + } this.cardId = cardId; this.barcodeType = barcodeType; this.headerColor = headerColor; @@ -45,6 +55,8 @@ public class LoyaltyCard String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)); String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)); long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)); + BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE))); + String balanceType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)); String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)); String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)); int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)); @@ -71,6 +83,6 @@ public class LoyaltyCard headerTextColor = cursor.getInt(headerTextColorColumn); } - return new LoyaltyCard(id, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, starred); + return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, starred); } } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java index fa7568547..4f069c5ed 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java @@ -9,6 +9,7 @@ import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.TextView; +import java.math.BigDecimal; import java.text.DateFormat; import java.util.Date; @@ -41,6 +42,7 @@ class LoyaltyCardCursorAdapter extends CursorAdapter ImageView thumbnail = view.findViewById(R.id.thumbnail); TextView storeField = view.findViewById(R.id.store); TextView noteField = view.findViewById(R.id.note); + TextView balanceField = view.findViewById(R.id.balance); TextView expiryField = view.findViewById(R.id.expiry); ImageView star = view.findViewById(R.id.star); @@ -63,6 +65,15 @@ class LoyaltyCardCursorAdapter extends CursorAdapter noteField.setVisibility(View.GONE); } + if(!loyaltyCard.balance.equals(new BigDecimal(0))) { + balanceField.setVisibility(View.VISIBLE); + balanceField.setText(context.getString(R.string.balanceSentence, Utils.formatBalance(loyaltyCard.balance, loyaltyCard.balanceType))); + } + else + { + balanceField.setVisibility(View.GONE); + } + if(loyaltyCard.expiry != null) { expiryField.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 3e3689113..61202eb4f 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -1,5 +1,6 @@ package protect.card_locker; +import android.annotation.SuppressLint; import android.app.DatePickerDialog; import android.app.Dialog; import android.content.DialogInterface; @@ -7,6 +8,7 @@ import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import com.google.android.material.chip.Chip; @@ -19,6 +21,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.DialogFragment; +import android.os.LocaleList; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -45,11 +48,21 @@ import com.jaredrummler.android.colorpicker.ColorPickerDialog; import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; import java.io.InvalidObjectException; +import java.math.BigDecimal; import java.text.DateFormat; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Currency; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Locale; public class LoyaltyCardEditActivity extends AppCompatActivity { @@ -62,6 +75,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity EditText noteFieldEdit; ChipGroup groupsChips; AutoCompleteTextView expiryField; + EditText balanceField; + AutoCompleteTextView balanceCurrencyField; View cardAndBarcodeLayout; TextView cardIdFieldView; AutoCompleteTextView barcodeTypeField; @@ -86,6 +101,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity boolean initDone = false; AlertDialog confirmExitDialog = null; + boolean validBalance = true; + + HashMap currencies = new HashMap<>(); + private void extractIntentFields(Intent intent) { final Bundle b = intent.getExtras(); @@ -120,12 +139,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity db = new DBHelper(this); importUriHelper = new ImportURIHelper(this); + for (Currency currency : Currency.getAvailableCurrencies()) { + currencies.put(currency.getSymbol(), currency); + } + tabs = findViewById(R.id.tabs); thumbnail = findViewById(R.id.thumbnail); storeFieldEdit = findViewById(R.id.storeNameEdit); noteFieldEdit = findViewById(R.id.noteEdit); groupsChips = findViewById(R.id.groupChips); expiryField = findViewById(R.id.expiryField); + balanceField = findViewById(R.id.balanceField); + balanceCurrencyField = findViewById(R.id.balanceCurrencyField); cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout); cardIdFieldView = findViewById(R.id.cardIdView); barcodeTypeField = findViewById(R.id.barcodeTypeField); @@ -183,6 +208,88 @@ public class LoyaltyCardEditActivity extends AppCompatActivity } }); + balanceField.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + try { + balanceField.setTag(Utils.parseCurrencyInUserLocale(s.toString())); + validBalance = true; + } catch (ParseException | NumberFormatException e) { + validBalance = false; + e.printStackTrace(); + } + } + + @Override + public void afterTextChanged(Editable s) { } + }); + + balanceCurrencyField.addTextChangedListener(new TextWatcher() { + CharSequence lastValue; + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + lastValue = s; + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + hasChanged = true; + + Currency currency; + + if (s.toString().equals(getString(R.string.points))) { + currency = null; + } else { + currency = currencies.get(s.toString()); + } + + balanceCurrencyField.setTag(currency); + + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), currency)); + } + + @Override + public void afterTextChanged(Editable s) { + ArrayList currencyList = new ArrayList<>(currencies.keySet()); + Collections.sort(currencyList, (o1, o2) -> { + boolean o1ascii = o1.matches("^[^a-zA-Z]*$"); + boolean o2ascii = o2.matches("^[^a-zA-Z]*$"); + + if (!o1ascii && o2ascii) { + return 1; + } else if (o1ascii && !o2ascii) { + return -1; + } + + return o1.compareTo(o2); + }); + + // Sort locale currencies on top + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + LocaleList locales = getApplicationContext().getResources().getConfiguration().getLocales(); + + for (int i = locales.size() - 1; i > 0; i--) { + Locale locale = locales.get(i); + String currencySymbol = Currency.getInstance(locale).getSymbol(); + currencyList.remove(currencySymbol); + currencyList.add(0, currencySymbol); + } + } else { + String currencySymbol = Currency.getInstance(getApplicationContext().getResources().getConfiguration().locale).getSymbol(); + currencyList.remove(currencySymbol); + currencyList.add(0, currencySymbol); + } + + currencyList.add(0, getString(R.string.points)); + ArrayAdapter currencyAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, currencyList); + balanceCurrencyField.setAdapter(currencyAdapter); + } + }); + cardIdFieldView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -262,10 +369,15 @@ public class LoyaltyCardEditActivity extends AppCompatActivity noteFieldEdit.setText(""); expiryField.setTag(null); expiryField.setText(""); + balanceField.setTag(null); + balanceField.setText(""); + balanceCurrencyField.setTag(null); + balanceCurrencyField.setText(""); cardIdFieldView.setText(""); barcodeTypeField.setText(""); } + @SuppressLint("DefaultLocale") @Override public void onResume() { @@ -297,11 +409,19 @@ public class LoyaltyCardEditActivity extends AppCompatActivity if(expiryField.getText().length() == 0) { expiryField.setTag(loyaltyCard.expiry); - if (loyaltyCard.expiry == null) { - expiryField.setText(getString(R.string.never)); - } else { - expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)); - } + formatExpiryField(loyaltyCard.expiry); + } + + if(balanceField.getText().length() == 0) + { + balanceField.setTag(loyaltyCard.balance); + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType)); + } + + if(balanceCurrencyField.getText().length() == 0) + { + balanceCurrencyField.setTag(loyaltyCard.balanceType); + formatBalanceCurrencyField(loyaltyCard.balanceType); } if(cardIdFieldView.getText().length() == 0) @@ -340,11 +460,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity storeFieldEdit.setText(importCard.store); noteFieldEdit.setText(importCard.note); expiryField.setTag(importCard.expiry); - if (importCard.expiry != null) { - expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(importCard.expiry)); - } else { - expiryField.setText(R.string.never); - } + formatExpiryField(importCard.expiry); + balanceField.setTag(importCard.balanceType); + formatBalanceCurrencyField(importCard.balanceType); cardIdFieldView.setText(importCard.cardId); barcodeTypeField.setText(importCard.barcodeType); headingColorValue = importCard.headerColor; @@ -354,6 +472,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity setTitle(R.string.addCardTitle); expiryField.setTag(null); expiryField.setText(getString(R.string.never)); + balanceField.setTag(null); + balanceField.setText(String.format("%f", new BigDecimal("0.0"))); + balanceCurrencyField.setTag(null); + balanceCurrencyField.setText(getString(R.string.points)); hideBarcode(); } @@ -456,6 +578,22 @@ public class LoyaltyCardEditActivity extends AppCompatActivity generateIcon(storeFieldEdit.getText().toString()); } + private void formatExpiryField(Date expiry) { + if (expiry == null) { + expiryField.setText(getString(R.string.never)); + } else { + expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(expiry)); + } + } + + private void formatBalanceCurrencyField(Currency balanceType) { + if (balanceType == null) { + balanceCurrencyField.setText(getString(R.string.points)); + } else { + balanceCurrencyField.setText(balanceType.getSymbol()); + } + } + @Override public void onBackPressed() { askBeforeQuitIfChanged(); @@ -584,12 +722,13 @@ public class LoyaltyCardEditActivity extends AppCompatActivity } } - private void doSave() { String store = storeFieldEdit.getText().toString(); String note = noteFieldEdit.getText().toString(); Date expiry = (Date) expiryField.getTag(); + BigDecimal balance = (BigDecimal) balanceField.getTag(); + String balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()).getCurrencyCode() : null; String cardId = cardIdFieldView.getText().toString(); String barcodeType = barcodeTypeField.getText().toString(); @@ -612,6 +751,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity return; } + if(!validBalance) + { + Snackbar.make(balanceField, getString(R.string.parsingBalanceFailed, balanceField.getText().toString()), Snackbar.LENGTH_LONG).show(); + return; + } + List selectedGroups = new ArrayList<>(); for (Integer chipId : groupsChips.getCheckedChipIds()) { @@ -621,12 +766,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity if(updateLoyaltyCard) { //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity) - db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, cardId, barcodeType, headingColorValue); + db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue); Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId); } else { - loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, cardId, barcodeType, headingColorValue, 0); + loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue, 0); } db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 3d103c5f5..c1e7ae719 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -39,6 +39,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.zxing.BarcodeFormat; +import java.math.BigDecimal; import java.text.DateFormat; import java.util.List; @@ -54,6 +55,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity ImageView bottomSheetButton; TextView noteView; TextView groupsView; + TextView balanceView; TextView expiryView; TextView storeName; ImageButton maximizeButton; @@ -123,6 +125,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity bottomSheetButton = findViewById(R.id.bottomSheetButton); noteView = findViewById(R.id.noteView); groupsView = findViewById(R.id.groupsView); + balanceView = findViewById(R.id.balanceView); expiryView = findViewById(R.id.expiryView); storeName = findViewById(R.id.storeName); maximizeButton = findViewById(R.id.maximizeButton); @@ -287,6 +290,15 @@ public class LoyaltyCardViewActivity extends AppCompatActivity groupsView.setVisibility(View.GONE); } + if(!loyaltyCard.balance.equals(new BigDecimal(0))) { + balanceView.setVisibility(View.VISIBLE); + balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(loyaltyCard.balance, loyaltyCard.balanceType))); + } + else + { + balanceView.setVisibility(View.GONE); + } + if(loyaltyCard.expiry != null) { expiryView.setVisibility(View.VISIBLE); @@ -523,7 +535,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity private void makeBottomSheetVisibleIfUseful() { - if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) { + if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || balanceView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) { bottomSheet.setVisibility(View.VISIBLE); } else diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 3d0ed569f..49335ec29 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -6,9 +6,14 @@ import android.content.Intent; import android.graphics.Color; import android.util.Log; +import java.math.BigDecimal; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.Calendar; +import java.util.Currency; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; import androidx.core.graphics.ColorUtils; @@ -77,4 +82,41 @@ public class Utils { return expiryDate.before(date.getTime()); } + + static public String formatBalance(BigDecimal value, Currency currency) { + NumberFormat numberFormat = NumberFormat.getInstance(); + + if (currency == null) { + numberFormat.setMaximumFractionDigits(0); + return numberFormat.format(value); + } + + NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); + currencyFormat.setCurrency(currency); + + return currencyFormat.format(value); + } + + static public String formatBalanceWithoutCurrencySymbol(BigDecimal value, Currency currency) { + NumberFormat numberFormat = NumberFormat.getInstance(); + + if (currency == null) { + numberFormat.setMaximumFractionDigits(0); + return numberFormat.format(value); + } + + numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits()); + numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits()); + + return numberFormat.format(value); + } + + static public BigDecimal parseCurrencyInUserLocale(String value) throws ParseException, NumberFormatException { + // BigDecimal only likes to parse in US locale + // So we have to translate whatever the input was to US locale + NumberFormat numberInputFormat = NumberFormat.getNumberInstance(); + NumberFormat numberToBigDecimalFormat = NumberFormat.getNumberInstance(Locale.US); + + return new BigDecimal(numberToBigDecimalFormat.format(numberInputFormat.parse(value))); + } } diff --git a/app/src/main/res/layout/loyalty_card_edit_activity.xml b/app/src/main/res/layout/loyalty_card_edit_activity.xml index 7e02fb469..ae421711c 100644 --- a/app/src/main/res/layout/loyalty_card_edit_activity.xml +++ b/app/src/main/res/layout/loyalty_card_edit_activity.xml @@ -136,6 +136,53 @@ android:textSize="@dimen/inputSize" /> + + + + + + + + + + + + + + + + + + + - + android:hint="@string/cardId" + android:labelFor="@+id/cardIdView"> + + + + Groups: %s Expires: %s Expired: %s + Balance: %s Card Barcode @@ -143,4 +144,10 @@ Choose expiry date Move the barcode to the top of the screen Center the barcode on the screen + + Balance + Currency + Points + + %s does not seem to be a valid balance. From 1675ebf1dc3b1dd95a73681e24adf7f526215d61 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 15 Feb 2021 21:58:22 +0100 Subject: [PATCH 02/28] Set hasChanged when balance was edited --- .../main/java/protect/card_locker/LoyaltyCardEditActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 61202eb4f..6148b4d01 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -214,6 +214,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity @Override public void onTextChanged(CharSequence s, int start, int before, int count) { + hasChanged = true; + try { balanceField.setTag(Utils.parseCurrencyInUserLocale(s.toString())); validBalance = true; From 4fbedbc30c7faaaf65943f00e07d118baf1ede83 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 15 Feb 2021 22:02:47 +0100 Subject: [PATCH 03/28] Fix parsing big numbers --- app/src/main/java/protect/card_locker/Utils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 49335ec29..6013e4619 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -117,6 +117,9 @@ public class Utils { NumberFormat numberInputFormat = NumberFormat.getNumberInstance(); NumberFormat numberToBigDecimalFormat = NumberFormat.getNumberInstance(Locale.US); + // BigDecimal won't understand values like 1,000 instead of 1000 + numberToBigDecimalFormat.setGroupingUsed(false); + return new BigDecimal(numberToBigDecimalFormat.format(numberInputFormat.parse(value))); } } From 1dc30ebaad277875e6a271e566bfa358b13b06a5 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 15 Feb 2021 22:30:45 +0100 Subject: [PATCH 04/28] Reformat balance after focus loss --- .../card_locker/LoyaltyCardEditActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 6148b4d01..f94a9ca1f 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -208,6 +208,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity } }); + balanceField.setOnFocusChangeListener((v, hasFocus) -> { + if (!hasFocus) { + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), (Currency) balanceCurrencyField.getTag())); + } + }); + balanceField.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -217,8 +223,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity hasChanged = true; try { - balanceField.setTag(Utils.parseCurrencyInUserLocale(s.toString())); + BigDecimal balance = Utils.parseCurrencyInUserLocale(s.toString()); validBalance = true; + + balanceField.setTag(balance); } catch (ParseException | NumberFormatException e) { validBalance = false; e.printStackTrace(); @@ -230,12 +238,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity }); balanceCurrencyField.addTextChangedListener(new TextWatcher() { - CharSequence lastValue; - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - lastValue = s; - } + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { From 4a568d02d38b6ad4ebd27519f020593e0e154fbc Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 16 Feb 2021 22:22:51 +0100 Subject: [PATCH 05/28] Various fixes and consistency improvements --- .../card_locker/CsvDatabaseImporter.java | 9 ++++++-- .../java/protect/card_locker/DBHelper.java | 15 ++++++------- .../protect/card_locker/ImportURIHelper.java | 21 ++++++++++++------- .../java/protect/card_locker/LoyaltyCard.java | 16 +++++++------- .../card_locker/LoyaltyCardEditActivity.java | 10 ++++----- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java index c7aae8ef2..a4105002c 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; import java.math.BigDecimal; +import java.util.Currency; import java.util.Date; import java.util.List; @@ -303,16 +304,20 @@ public class CsvDatabaseImporter implements DatabaseImporter } catch (NullPointerException | FormatException e) { } BigDecimal balance; - String balanceType = null; try { balance = new BigDecimal(extractString(DBHelper.LoyaltyCardDbIds.BALANCE, record, null)); - balanceType = extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, null); } catch (FormatException _e ) { // These fields did not exist in versions 1.8.1 and before // We catch this exception so we can still import old backups balance = new BigDecimal("0.0"); } + Currency balanceType = null; + String unparsedBalanceType = extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, ""); + if(!unparsedBalanceType.isEmpty()) { + balanceType = Currency.getInstance(unparsedBalanceType); + } + String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, ""); if(cardId.isEmpty()) { diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 402585ee2..d25de0cb3 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Color; import java.math.BigDecimal; +import java.util.Currency; import java.util.Date; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ public class DBHelper extends SQLiteOpenHelper public static final String STORE = "store"; public static final String EXPIRY = "expiry"; public static final String BALANCE = "balance"; - public static final String BALANCE_TYPE = "balance_type"; + public static final String BALANCE_TYPE = "balancetype"; public static final String NOTE = "note"; public static final String HEADER_COLOR = "headercolor"; public static final String HEADER_TEXT_COLOR = "headertextcolor"; @@ -145,7 +146,7 @@ public class DBHelper extends SQLiteOpenHelper } public long insertLoyaltyCard(final String store, final String note, final Date expiry, - final BigDecimal balance, final String balanceType, + final BigDecimal balance, final Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -155,7 +156,7 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); - contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -167,7 +168,7 @@ public class DBHelper extends SQLiteOpenHelper public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store, final String note, final Date expiry, final BigDecimal balance, - final String balanceType, final String cardId, + final Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -177,7 +178,7 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); - contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -189,7 +190,7 @@ public class DBHelper extends SQLiteOpenHelper public boolean updateLoyaltyCard(final int id, final String store, final String note, final Date expiry, final BigDecimal balance, - final String balanceType, final String cardId, + final Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor) { SQLiteDatabase db = getWritableDatabase(); @@ -198,7 +199,7 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null); contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString()); - contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType); + contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); diff --git a/app/src/main/java/protect/card_locker/ImportURIHelper.java b/app/src/main/java/protect/card_locker/ImportURIHelper.java index 9e276e630..776f44832 100644 --- a/app/src/main/java/protect/card_locker/ImportURIHelper.java +++ b/app/src/main/java/protect/card_locker/ImportURIHelper.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.net.Uri; import java.io.InvalidObjectException; import java.math.BigDecimal; +import java.util.Currency; import java.util.Date; public class ImportURIHelper { @@ -12,7 +13,7 @@ public class ImportURIHelper { private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE; private static final String EXPIRY = DBHelper.LoyaltyCardDbIds.EXPIRY; private static final String BALANCE = DBHelper.LoyaltyCardDbIds.BALANCE; - private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE; + private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BALANCE_TYPE; private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID; private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE; @@ -47,7 +48,7 @@ public class ImportURIHelper { // These values are allowed to be null Date expiry = null; BigDecimal balance = new BigDecimal("0.0"); - String balanceType = null; + Currency balanceType = null; Integer headerColor = null; Integer headerTextColor = null; @@ -57,11 +58,6 @@ public class ImportURIHelper { String barcodeType = uri.getQueryParameter(BARCODE_TYPE); if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI"); - String unparsedExpiry = uri.getQueryParameter(EXPIRY); - if(unparsedExpiry != null && !unparsedExpiry.equals("")) - { - expiry = new Date(Long.parseLong(unparsedExpiry)); - } String unparsedBalance = uri.getQueryParameter(BALANCE); if(unparsedBalance != null && !unparsedBalance.equals("")) { @@ -70,7 +66,12 @@ public class ImportURIHelper { String unparsedBalanceType = uri.getQueryParameter(BALANCE_TYPE); if (unparsedBalanceType != null && !unparsedBalanceType.equals("")) { - balanceType = unparsedBalanceType; + balanceType = Currency.getInstance(unparsedBalanceType); + } + String unparsedExpiry = uri.getQueryParameter(EXPIRY); + if(unparsedExpiry != null && !unparsedExpiry.equals("")) + { + expiry = new Date(Long.parseLong(unparsedExpiry)); } String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR); @@ -93,6 +94,10 @@ public class ImportURIHelper { uriBuilder.path(path); uriBuilder.appendQueryParameter(STORE, loyaltyCard.store); uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note); + uriBuilder.appendQueryParameter(BALANCE, loyaltyCard.balance.toString()); + if (loyaltyCard.balanceType != null) { + uriBuilder.appendQueryParameter(BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode()); + } if (loyaltyCard.expiry != null) { uriBuilder.appendQueryParameter(EXPIRY, String.valueOf(loyaltyCard.expiry.getTime())); } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCard.java b/app/src/main/java/protect/card_locker/LoyaltyCard.java index 39e22f05a..64461a0e1 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCard.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCard.java @@ -28,7 +28,7 @@ public class LoyaltyCard public final int starStatus; public LoyaltyCard(final int id, final String store, final String note, final Date expiry, - final BigDecimal balance, final String balanceType, final String cardId, + final BigDecimal balance, final Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final Integer headerTextColor, final int starStatus) { @@ -37,11 +37,7 @@ public class LoyaltyCard this.note = note; this.expiry = expiry; this.balance = balance; - if (balanceType != null) { - this.balanceType = Currency.getInstance(balanceType); - } else { - this.balanceType = null; - } + this.balanceType = balanceType; this.cardId = cardId; this.barcodeType = barcodeType; this.headerColor = headerColor; @@ -56,18 +52,24 @@ public class LoyaltyCard String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)); long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)); BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE))); - String balanceType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)); String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)); String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)); int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)); + int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE); int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR); int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR); + Currency balanceType = null; Date expiry = null; Integer headerColor = null; Integer headerTextColor = null; + if (cursor.isNull(balanceTypeColumn) == false) + { + balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn)); + } + if(expiryLong > 0) { expiry = new Date(expiryLong); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index f94a9ca1f..2d5970b55 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -467,7 +467,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity noteFieldEdit.setText(importCard.note); expiryField.setTag(importCard.expiry); formatExpiryField(importCard.expiry); - balanceField.setTag(importCard.balanceType); + balanceField.setTag(importCard.balance); + balanceCurrencyField.setTag(importCard.balanceType); formatBalanceCurrencyField(importCard.balanceType); cardIdFieldView.setText(importCard.cardId); barcodeTypeField.setText(importCard.barcodeType); @@ -478,10 +479,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity setTitle(R.string.addCardTitle); expiryField.setTag(null); expiryField.setText(getString(R.string.never)); - balanceField.setTag(null); - balanceField.setText(String.format("%f", new BigDecimal("0.0"))); + balanceField.setTag(new BigDecimal("0.0")); balanceCurrencyField.setTag(null); - balanceCurrencyField.setText(getString(R.string.points)); + formatBalanceCurrencyField(null); hideBarcode(); } @@ -734,7 +734,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity String note = noteFieldEdit.getText().toString(); Date expiry = (Date) expiryField.getTag(); BigDecimal balance = (BigDecimal) balanceField.getTag(); - String balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()).getCurrencyCode() : null; + Currency balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()) : null; String cardId = cardIdFieldView.getText().toString(); String barcodeType = barcodeTypeField.getText().toString(); From 0baf1ba348b58950dc7c3bf48def286cdb11e035 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sat, 20 Feb 2021 18:15:58 +0100 Subject: [PATCH 06/28] Fix tests --- .../protect/card_locker/DatabaseTest.java | 23 ++++++------- .../protect/card_locker/ImportExportTest.java | 15 +++++---- .../protect/card_locker/ImportURITest.java | 5 +-- .../LoyaltyCardCursorAdapterTest.java | 13 ++++---- .../LoyaltyCardViewActivityTest.java | 33 ++++++++++--------- .../protect/card_locker/MainActivityTest.java | 15 +++++---- 6 files changed, 55 insertions(+), 49 deletions(-) diff --git a/app/src/test/java/protect/card_locker/DatabaseTest.java b/app/src/test/java/protect/card_locker/DatabaseTest.java index c8db55818..7fa31444a 100644 --- a/app/src/test/java/protect/card_locker/DatabaseTest.java +++ b/app/src/test/java/protect/card_locker/DatabaseTest.java @@ -15,6 +15,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -43,7 +44,7 @@ public class DatabaseTest public void addRemoveOneGiftCard() { assertEquals(0, db.getLoyaltyCardCount()); - long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); + long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -66,12 +67,12 @@ public class DatabaseTest @Test public void updateGiftCard() { - long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); + long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); - result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR); + result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -88,7 +89,7 @@ public class DatabaseTest @Test public void updateGiftCardOnlyStar() { - long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); + long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -112,7 +113,7 @@ public class DatabaseTest { assertEquals(0, db.getLoyaltyCardCount()); - boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1", + boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR); assertEquals(false, result); assertEquals(0, db.getLoyaltyCardCount()); @@ -121,7 +122,7 @@ public class DatabaseTest @Test public void emptyGiftCardValues() { - long id = db.insertLoyaltyCard("", "", null, "", "", null, 0); + long id = db.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", "", null, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -144,7 +145,7 @@ public class DatabaseTest // that they are sorted for(int index = CARDS_TO_ADD-1; index >= 0; index--) { - long id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index, + long id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index, BarcodeFormat.UPC_A.toString(), index, 0); boolean result = (id != -1); assertTrue(result); @@ -187,12 +188,12 @@ public class DatabaseTest for(int index = CARDS_TO_ADD-1; index >= 0; index--) { if (index == CARDS_TO_ADD-1) { - id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index, + id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index, BarcodeFormat.UPC_A.toString(), index, 1); } else { - id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index, + id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index, BarcodeFormat.UPC_A.toString(), index, 0); } boolean result = (id != -1); @@ -288,7 +289,7 @@ public class DatabaseTest { // Create card assertEquals(0, db.getLoyaltyCardCount()); - long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); + long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -405,7 +406,7 @@ public class DatabaseTest { // Create card assertEquals(0, db.getLoyaltyCardCount()); - long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); + long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0); boolean result = (id != -1); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index ddf3b6c3f..831644377 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -24,6 +24,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; @@ -72,7 +73,7 @@ public class ImportExportTest { String storeName = String.format("store, \"%4d", index); String note = String.format("note, \"%4d", index); - long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 0); + long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0); boolean result = (id != -1); assertTrue(result); } @@ -88,7 +89,7 @@ public class ImportExportTest { String storeName = String.format("store, \"%4d", index); String note = String.format("note, \"%4d", index); - long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 1); + long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 1); boolean result = (id != -1); assertTrue(result); } @@ -97,7 +98,7 @@ public class ImportExportTest String storeName = String.format("store, \"%4d", index); String note = String.format("note, \"%4d", index); //if index is even - long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 0); + long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0); boolean result = (id != -1); assertTrue(result); } @@ -106,21 +107,21 @@ public class ImportExportTest private void addLoyaltyCardsWithExpiryNeverPastTodayFuture() { - long id = db.insertLoyaltyCard("No Expiry", "", null, BARCODE_DATA, BARCODE_TYPE, 0, 0); + long id = db.insertLoyaltyCard("No Expiry", "", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); boolean result = (id != -1); assertTrue(result); - id = db.insertLoyaltyCard("Past", "", new Date((long) 1), BARCODE_DATA, BARCODE_TYPE, 0, 0); + id = db.insertLoyaltyCard("Past", "", new Date((long) 1), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); - id = db.insertLoyaltyCard("Today", "", new Date(), BARCODE_DATA, BARCODE_TYPE, 0, 0); + id = db.insertLoyaltyCard("Today", "", new Date(), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); // This will break after 19 January 2038 // If someone is still maintaining this code base by then: I love you - id = db.insertLoyaltyCard("Future", "", new Date(2147483648L), BARCODE_DATA, BARCODE_TYPE, 0, 0); + id = db.insertLoyaltyCard("Future", "", new Date(2147483648L), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); diff --git a/app/src/test/java/protect/card_locker/ImportURITest.java b/app/src/test/java/protect/card_locker/ImportURITest.java index 02bd27462..56b8153e8 100644 --- a/app/src/test/java/protect/card_locker/ImportURITest.java +++ b/app/src/test/java/protect/card_locker/ImportURITest.java @@ -11,6 +11,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.io.InvalidObjectException; +import java.math.BigDecimal; import java.util.Date; import static org.junit.Assert.assertEquals; @@ -38,7 +39,7 @@ public class ImportURITest { // Generate card Date date = new Date(); - db.insertLoyaltyCard("store", "note", date, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1); + db.insertLoyaltyCard("store", "note", date, new BigDecimal("0"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1); // Get card LoyaltyCard card = db.getLoyaltyCard(1); @@ -64,7 +65,7 @@ public class ImportURITest { public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException { // Generate card - db.insertLoyaltyCard("store", "note", null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0); // Get card LoyaltyCard card = db.getLoyaltyCard(1); diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java index 3d8c25e2a..65d65e6fa 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -21,6 +21,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import java.math.BigDecimal; import java.text.DateFormat; import java.util.Date; @@ -102,7 +103,7 @@ public class LoyaltyCardCursorAdapterTest @Test public void TestCursorAdapterEmptyNote() { - db.insertLoyaltyCard("store", "", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); LoyaltyCard card = db.getLoyaltyCard(1); Cursor cursor = db.getLoyaltyCardCursor(); @@ -118,7 +119,7 @@ public class LoyaltyCardCursorAdapterTest @Test public void TestCursorAdapterWithNote() { - db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); LoyaltyCard card = db.getLoyaltyCard(1); Cursor cursor = db.getLoyaltyCardCursor(); @@ -138,7 +139,7 @@ public class LoyaltyCardCursorAdapterTest Date expiryDate = new Date(); String dateString = context.getString(R.string.expiryStateSentence, DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate)); - db.insertLoyaltyCard("store", "note", expiryDate, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", expiryDate, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); LoyaltyCard card = db.getLoyaltyCard(1); Cursor cursor = db.getLoyaltyCardCursor(); @@ -159,9 +160,9 @@ public class LoyaltyCardCursorAdapterTest @Test public void TestCursorAdapterStarring() { - db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); - db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); - db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); + db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); + db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); Cursor cursor = db.getLoyaltyCardCursor(); cursor.moveToFirst(); diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index d203f0393..6c0430c93 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -39,6 +39,7 @@ import com.google.android.material.textfield.TextInputLayout; import com.google.zxing.BarcodeFormat; import com.google.zxing.client.android.Intents; import java.io.IOException; +import java.math.BigDecimal; import java.text.DateFormat; import java.util.Date; import org.junit.Before; @@ -440,7 +441,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -458,7 +459,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -476,7 +477,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -499,7 +500,7 @@ public class LoyaltyCardViewActivityTest LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -537,7 +538,7 @@ public class LoyaltyCardViewActivityTest final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -570,7 +571,7 @@ public class LoyaltyCardViewActivityTest final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", new Date(), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", new Date(), new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -594,7 +595,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -642,7 +643,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -662,7 +663,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0); activityController.start(); activityController.visible(); @@ -682,7 +683,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0); activityController.start(); activityController.visible(); @@ -701,7 +702,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, "", Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, "", Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -721,7 +722,7 @@ public class LoyaltyCardViewActivityTest final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); @@ -750,7 +751,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); final int STORE_FONT_SIZE = 50; final int CARD_FONT_SIZE = 40; @@ -790,7 +791,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); settings.edit() @@ -827,7 +828,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity) activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0); activityController.start(); activityController.visible(); activityController.resume(); @@ -862,7 +863,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); diff --git a/app/src/test/java/protect/card_locker/MainActivityTest.java b/app/src/test/java/protect/card_locker/MainActivityTest.java index 5dcee2a93..34c35c06e 100644 --- a/app/src/test/java/protect/card_locker/MainActivityTest.java +++ b/app/src/test/java/protect/card_locker/MainActivityTest.java @@ -21,6 +21,7 @@ import org.robolectric.annotation.Config; import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.ShadowActivity; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -95,7 +96,7 @@ public class MainActivityTest assertEquals(0, list.getCount()); DBHelper db = new DBHelper(mainActivity); - db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); assertEquals(View.VISIBLE, helpText.getVisibility()); assertEquals(View.GONE, noMatchingCardsText.getVisibility()); @@ -131,10 +132,10 @@ public class MainActivityTest assertEquals(0, list.getCount()); DBHelper db = new DBHelper(mainActivity); - db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); - db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); - db.insertLoyaltyCard("storeD", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); - db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); + db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("storeD", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); + db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); assertEquals(View.VISIBLE, helpText.getVisibility()); assertEquals(View.GONE, noMatchingCardsText.getVisibility()); @@ -232,8 +233,8 @@ public class MainActivityTest TabLayout groupTabs = mainActivity.findViewById(R.id.groups); DBHelper db = new DBHelper(mainActivity); - db.insertLoyaltyCard("The First Store", "Initial note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); - db.insertLoyaltyCard("The Second Store", "Secondary note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("The First Store", "Initial note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("The Second Store", "Secondary note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); db.insertGroup("Group one"); List groups = new ArrayList<>(); From d4f62435ae615f70bafaf8d4ac7fdde9e8e5f11e Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sat, 20 Feb 2021 22:10:23 +0100 Subject: [PATCH 07/28] Add tests for balance --- .../card_locker/CsvDatabaseImporter.java | 2 +- .../java/protect/card_locker/DBHelper.java | 4 +- .../protect/card_locker/ImportURIHelper.java | 2 +- .../card_locker/LoyaltyCardCursorAdapter.java | 4 +- .../card_locker/LoyaltyCardEditActivity.java | 2 +- .../card_locker/LoyaltyCardViewActivity.java | 2 +- .../main/java/protect/card_locker/Utils.java | 4 +- app/src/main/res/values/strings.xml | 1 + .../protect/card_locker/DatabaseTest.java | 21 ++- .../protect/card_locker/ImportExportTest.java | 29 ++- .../protect/card_locker/ImportURITest.java | 12 +- .../LoyaltyCardCursorAdapterTest.java | 90 ++++++++- .../LoyaltyCardViewActivityTest.java | 177 +++++++++++++++--- 13 files changed, 300 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java index a4105002c..5ba74393e 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java @@ -309,7 +309,7 @@ public class CsvDatabaseImporter implements DatabaseImporter } catch (FormatException _e ) { // These fields did not exist in versions 1.8.1 and before // We catch this exception so we can still import old backups - balance = new BigDecimal("0.0"); + balance = new BigDecimal("0"); } Currency balanceType = null; diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index d25de0cb3..10a631eca 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -70,7 +70,7 @@ public class DBHelper extends SQLiteOpenHelper LoyaltyCardDbIds.STORE + " TEXT not null," + LoyaltyCardDbIds.NOTE + " TEXT not null," + LoyaltyCardDbIds.EXPIRY + " INTEGER," + - LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0.0'," + + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'," + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," + @@ -139,7 +139,7 @@ public class DBHelper extends SQLiteOpenHelper if(oldVersion < 8 && newVersion >= 8) { db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE - + " ADD COLUMN " + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0.0'"); + + " ADD COLUMN " + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'"); db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + " ADD COLUMN " + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT"); } diff --git a/app/src/main/java/protect/card_locker/ImportURIHelper.java b/app/src/main/java/protect/card_locker/ImportURIHelper.java index 776f44832..54add450f 100644 --- a/app/src/main/java/protect/card_locker/ImportURIHelper.java +++ b/app/src/main/java/protect/card_locker/ImportURIHelper.java @@ -47,7 +47,7 @@ public class ImportURIHelper { try { // These values are allowed to be null Date expiry = null; - BigDecimal balance = new BigDecimal("0.0"); + BigDecimal balance = new BigDecimal("0"); Currency balanceType = null; Integer headerColor = null; Integer headerTextColor = null; diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java index 4f069c5ed..e20a03769 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java @@ -65,9 +65,9 @@ class LoyaltyCardCursorAdapter extends CursorAdapter noteField.setVisibility(View.GONE); } - if(!loyaltyCard.balance.equals(new BigDecimal(0))) { + if(!loyaltyCard.balance.equals(new BigDecimal("0"))) { balanceField.setVisibility(View.VISIBLE); - balanceField.setText(context.getString(R.string.balanceSentence, Utils.formatBalance(loyaltyCard.balance, loyaltyCard.balanceType))); + balanceField.setText(context.getString(R.string.balanceSentence, Utils.formatBalance(context, loyaltyCard.balance, loyaltyCard.balanceType))); } else { diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 2d5970b55..dd7e7606b 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -479,7 +479,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity setTitle(R.string.addCardTitle); expiryField.setTag(null); expiryField.setText(getString(R.string.never)); - balanceField.setTag(new BigDecimal("0.0")); + balanceField.setTag(new BigDecimal("0")); balanceCurrencyField.setTag(null); formatBalanceCurrencyField(null); hideBarcode(); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index c1e7ae719..db505dbd0 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -292,7 +292,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity if(!loyaltyCard.balance.equals(new BigDecimal(0))) { balanceView.setVisibility(View.VISIBLE); - balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(loyaltyCard.balance, loyaltyCard.balanceType))); + balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType))); } else { diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 6013e4619..abbfb2edb 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -83,12 +83,12 @@ public class Utils { return expiryDate.before(date.getTime()); } - static public String formatBalance(BigDecimal value, Currency currency) { + static public String formatBalance(Context context, BigDecimal value, Currency currency) { NumberFormat numberFormat = NumberFormat.getInstance(); if (currency == null) { numberFormat.setMaximumFractionDigits(0); - return numberFormat.format(value); + return context.getString(R.string.balancePoints, numberFormat.format(value)); } NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc4c826b8..71260b998 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,6 +135,7 @@ Expires: %s Expired: %s Balance: %s + %s points Card Barcode diff --git a/app/src/test/java/protect/card_locker/DatabaseTest.java b/app/src/test/java/protect/card_locker/DatabaseTest.java index 7fa31444a..a1f6b11da 100644 --- a/app/src/test/java/protect/card_locker/DatabaseTest.java +++ b/app/src/test/java/protect/card_locker/DatabaseTest.java @@ -17,6 +17,7 @@ import org.robolectric.annotation.Config; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Currency; import java.util.List; import static org.junit.Assert.assertEquals; @@ -54,6 +55,8 @@ public class DatabaseTest assertEquals("store", loyaltyCard.store); assertEquals("note", loyaltyCard.note); assertEquals(null, loyaltyCard.expiry); + assertEquals(new BigDecimal("0"), loyaltyCard.balance); + assertEquals(null, loyaltyCard.balanceType); assertEquals("cardId", loyaltyCard.cardId); assertEquals(0, loyaltyCard.starStatus); assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType); @@ -72,7 +75,7 @@ public class DatabaseTest assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); - result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR); + result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -81,6 +84,8 @@ public class DatabaseTest assertEquals("store1", loyaltyCard.store); assertEquals("note1", loyaltyCard.note); assertEquals(null, loyaltyCard.expiry); + assertEquals(new BigDecimal("10.00"), loyaltyCard.balance); + assertEquals(Currency.getInstance("EUR"), loyaltyCard.balanceType); assertEquals("cardId1", loyaltyCard.cardId); assertEquals(0, loyaltyCard.starStatus); assertEquals(BarcodeFormat.AZTEC.toString(), loyaltyCard.barcodeType); @@ -103,6 +108,8 @@ public class DatabaseTest assertEquals("store", loyaltyCard.store); assertEquals("note", loyaltyCard.note); assertEquals(null, loyaltyCard.expiry); + assertEquals(new BigDecimal("0"), loyaltyCard.balance); + assertEquals(null, loyaltyCard.balanceType); assertEquals("cardId", loyaltyCard.cardId); assertEquals(1, loyaltyCard.starStatus); assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType); @@ -132,6 +139,8 @@ public class DatabaseTest assertEquals("", loyaltyCard.store); assertEquals("", loyaltyCard.note); assertEquals(null, loyaltyCard.expiry); + assertEquals(new BigDecimal("0"), loyaltyCard.balance); + assertEquals(null, loyaltyCard.balanceType); assertEquals("", loyaltyCard.cardId); assertEquals("", loyaltyCard.barcodeType); } @@ -165,6 +174,8 @@ public class DatabaseTest assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE))); assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE))); assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY))); + assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE))); + assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE))); assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID))); assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE))); assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS))); @@ -212,6 +223,8 @@ public class DatabaseTest assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE))); assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE))); assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY))); + assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE))); + assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE))); assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID))); assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE))); assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS))); @@ -223,7 +236,9 @@ public class DatabaseTest { assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE))); assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE))); - assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID))); + assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY))); + assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE))); + assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE))); assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID))); assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE))); assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS))); @@ -466,6 +481,8 @@ public class DatabaseTest LoyaltyCard card = db.getLoyaltyCard(newCardId); assertEquals("store", card.store); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("cardId", card.cardId); assertEquals(BarcodeFormat.UPC_A.toString(), card.barcodeType); assertEquals("", card.note); diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index 831644377..2baf9ee73 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -105,7 +105,8 @@ public class ImportExportTest assertEquals(cardsToAdd, db.getLoyaltyCardCount()); } - private void addLoyaltyCardsWithExpiryNeverPastTodayFuture() + @Test + public void addLoyaltyCardsWithExpiryNeverPastTodayFuture() { long id = db.insertLoyaltyCard("No Expiry", "", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); boolean result = (id != -1); @@ -161,6 +162,8 @@ public class ImportExportTest assertEquals(expectedStore, card.store); assertEquals(expectedNote, card.note); + assertEquals(new BigDecimal(String.valueOf(index)), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(index), card.headerColor); @@ -191,6 +194,8 @@ public class ImportExportTest assertEquals(expectedStore, card.store); assertEquals(expectedNote, card.note); + assertEquals(new BigDecimal(String.valueOf(index)), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(index), card.headerColor); @@ -209,6 +214,8 @@ public class ImportExportTest assertEquals(expectedStore, card.store); assertEquals(expectedNote, card.note); + assertEquals(new BigDecimal(String.valueOf(index)), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(index), card.headerColor); @@ -577,6 +584,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); @@ -613,6 +622,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); @@ -675,6 +686,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("", card.barcodeType); assertEquals(0, card.starStatus); @@ -711,6 +724,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("type", card.barcodeType); assertEquals(1, card.starStatus); @@ -747,6 +762,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); @@ -803,6 +820,8 @@ public class ImportExportTest assertEquals("store", card.store); assertEquals("note", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals("12345", card.cardId); assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); @@ -820,6 +839,8 @@ public class ImportExportTest assertEquals("Never", card.store); assertEquals("", card.note); assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(0), card.headerColor); @@ -830,6 +851,8 @@ public class ImportExportTest assertEquals("Past", card.store); assertEquals("", card.note); assertTrue(card.expiry.before(new Date())); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(0), card.headerColor); @@ -841,6 +864,8 @@ public class ImportExportTest assertEquals("", card.note); assertTrue(card.expiry.before(new Date(new Date().getTime()+86400))); assertTrue(card.expiry.after(new Date(new Date().getTime()-86400))); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(0), card.headerColor); @@ -851,6 +876,8 @@ public class ImportExportTest assertEquals("Future", card.store); assertEquals("", card.note); assertTrue(card.expiry.after(new Date(new Date().getTime()+86400))); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); assertEquals(BARCODE_DATA, card.cardId); assertEquals(BARCODE_TYPE, card.barcodeType); assertEquals(Integer.valueOf(0), card.headerColor); diff --git a/app/src/test/java/protect/card_locker/ImportURITest.java b/app/src/test/java/protect/card_locker/ImportURITest.java index 56b8153e8..2b29c47cb 100644 --- a/app/src/test/java/protect/card_locker/ImportURITest.java +++ b/app/src/test/java/protect/card_locker/ImportURITest.java @@ -12,6 +12,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.io.InvalidObjectException; import java.math.BigDecimal; +import java.util.Currency; import java.util.Date; import static org.junit.Assert.assertEquals; @@ -39,7 +40,7 @@ public class ImportURITest { // Generate card Date date = new Date(); - db.insertLoyaltyCard("store", "note", date, new BigDecimal("0"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1); + db.insertLoyaltyCard("store", "note", date, new BigDecimal("100"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1); // Get card LoyaltyCard card = db.getLoyaltyCard(1); @@ -56,6 +57,8 @@ public class ImportURITest { assertEquals(card.headerColor, parsedCard.headerColor); assertEquals(card.note, parsedCard.note); assertEquals(card.expiry, parsedCard.expiry); + assertEquals(card.balance, parsedCard.balance); + assertEquals(card.balanceType, parsedCard.balanceType); assertEquals(card.store, parsedCard.store); // No export of starStatus for single cards foreseen therefore 0 will be imported assertEquals(0, parsedCard.starStatus); @@ -65,7 +68,7 @@ public class ImportURITest { public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException { // Generate card - db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0); // Get card LoyaltyCard card = db.getLoyaltyCard(1); @@ -81,6 +84,8 @@ public class ImportURITest { assertEquals(card.cardId, parsedCard.cardId); assertEquals(card.note, parsedCard.note); assertEquals(card.expiry, parsedCard.expiry); + assertEquals(card.balance, parsedCard.balance); + assertEquals(card.balanceType, parsedCard.balanceType); assertEquals(card.store, parsedCard.store); assertNull(parsedCard.headerColor); assertNull(parsedCard.headerTextColor); @@ -126,5 +131,8 @@ public class ImportURITest { assertEquals("store", parsedCard.store); assertEquals(Integer.valueOf(-416706), parsedCard.headerColor); assertEquals(0, parsedCard.starStatus); + assertEquals(null, parsedCard.expiry); + assertEquals(new BigDecimal("0"), parsedCard.balance); + assertEquals(null, parsedCard.balanceType); } } diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java index 65d65e6fa..6132d1308 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -23,6 +23,7 @@ import org.robolectric.annotation.Config; import java.math.BigDecimal; import java.text.DateFormat; +import java.util.Currency; import java.util.Date; import static org.junit.Assert.assertEquals; @@ -61,11 +62,12 @@ public class LoyaltyCardCursorAdapterTest return view; } - private void checkView(final View view, final String store, final String note, final String expiry, boolean checkFontSizes) + private void checkView(final View view, final String store, final String note, final String expiry, final String balance, boolean checkFontSizes) { final TextView storeField = view.findViewById(R.id.store); final TextView noteField = view.findViewById(R.id.note); final TextView expiryField = view.findViewById(R.id.expiry); + final TextView balanceField = view.findViewById(R.id.balance); if(checkFontSizes) { @@ -78,7 +80,7 @@ public class LoyaltyCardCursorAdapterTest } assertEquals(store, storeField.getText().toString()); - if(note.isEmpty() == false) + if(!note.isEmpty()) { assertEquals(View.VISIBLE, noteField.getVisibility()); assertEquals(note, noteField.getText().toString()); @@ -88,7 +90,7 @@ public class LoyaltyCardCursorAdapterTest assertEquals(View.GONE, noteField.getVisibility()); } - if(expiry.isEmpty() == false) + if(!expiry.isEmpty()) { assertEquals(View.VISIBLE, expiryField.getVisibility()); assertEquals(expiry, expiryField.getText().toString()); @@ -97,6 +99,16 @@ public class LoyaltyCardCursorAdapterTest { assertEquals(View.GONE, expiryField.getVisibility()); } + + if(!balance.isEmpty()) + { + assertEquals(View.VISIBLE, balanceField.getVisibility()); + assertEquals(balance, balanceField.getText().toString()); + } + else + { + assertEquals(View.GONE, balanceField.getVisibility()); + } } @@ -111,7 +123,7 @@ public class LoyaltyCardCursorAdapterTest View view = createView(cursor); - checkView(view, card.store, card.note, "", false); + checkView(view, card.store, card.note, "", "",false); cursor.close(); } @@ -127,7 +139,7 @@ public class LoyaltyCardCursorAdapterTest View view = createView(cursor); - checkView(view, card.store, card.note, "", false); + checkView(view, card.store, card.note, "", "",false); cursor.close(); } @@ -148,11 +160,11 @@ public class LoyaltyCardCursorAdapterTest setFontSizes(1, 2); View view = createView(cursor); - checkView(view, card.store, card.note, dateString, true); + checkView(view, card.store, card.note, dateString, "", true); setFontSizes(30, 31); view = createView(cursor); - checkView(view, card.store, card.note, dateString, true); + checkView(view, card.store, card.note, dateString, "",true); cursor.close(); } @@ -182,4 +194,68 @@ public class LoyaltyCardCursorAdapterTest cursor.close(); } + + @Test + public void TestCursorAdapter0Points() + { + db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + LoyaltyCard card = db.getLoyaltyCard(1); + + Cursor cursor = db.getLoyaltyCardCursor(); + cursor.moveToFirst(); + + View view = createView(cursor); + + checkView(view, card.store, card.note, "", "",false); + + cursor.close(); + } + + @Test + public void TestCursorAdapter0EUR() + { + db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), Currency.getInstance("EUR"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + LoyaltyCard card = db.getLoyaltyCard(1); + + Cursor cursor = db.getLoyaltyCardCursor(); + cursor.moveToFirst(); + + View view = createView(cursor); + + checkView(view, card.store, card.note, "", "",false); + + cursor.close(); + } + + @Test + public void TestCursorAdapter100Points() + { + db.insertLoyaltyCard("store", "note", null, new BigDecimal("100"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + LoyaltyCard card = db.getLoyaltyCard(1); + + Cursor cursor = db.getLoyaltyCardCursor(); + cursor.moveToFirst(); + + View view = createView(cursor); + + checkView(view, card.store, card.note, "", "Balance: 100 points",false); + + cursor.close(); + } + + @Test + public void TestCursorAdapter10Eur() + { + db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + LoyaltyCard card = db.getLoyaltyCard(1); + + Cursor cursor = db.getLoyaltyCardCursor(); + cursor.moveToFirst(); + + View view = createView(cursor); + + checkView(view, card.store, card.note, "", "Balance: €10.00",false); + + cursor.close(); + } } diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index 6c0430c93..6a2b7f4b5 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -41,6 +41,7 @@ import com.google.zxing.client.android.Intents; import java.io.IOException; import java.math.BigDecimal; import java.text.DateFormat; +import java.util.Currency; import java.util.Date; import org.junit.Before; import org.junit.Test; @@ -109,6 +110,8 @@ public class LoyaltyCardViewActivityTest private void saveLoyaltyCardWithArguments(final Activity activity, final String store, final String note, final Date expiry, + final BigDecimal balance, + final Currency balanceType, final String cardId, final String barcodeType, boolean creatingNewCard) @@ -126,12 +129,20 @@ public class LoyaltyCardViewActivityTest final EditText storeField = activity.findViewById(R.id.storeNameEdit); final EditText noteField = activity.findViewById(R.id.noteEdit); final TextInputLayout expiryView = activity.findViewById(R.id.expiryView); + final EditText balanceView = activity.findViewById(R.id.balanceField); + final EditText balanceCurrencyField = activity.findViewById(R.id.balanceCurrencyField); final TextView cardIdField = activity.findViewById(R.id.cardIdView); final TextView barcodeTypeField = activity.findViewById(R.id.barcodeTypeField); storeField.setText(store); noteField.setText(note); expiryView.setTag(expiry); + if (balance != null) { + balanceView.setText(balance.toPlainString()); + } + if (balanceType != null) { + balanceCurrencyField.setText(balanceType.getSymbol()); + } cardIdField.setText(cardId); barcodeTypeField.setText(barcodeType); @@ -144,6 +155,13 @@ public class LoyaltyCardViewActivityTest LoyaltyCard card = db.getLoyaltyCard(1); assertEquals(store, card.store); assertEquals(note, card.note); + assertEquals(expiry, card.expiry); + if (balance != null) { + assertEquals(balance, card.balance); + } else { + assertEquals(new BigDecimal("0"), card.balance); + } + assertEquals(balanceType, card.balanceType); assertEquals(cardId, card.cardId); // The special "No barcode" string shouldn't actually be written to the loyalty card @@ -254,7 +272,9 @@ public class LoyaltyCardViewActivityTest } private void checkAllFields(final Activity activity, ViewMode mode, - final String store, final String note, final String expiryString, final String cardId, final String barcodeType) + final String store, final String note, final String expiryString, + final String balanceString, final String balanceTypeString, + final String cardId, final String barcodeType) { if(mode == ViewMode.VIEW_CARD) { @@ -267,6 +287,8 @@ public class LoyaltyCardViewActivityTest checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store); checkFieldProperties(activity, R.id.noteEdit, editVisibility, note); checkFieldProperties(activity, R.id.expiryView, editVisibility, expiryString); + checkFieldProperties(activity, R.id.balanceField, editVisibility, balanceString); + checkFieldProperties(activity, R.id.balanceCurrencyField, editVisibility, balanceTypeString); checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId); checkFieldProperties(activity, R.id.barcodeTypeField, View.VISIBLE, barcodeType); checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null); @@ -284,7 +306,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "", ""); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "0", context.getString(R.string.points), "", ""); } @Test @@ -302,7 +324,6 @@ public class LoyaltyCardViewActivityTest final EditText storeField = activity.findViewById(R.id.storeNameEdit); final EditText noteField = activity.findViewById(R.id.noteEdit); - final TextView cardIdField = activity.findViewById(R.id.cardIdView); activity.findViewById(R.id.fabSave).performClick(); assertEquals(0, db.getLoyaltyCardCount()); @@ -346,15 +367,17 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", ""); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", ""); // Complete barcode capture successfully captureBarcodeWithResult(activity, true); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); + + shadowOf(getMainLooper()).idle(); // Save and check the loyalty card - saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, true); + saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, BARCODE_TYPE, true); } @Test @@ -368,12 +391,12 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", ""); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", ""); // Complete barcode capture in failure captureBarcodeWithResult(activity, false); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", ""); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", ""); } @Test @@ -387,12 +410,12 @@ public class LoyaltyCardViewActivityTest LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", ""); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", ""); // Complete barcode capture successfully captureBarcodeWithResult(activity, true); - checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); // Cancel the loyalty card creation assertEquals(false, activity.isFinishing()); @@ -439,6 +462,7 @@ public class LoyaltyCardViewActivityTest { ActivityController activityController = createActivityWithLoyaltyCard(true); Activity activity = (Activity)activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); @@ -447,7 +471,7 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); db.close(); } @@ -457,6 +481,7 @@ public class LoyaltyCardViewActivityTest { ActivityController activityController = createActivityWithLoyaltyCard(false); Activity activity = (Activity)activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0); @@ -465,7 +490,7 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); db.close(); } @@ -475,6 +500,7 @@ public class LoyaltyCardViewActivityTest { ActivityController activityController = createActivityWithLoyaltyCard(true); Activity activity = (Activity)activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); @@ -483,12 +509,12 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); // Complete barcode capture successfully captureBarcodeWithResult(activity, true); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); db.close(); } @@ -498,6 +524,7 @@ public class LoyaltyCardViewActivityTest { ActivityController activityController = createActivityWithLoyaltyCard(true); LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); @@ -506,12 +533,12 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); // Complete barcode capture successfully captureBarcodeWithResult(activity, true); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); // Cancel the loyalty card creation assertEquals(false, activity.isFinishing()); @@ -544,7 +571,7 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); // Set date to today MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField); @@ -558,7 +585,7 @@ public class LoyaltyCardViewActivityTest shadowOf(getMainLooper()).idle(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); db.close(); } @@ -577,13 +604,105 @@ public class LoyaltyCardViewActivityTest activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); // Set date to never MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField); expiryField.setText(expiryField.getAdapter().getItem(0).toString(), false); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + + db.close(); + } + + @Test + public void startWithLoyaltyCardNoBalanceSetBalance() throws IOException + { + ActivityController activityController = createActivityWithLoyaltyCard(true); + Activity activity = (Activity)activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); + DBHelper db = new DBHelper(activity); + + db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + + activityController.start(); + activityController.visible(); + activityController.resume(); + + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + + // Set balance to 10 points + EditText balanceField = activity.findViewById(R.id.balanceField); + balanceField.setText("10"); + + shadowOf(getMainLooper()).idle(); + + // Change points to EUR + MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField); + balanceTypeField.setText("€", false); + + shadowOf(getMainLooper()).idle(); + + // Ensure the balance is reformatted for EUR when focus is cleared + shadowOf(getMainLooper()).idle(); + balanceField.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + assertEquals("10.00", balanceField.getText().toString()); + + shadowOf(getMainLooper()).idle(); + + DatePickerDialog datePickerDialog = (DatePickerDialog) (ShadowDialog.getLatestDialog()); + assertNotNull(datePickerDialog); + datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).performClick(); + + shadowOf(getMainLooper()).idle(); + + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "10.00", "€", EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + + db.close(); + } + }); + balanceField.clearFocus(); + } + + @Test + public void startWithLoyaltyCardBalanceSetNoBalance() throws IOException + { + ActivityController activityController = createActivityWithLoyaltyCard(true); + Activity activity = (Activity)activityController.get(); + final Context context = ApplicationProvider.getApplicationContext(); + DBHelper db = new DBHelper(activity); + + db.insertLoyaltyCard("store", "note", new Date(), new BigDecimal("10.00"), Currency.getInstance("EUR"), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + + activityController.start(); + activityController.visible(); + activityController.resume(); + + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "10.00", "€", EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + + shadowOf(getMainLooper()).idle(); + + // Change EUR to WON + MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField); + balanceTypeField.setText("â‚©", false); + + shadowOf(getMainLooper()).idle(); + + // Ensure the balance is reformatted for WON when focus is cleared + EditText balanceField = activity.findViewById(R.id.balanceField); + balanceField.clearFocus(); + assertEquals("10", balanceField.getText().toString()); + + shadowOf(getMainLooper()).idle(); + + // Set the balance to 0 + balanceField.setText("0"); + + shadowOf(getMainLooper()).idle(); + + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", "â‚©", EAN_BARCODE_DATA, EAN_BARCODE_TYPE); db.close(); } @@ -690,7 +809,7 @@ public class LoyaltyCardViewActivityTest activityController.resume(); // Save and check the loyalty card - saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, false); + saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, false); db.close(); } @@ -709,7 +828,7 @@ public class LoyaltyCardViewActivityTest activityController.resume(); // Save and check the loyalty card - saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); + saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); db.close(); } @@ -729,17 +848,17 @@ public class LoyaltyCardViewActivityTest activityController.resume(); // First check if the card is as expected - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE); // Complete empty barcode selection successfully selectBarcodeWithResult(activity, BARCODE_DATA, "", true); // Check if the barcode type is NO_BARCODE as expected - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode)); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode)); assertEquals(View.GONE, activity.findViewById(R.id.barcodeLayout).getVisibility()); // Check if the special NO_BARCODE string doesn't get saved - saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); + saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); db.close(); } @@ -961,7 +1080,7 @@ public class LoyaltyCardViewActivityTest { Date date = new Date(); - Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); + Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balanceType=EUR&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); Intent intent = new Intent(); intent.setData(importUri); @@ -975,7 +1094,9 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "123456", "AZTEC"); + shadowOf(getMainLooper()).idle(); + + checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "€", "123456", "AZTEC"); assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor()); } @@ -996,7 +1117,7 @@ public class LoyaltyCardViewActivityTest Activity activity = (Activity)activityController.get(); final Context context = ApplicationProvider.getApplicationContext(); - checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "123456", "AZTEC"); + checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "0", context.getString(R.string.points), "123456", "AZTEC"); assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor()); } } From 9a37d917f7374abc72950e3a8bf0c278c52fb016 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sat, 20 Feb 2021 22:59:51 +0100 Subject: [PATCH 08/28] Fix test URL --- .../java/protect/card_locker/LoyaltyCardViewActivityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index 6a2b7f4b5..6eb668698 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -1080,7 +1080,7 @@ public class LoyaltyCardViewActivityTest { Date date = new Date(); - Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balanceType=EUR&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); + Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balancetype=EUR&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); Intent intent = new Intent(); intent.setData(importUri); From 0af1935d205287b7e0dac3c3b2146ec59f46c39f Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 21 Feb 2021 18:00:06 +0100 Subject: [PATCH 09/28] Switch tests to USD --- .../protect/card_locker/LoyaltyCardCursorAdapterTest.java | 6 +++--- .../protect/card_locker/LoyaltyCardViewActivityTest.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java index 6132d1308..8055e8026 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -244,9 +244,9 @@ public class LoyaltyCardCursorAdapterTest } @Test - public void TestCursorAdapter10Eur() + public void TestCursorAdapter10USD() { - db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0); LoyaltyCard card = db.getLoyaltyCard(1); Cursor cursor = db.getLoyaltyCardCursor(); @@ -254,7 +254,7 @@ public class LoyaltyCardCursorAdapterTest View view = createView(cursor); - checkView(view, card.store, card.note, "", "Balance: €10.00",false); + checkView(view, card.store, card.note, "", "Balance: $10.00",false); cursor.close(); } diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index 6eb668698..3fa5926ae 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -674,13 +674,13 @@ public class LoyaltyCardViewActivityTest final Context context = ApplicationProvider.getApplicationContext(); DBHelper db = new DBHelper(activity); - db.insertLoyaltyCard("store", "note", new Date(), new BigDecimal("10.00"), Currency.getInstance("EUR"), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); + db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0); activityController.start(); activityController.visible(); activityController.resume(); - checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "10.00", "€", EAN_BARCODE_DATA, EAN_BARCODE_TYPE); + checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "10.00", "$", EAN_BARCODE_DATA, EAN_BARCODE_TYPE); shadowOf(getMainLooper()).idle(); @@ -1080,7 +1080,7 @@ public class LoyaltyCardViewActivityTest { Date date = new Date(); - Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balancetype=EUR&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); + Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balancetype=USD&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); Intent intent = new Intent(); intent.setData(importUri); @@ -1096,7 +1096,7 @@ public class LoyaltyCardViewActivityTest shadowOf(getMainLooper()).idle(); - checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "€", "123456", "AZTEC"); + checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "$", "123456", "AZTEC"); assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor()); } From d54f574558e23c6260b345c46bf664a30674ec2e Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 21 Feb 2021 18:10:56 +0100 Subject: [PATCH 10/28] Update screenshots --- .../images/phoneScreenshots/screenshot-02.png | Bin 71297 -> 65125 bytes .../images/phoneScreenshots/screenshot-06.png | Bin 68033 -> 60332 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png index 9be5728578c3043788eb3e110281f7adee3df073..505c8dd9f30572c2f9c00deaa85fbb2783c7b35b 100644 GIT binary patch literal 65125 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 literal 71297 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 z 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 diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png index b52d8d37fa1a56473e877640bdea31b0da368e5d..aa3222545c7e4c8b3673b041e26d8b1004595853 100644 GIT binary patch literal 60332 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

1zwkHRK1QcqNW7M0#dcAzi&0oN)_Web9fZYLU|U zX#C6bL#{a4b?R*YY$&R|_m6w$C;c?QEmPHop1Nj!&y2si#fUA2vCF%xZP#%z~ zrS(itR!qTkhKGk!*p(lw@ZX;cY&za>V4rgF$jVs0xI2Bh`xLLT>5WgCGWsaeU5?`i z{f=~jfWS$VwADJ<)5=GrPQ@w#uDie1~Kq zsxJsqptvliD# zX?{YBQbih>s9S^?7*30(b@kEqPsD;@^`I_I4^*Rfc_L5h8*PzMco-VL)-d|9gTj~w$TLF}Ho(&FC79F4KeBR^F&qi`(@NNygzXo<$*=On3 zGkOBp$%dAf8a()?nveIVSb{$8s%Qx~b^-1;)_%WUZ+8|Y;cp@a@O|Nq4e{JC0it z%0&FK+eAydW4ttZVY8-N_puVNmYt?9WJb0G_WDO^ZK4k79 zu7i+X`7L}^=G#9P?N99w+Nbqa3&#M7{e3GOLzgmbD?z+^iBiKTezfcr*jt!1C9JV= zmdP`#Kr}RTyJau zR%|QbA{4zeS`YZrmbLv?K1FiNnBRC!51M|tv1dhMaJk2Thtp0*3}1^D)~X3p&UAz< z5+d6^h~5U@KPrN5(lHjHAj?!5dANYZpVKwd?zL{ zpDV(J4}jHdIE16bRa??2LeLK<2)^(Bg%uVgLOJ=wTCg>mv{iZOHFFyS`n9G^&jZP> zq824?>5+kWi)yfkd+7TGE9-*K$Jk*|&jTro2{|8s2PRHpBp=s;GaOLlqpHbVCJZ)dyZEIxOXi* zp_>65gL3V0B^J@4TVHysAg1ZG4CRUQdIYrsQGEC?kuirdMXx!Jg|F7{%x)huHUo7| zcxj#~PW^6yh1W>NOe{3Gkm>cf3`P!JTAagzADtjS!pLDzCnuY-U zpqDetLn#fN0@F2p@NkrNJ86`0j$~yfe_JfZCMHQi9o$fgztS~&ma$)p(X29*m$L3LEmvN3$PESU;_5kUA(J7zV6IF5k8@uV-%LQ$9F+ zfb4QXNeUQ_{@v|t`lhBn&9XyBjb99d4*;R{1Yn{nyh4kOVX`7`-UTzVhg6lp)`ViB zmrjc!?}SH?NaNNnr_TKIDRc`TjVx&WEfxmNy)9(wYUrwxr(>U5#p2pXu`eHacEF-f zd)D}Yu2!SRRl_LzeHeP5d3w6C?%bcMxaFZbbc!GKYUH=YhIl9Iz#V9!+9rr#WD1+o zoJIUiGe(66#L2l|GQtY0*4rY+a&e$Nr7dpqEOiVnoOxD1S3 zU8Vi%j`vo2*RJ!RioD!76m5F1-@o{eE7{h%bT)b?_sw-|IiS4O*Yq>CMTsH5XSTb( zWs=KCgl3MRpA!Oy*0Pf3-;S^SIT}W^g@*j(*M&oT7K%Z%2(th3SBq+D z*LDlV^=amo5YGiFC8Wh)q2lveWWdg09bjONL*caLdVfc-XPPSn$oB%Qk{_)2c0Cqcw9KUfD)qj6E0OZ(F zE8Mb6roz26y)RNo0_@3!SL*InJN)*8@Hn*or@N)VgpL-MHTb6w(xT+?|MbCY`Nv)w z^kve)NxCB8<{N{{jtE5W&O~l30NYGY_+G)zBg*}=nbl40y090V_!KV7JTKdwsR|$~ zh+Ir@Swk+g?sXECU75g5%v?S!vFX?1yuBA>L= zm-aF~9pqLp8H0hyO3=ad2f$f@JlZ(hK@vjAKrIz~cJcj@fB5b1BkslW4tL394Rash z&!(gMd2ikQ)h6{VCC7sH;Ys}t%9AhC%ay6zeFa8zbWUoSjy{L3z1kmgBVea%_l%fX z%qVTV^o zr@lP-crUV9>6{)Q7$vey8?4+0TZa6$LG^9?hHS%s^V@&=%KvbJzrM0@y1#z=f4Szr z88j{;5DQ2#boB~h;s%}k8$j}tm|z#BA2VeRTsY>!yk+Uw-F%3Ksn>z!s$7G=73*-k zx0cAeB1}4P^UdC<6gs+7x%)%rEH7HP2Vmh$6#w3P0xEKD;}>wa8?QEPKe@OYrcRF| zFBId=Q69R<$47xLWYJZC6!eyDO+7Q&%N9WE2`4%_l3C3$rCoz*w|)0V<=`E0!f14O z^U_Y)_X0qI%i;dZKL zhpoBE@**G8OwK$JD+LF0tq7=ud;yvdCv!&dmj0Ojd}?m2F03yjppeKXyuo;@Lp zVK@Caeswkxp(gFk8$KKkcky5U!qPMK?&=(z$2#bTugg@vF(>{>g#EY%j6(<$L3|I=~*bfv!s z0N8&NsB`|OD{ZjDf1dJxc|`v-@+@hj7GaO`TY*r#3j;kp{WJ>EZwnv>PZuR%Wgl$> zbvBlkmcel!^DW3rEK%~4Cxdq&cKApj_L%RA3o^AE*>YFO^)XPTPi`luFe}cDKa{@y zZ3%`Be(@`j<(X+&;m!VfSzi2ZW$Le`RX6B#i-=z_8Dfz8K%oWlobEuEvEfS`dY~HL z@b_sC>_optFm`EsA+WkIq*@H5l%mt?NhfbUK_2}8EYo%Zpcv0A$V$oZqM1oko()}+V`Ta#1tCvQ>l<;Da?IT@lD(~mk=q4FNA;Z z`VR>CPO#yDvP*VxGcp>4ws^=Fd^oZXyeBb61(N{*-*hV# zr2FzN!?<~(QlIL?49K3T^9QLPW-)CwaO-;w#H)kg-usq{A@)Dg^YrLS z$9$)S#pupmJaUpF`T|m-7S#&RPF;vhcQvVS%MCrZ;XQFU20(x~*O!4H%I3g>XW7fl z@T2cXRd_^18Yhxr2AtWkYdYgYf>h44#oXb#Im?ix*NK3gC%JvQ2LFq++VT8nw{uHm z17Ue~Xce9@@X2siF*0>#DIa2QeL%%}2fo?mNAf*kD9dQi08+{xi0o`m?2FE5N?Q?3$q@q2bU60JVMc{#b(tyEx*_+>8JZ=2&T9 z&OB;f`Les1OVP{4=Mf~FS(k7|khMt;L1wB;_NKll-P5s?kn%zuS#ZmB=CVv9#W+KhzVeJG6I{Br>ANslzok* zZR|d0NC8JlX_WZu&rC`u@2TuommnR59kg|RG{Fk?v$l|6 zm#6MrM=9m%f@@&5^Wb5(w&|3$l!3v*WbEHgIOb1Hh`rubqM0No<%$Db!o!CKXi#R+ zrRn}-hGwq%jht=F=;`fcL2Nd?m1WUj+5)giJJFbY^D2tqt-7gg2DUix>zY2a>HdcJ z)%|sKb<=fm6^;c3>t|nrfy%aw(bJ1!51v@=s-iu ze#Wp2vkEtTKsK5PBK;!wnlXfX4NpvtZ9$ABV*nxs>3n7s>xTozTH7c2o{H{Kh4%-P z6HfsDsB0|(=&SEK&Kq;0H!cH}e6c#?Oq4VWp#H%D@fdvUo74;XblIEY=Rs zLrNw~5>O?FcPer89Wwz^{jOEli)(s~tG*~`NORKftkJ083rQ$G8+jgW>AM@aLtSfL zc{lrW!eL*f9eaJ=>{VN`K}6x@-=k6^fK*tmi`LGZK<1TpOMS{VPDsuG$OC1(r9 zmfgbVrh@lY(9LK(-#jfS3qemLq_vMynAouC2^lC%GCxb*H@B zILBuJ>Se%Q z0Jus_b{&CHYt&a-(!m+{HFh0DW;5m!2F0P^abpF^Cgg9`@sTgY?GX&w&0{=S0hPFt zemImE)hwer9a?l~#2poOrw30a2fa$aqTam@R3_x_SWE(i; z`#@mvDf5_h%&Zdo2GDZT*MP=zl+ia#AEg zXB^Sz3)>IexaG@0N9U@-(JNTLZ28JUB(MqUu=n|&!eDOq{Oe!9Y_s^E0*ZeEOb&^= z=9kj(X67w4h*N?1g>V;~#-)D^YIX5=Rbg zp0;8KY97MgoL~W8jxT~|mn=yZxl{U87}S+#!@U+NA-nGD)6o$Zo<0~(&p-Ay|8uQy z2QNP0P}IqTa?-4nc;{E}*Ut>KOT!nidmxBki(0qXPs<+8UK##~h0%iM;K0RAbhi@O zLdGmV@`g;d-hOb@R3)VYfiQ0ph7TNr?@RAgSvh(@qtOqRW{xy#On&$oYQNi~7>vaDwdE(!9_TR4gUkClyeE;iRdk(zX!fX%tC%6`{ zG)Q^9scAR3qlJksci^e^U1NMKV*jFGq-NW$$H(p87pQ|tery<-b+#_e<3;#R?o&AR zQZ;cQA)D4GbpSX&mC1FmJQS+QBmHvHND9qc7g)1x4;~Bc6V{$Qc>?Zo?1&xq%aSE~ z5Kk(Xei}tbLCF3TF4Y{E0+1wSsJ-X0bPq~;!lk1SzCD;}2ih~cpx~qW-ZCGW$`?%b zUbyJnmYQu_3Dig-hzUMT3{)399`$K>#>Ey*!B}nqa50Ie66o}{_3g14eYq{m8BkmeB zYiWKa#vqTEdg4KJV3j_Vyo~_)>V9_ivD(q(0+ZTi{YW8bHWSQrkBrHH*uVKcsY_5H z$m|*11CE=Ko8LbW3-&=vgs(k3w&;&rhUO(A!NI{R+0^^}(X6=0aJ6Y-!KSTjW)=R zf6B)pCMae{a4K>OMQ~@Gm<*UXFMjOKzdwV!g(PZ)w~3`m1(}vP5KmV8=ll!S|JEOt9I)kEhYmRo`A4t88KB8-`Hr}<|kZ7 z+voc7pr0Z{z>N9jAO!)^xtT}aEyzDK1usUr3lY<3Cq*I=qynui-t(jSlf#xkNR16t9|Vg9x$L4c4sJYYZYg_M;bYVcaEFS7cK+ z4}ExC3Q;}Grs4z5C2ker!E#$?2_}1Vnz<8mjmeIEK|MM!Rudhbi1bYd#ZiCml{Ed!l``9Y|&U1Sy8yjd~bMt z>H9~(Qej8W{py+WD}5Hv%z5G`XamG#@Cv_PZMo`N-?0~8b8bL}ysR1C={M!wmvdiC z_5se%;Ig{&;Cs*64>EdFlw*E7=N|4p-7&%l6-uphTK;42PbbD(P}y zR9P>N1>2!(YHAuE&;*b#h3ES4 zNNh9Syx`(%cWZODiOaWEn?upX5FE z$)1CcY(fAFMz4BHY%ECI*Q1s$9RBI`ptp^++M&*$AaEvFx5crs-Kkl5o$4_Vls(y* zZ$Z8IE+gJN!3BE(URF~Vdw<&y@Z8aZ41x3MB8sO)_O_fB;Ku9e#g_NHJ9mA^^7rDv zR7-VLeetL+0fiG!+)KYA@)PQ zZ&~X_5a5^Af~26-vso%_kjI`Gqk`Pz>N-ms?zHPuE)f7g1dW3CUN9QEq)7c$o289~ z@%k+kr4Ux7s6ZWawVCKT*9 zp2rukF|527u_CfZIUoae`Kavj1%@t~BTnte_hU=CxM!-?syT6aveRv{+vxL(Hlz!F z6p#G@*b6PtW3%ene3<(LB;qYIy}M9&>+I>&P+nuRWKe0ub?fQ;sG6?l7Cx&Ak$V{5 zM{a->T#pKxr$F;i+i-bdGZ6fP?B@4KO+ix8>Zo`0FcqC}9SnCzYkRuQ;)x~B#!`d5 zi2a86ee68$EA&xG=yT}3QOk67{_6;m!_OhLF$#&KRv@@9=uDPkP?r|5{Z&P0~~1Ol%7E@X7@Ae$jjO3c$Cv1C4}ikjYGURayy< z(s|F+@B2nDG=H?llVI3!!RZ+%`82E-&fABcMY;9FO4M({x9?2Rn|fhSezg^i^`%h`lU|m(!UUcJYz&%`?jIGTQ5%-77v@2Y?)Y{Btf?QYDcsfQ`z?I$ zs$MDb2uN(`9Is{pGA}aE+fe3q-1mc9qbKcsb%FmU=4}ihnzqMZZk0Ox$XIKSB8C!A zre2NOPEZg}J}-4LBl$iDOEXBectO>bE?692>3hy~`#;Pcgpm3&0oXmhVM{ab#0?$l zkWq>H!;^CL!x7S(V8n&qeKSS5r&S=FgOeyu{6%iPSyz~R8lP`!L0tVGvO{0z`R13L)C3}n!06_4LTAT;~Efr(-T~RexI376ngcgw@aiz zU^1E8*ORA9@SgKT2trmg1~?^=y*OAUH(*1}ABUZjLoH)wS#ZvhLZv-w`hh zDx38Izbw969$&Aj1Z-aA%1 zss_O-Sd2P>cU%RwCCGEKYOxoh>a)twOhk=ZQkS80bgG`)@vgnFC-?B;R3Bdc{ehzlZl7vX&eP%m4(@K43!9JWq3#&9!OtckY8GY8N4E zZM_^_tBjbn;ZjI@!=o8aCq6uJ6?Nu&smDvY2EG%S=~eyJ`77syL>;*_*TAVA(&Ka{dFpmMr_9KQQ`1FwniJA@XYZYu@_IY`@sK*0yizyU-?yTX?8(907{TjnF((LvOB1<9!G?u60RyMVy9y-6GBrY(Q4;_^KN7=Q&Nz*{z z_walIj~j=r!hE|PirueOmAxZROcu#nE@x%3o^A0L(jX-|IjYBJ4Ion95l$}kO*BO0 z?4+>}7>*d`eH-du(+s@8ucJnV=_~&-jw|cklcT%Q7>HGCcTM*yDk)V=lyvEKUlzOo zZ4;?SgNHSm5N+hmSTLV%s>*!;s-xt_AzSSQiJH12VotwxpscC*abtXB#^|@=F(}Q! zidO!r{guNFGeEtMRWbcLj=R|Mf1MboTMOJEKn`#Ei==mehGut81>VT(&}q)UfE_?n00WY! zoHZ4?h1-TeC1nO*5a$_y07h{$MnZHQcPO{J0Y8BhZQe98p~2~wuFl^mP&@!MYkJiT z@@B}T7dNng@ZIMA2!thMkB}&`H*3n6;LICno|6u!KzkH2cQeYD?l%+s8;e?RE3i~y zMv-D`9CBS3yYYYDTfb$C>-k*2roz84H>w7J4bFn2^kqzJ;~Sgi5os%1mgS=MMussp<;;ueBxYLG&*kx?g$FPg5j z+jM^|bxd~ypW7G9@~UoDH%)Wb`oI1#A~~gD70WG=iHJ7PL7>e4Eo}Q=F<-!^ z5j^x^#6Lz|2F8q?(@H1^Z)Ba#aKZ7a?zR{1V)7z zgveUP-q}7BZwZl*b&Br-#2CDkGw`g?v)wAshB62VtM_2^M}UF^>X4PjP4flS?%ep- zPxYHX7;qMYdA%SRTOL3dQ;*PyjTRSK$D(=ybg@)e(AL+lv8zT_4Z{MWN+&P_kMDu$gt@ z&3d4Y(>x&^LX$wG1?K`kVCVA1rzVI23`{F%TdvE4uHGgDB1S2ASMS*^FvcUKy9ZGgg9>9Y%t(l816Udzs&Ig&R;C0j^B; zco)o#C|uGOp>3Z`z*Wa|)OZM&nt`Z94R(Xgtk1`91^U?*#{hX*;zOj;P}mCItvi2f z(-sII-6&y#3owTT1;-9yGEPTahxZ7it<@+$;RF5nwePQP!|NG#ypsjh&8Kc9&;u|! z%3OJS@7HI`>z}65nL%a%!C{CYLXg6eKuwgA?5QZQD~)h!mg|mh1+uIsp`1Od=S-I9 z^%(f@$Y2)C*r-AI%a=<_{n5~{zyngSPbrygOpb9CVZ#$O+ti5%9NZ6y&D^G90Hu>Sdx;RCK)mwT8i?^Q^ z-oE%;7ASbJit)lj z#aaj;;Z9={F*s}mFRGnQ;|uW@^yA6ND=2H$N^TU@O0<3s) z%@f`8%c|@#2h=CN_);hruwT^pjQ@8~g6IqLlhV2IKnL*6%b>LNc5_}v7Q%tO1VA!? z&GjWoc(3O=%&c-Hjj@99^D~fA$9FI!fiy`cR7ZE13R7obKrRN|Ijcx_ni?~o0MQB@ zr)aqxDrs3QlFRkAAG8~NYmYO6&fNNjW^9gvfUDl4DG(eyin=wyx1$q0A=$_fJLd9Q zr=4LoN0;cfl>o5;%u4VPM~E6y-DTyOpm-8=Us2UeNP$nAqlyZj=wTN@D2?YidE+~* zBoY}9*T<*k*Z?RT9DvPW=bk?Wvk>lJ058gU@JUPlIy`aHv}Zp%oiHBp!<5#K??SHg zO4HN@lOzF92I-Y5Ph)BId~Q0s+xVB`a*q#I&fn#fs9_9c=Hp#BH#hq$dagxiNAot@ zqK~(Np2|zor*h_h6ef0jn~C=hQsCiaK4TESZ3}FAy`Ap>S>@{RmKdvH$jDy=>1oNLMoI-b z&ZIv18J@8Nt#4%Cz6GVl0&hw<(`EorwMH6k3RF5_>m>k7w284?o^IrjOf8s18z!!8@ zUa}~46Y~d;N`2-?AEpwjj)^2#)&wRrihM2mBeLJ^(jVXPyD&4JZ*$pT;EDoKx`bLR z4)CVuoM3Q$6+G@8i{^0gGl6~pR2$BT)@X%Sf5bw+@vHCzx(pBuaEy#d2M~J-f{PR7$o))6ZfNL!js-g5Us8csI*7fXLS}f$#kw8AdKU>Ahe#U5ktPoxP#l?$=K%bSHG+Zpt z4~K%XFp*{j-FmF&LW8P4XYOJ*bGbkDoP|+fb^<8Mx&WQn_RO7@(CDJdB_RLv5p?Y4vp133xUKlAM9-AG@3tF z`>q0Gz%RF{^qKb=12A0<&k2*apQK5GA;|t{b=UmiQ&y2}?aRC(D{i8aOHyq!YD^dP zqb5Il{BhZX0`|XRG82Q>l9zsTmpS{M;`cX3cKzx4@F))a*v<9uZ3KS%zTrs`^+nGE zK=ZhK@TC~P`dBS)RCs0DPshZfm8d&XXHdR9JkOgU$u3y>@?4E1!POhf&lfZZUB@F9 z-Z{PFGy8TNjPo~ce0JL}EAr!W!{AsOi^xx$-&164HjTQy1JMcA1vLOLe_>Gv=*@tK z3>&;F^Hhz;74I@j`M5j}@g)QS)9eP5T*z)+wIHa<#4%j6J~HiYYv!AAr^0|M-4v^kd0uF*Gac=hABZk)pWU3anhq zXY7>i$wy16tr9+6Xe699oD*@?B46C#C}g%)IWC@I`uV{ zx=BF!r!kHp$%tB5H$2qZL(x9@BdCya8F)1P{U#&4B=ASA6Q-aaGx}@?q-&oIc~38k zau}b(&Yp+S2XPKvk8LIdC*ud=$WYR*VO-lxV~gr5I8VFYIMnnzKe z=9zCL*SSSOhPJ!)lfXRBJ;RMrw3=BI-Z*|0)SfQ)6pf(gy*dEntrbMNa}YPp9}?e1{>glDGk ziiQWsNeT1chNJESD+bH{`JBvN2FHY@38?kjmx?-EuA742*4`0EHrPhMDqK7%kI8^L z*6}ZZWM_CN)jOh^6D`FP!hSZM0Sbe&Uw+A+J2kDg8%a3YMN8m?CNU=V5Y)9U(>3t8 zn`Sv0U$}4*BC&CW$?b1%>U+NM=?ET_v9(fpZ1mY4;?7?X#9^5ZBRmn68p7=$H2<`G zI=?hqm;^2>_rAkHM0<-$?iz1s7pbw{0}W}A2;A{Jkhk$J^%D$&iF?+h=iHzd zQ6f^O{T>|8 zE@)yvX9Y{ULnU)YuflPcYgen#FTO+2BRUok+uov+HFqiJ%a)EVe=WDlP_gC7t34A+ z&l&*_(6g`qn4jq!%>$wZ1RqcCO6fU&ut$)`TDAutHdDY) z#*T=Te#u@u^%@u*&qh@IVV)%Bb#Ex@e3Nn**9JV89LO+-HHs0G8U|xHv-=o`9Q80L zu3>Q~s?RE8JS{H`9e0Myf-=xzVhByyc2BYL*c_K;f+T|xiV>5+!sPhDZ69WM=E=VO zlu{w2BqO8z0QgD4#2Ymb!mF&APsxK zuw7UNom@@mzPBHUF-YqGV0_&myGVUL0mlU!=RzJI8h<#U0z|6WPaQ7mA7XMGC=Y{@ zhr?RDCeJSgw?OS4WUx^0^aI?}xtrD+8-*Zy@NY{H<#n@dFTBxaRAOvH6bYRdH9$#{ zax$^druCr7mJPWhCYb9J2$lhAWd^MP1l+B;k&1eZfBY?hwa5A>usZgS>HqxX!Q7Ps zH~C#v?L^=B3S|WTdAF{6J+O_0_A_lvak*Kor3TM8fDUq5)LN z?3Vrc?!^Q&u!1fYJ|OCZ_a$^Zzua)tktD45biAun{P__8*wvw&o{bqqUuHOv9?c<5 z6P&!CA+1tC_KUZr^JKwO;1_?N#fm;rF|p=v*-D_Sm}a|B?Ndep@fdX{8uIJUee1Nv z7~eMNq+mP~YS|2lT8{KBA4W6-d2PZm6v&t){k7i3YIgrIz!$C#`N{)0s|I@d18Fv! zKxrI+weeq{i6_7D4nTTGK$;X*RB2r5pa_p7Qo26PsYF}q2REksdQ?FlIDz;GZOTN1V}xj(u7u1>^*l!BXz9OHR_1yz%|jQlGqSyO1i>@Jj8$ z^ZNXH?crPrX^XBkOreQS35cCcHcriwu-HD!&cN!V7ljS_@5&{RWL9R=G6KIopnDxnMfgH&gG2h#2!0Q+G(0Xk5eWtxUH*5zyB1M#=ZBH<#`D%kGk*ABqm0QkWViho)e z`~25^k)59<2DfO_zK0BrSO%KcKbttJ&ML!d!12(srHZBW2bY2ez&ZRRd<3!}Ix3)b zkcpRH2gLu$GQyHehl+((y2pi_-3ips{J_L>`bkP^?3DG?voDg*pDXj-o29!NnGnjYcr1Tsj@gm?x zFm50Q39B(nS(j4$RSJ|dlI zmY-6X*H)4rc%WAjzsERrm^{sQ#9mt^_vAPPBj>?a^)uPX^*L%LE6CDVm>IUc%^|h{ z#lK_+%2l0Fd!*@8mNo@F0_+I$yZ3k;ga13Ysb%QEBQ^gx#1*1cU8}$*z?=K~)-nq0 zI>q=l%^2-q>ypSWNlb)GyoV^aUvmgxrzYP<(AHDL8^1}Aq3+Fe2O23UqZUKMA2Hb$ zc!6u|?)0F&&;W|O4$({%P+_PPaVj)8ikrXn05SuJ8pNupd(Qa*pXU!q7!dQq%R&z! zf~$+41zOCHR=lLRB>+g}e0b8838tX{7Cik~dmkXGHDuu@O8c~ojghV{%dGr4KlQO7 zt~)w{M|#ebcgG`YM_LTL4Q#1k(@$vg2iP377$yGY2j ziW}aFjwn3Nd4fUBAdIP!m{*$yKmqYbjb_%1+y!yE1a}Hqsv&g~H`g96<5Qc@LT5t$ zgEl*ah?fp*W0NX;nfBxkqufo*Axb#E(9N*cC7FmL$ER)DN&Gt>UOwO)^YG!D#y6r? zPxktY>0pd#eWPvio#r{I8q^=OG0}@V{oljxyxVR8Qr+NXG6(Yr3tBWM(Sgfj+1ohxKf!Cf^`>`!2&o zoDag5@4QrE)^G8WLa^|jD4sJ9Mk4Iy&akpoq@CEDZKBs`*78mQqS|szJ}nC+Msu_{ z#He~Q5Wv*P^H{ki)9D%b7W%DxDL_EBzb`uY$XZ$o4;fUVAH=_jl+Im5LQoR~YM&Zh z)<(4bv!B1}FtkYW8(62dtlNL@K*WN8oT3=lms|XsHAwBH&PT>TxT2}1>YLdhDU81) ze^^5ax^podeN_E&7Wh=Wj?NGFO}TAYv?@vRD+Jd;J8$6{2aFIq4srQCze`k9j5)Eg zQlUzIz@5BmmX1*-!O!Wwa3RFDAe=A(jJipjl+?iyF`@nR%DoH~$VG&~+D0 zw1^1w%INtijI?Vx64HdU#r&nM052tTF6+J;dm7~{@)J=U-bP(s+n$-;a(UA~UB*6$ z-P|J;9DtO$8;!a=Qlyuio$X}wXs#89Xat=@o50mR_Ir&)W}j3BXsc7&DW%RJ#97_9 z`$Idr&P4dMDv53fVUUUN@D8G+%bWd`eJAJ$&mv*|gQK6s+KSXlW|>OH!fPw;z35OL zw+oSsd(_^30MZfZoT$b#^98MHCVcv;L{Jk*Acq%s5~YHgAa7U#tE=OEC(DLn zf-g57FdPIakJkDFP?WKnk%9nGDXGuBT&S}M54CD16cHBHE1KMTT7rFU7>>J!Or7Z3 zeK~MrXH!l9#yqD7_|s_oP9jNVppM;w>S=aR8cv-I+R(Ldk-R&?r_%)S>DnDj;Bw>p zo1h_N`Ry)eHVx7P{q?RsXxyk{G3W~+!SiIDqps#MoHj4@u|(YmUWT-FdTzdrtj|r87Jnr51`->>~$=hgaId@K@DlCz9BG73ppK1JIcE53@B2Y$Ugt$ zY>uSENOM@o)gS;EgdVW@5{8rIV`60bdm1?p&Lb!60-@5Ul*dSEQJQwv&FHt{z9Slb z@pM7hJD#3135aiuYLWX9w69G>0$%mVvV(9262NrBGw;As;?wkzN<;QdTn+@?Y}VAm z<4V+DwLev6!42BFEYEca!rn6@tJ0lDjb$pYU7JZbF5WV21c}=D|< z(yH2MmuFJy0Fr{H0i%(%W8~!zj0;y&S=$q0TXd$*~8tB{S;{j!K*HmX5v^yP|AK; zO*Vvwt*!Iz`jGPa*I}voO*Zz^hEgDV z^n`~!7KJE+!ozp|aXm0PpDM65@Ju|1$`Z)RE`gu`d1#>*6f|9&pWFQ0$LG%K7MLho zMBo_7{i0#q2ZA*4%kQDdoBMETD1szuD=lAHv5)%|>FATzY}jR*%F^a90ddy?*qIav z8bSNgU)8?jE)+_3ddMSG0hGTUhnKRG@}a;EL?RQb5V9c;(%gyXw=A~qL7&dJ2~iZd zmrm)QgO2{ICR^_+U#B~rp#|~GQs4So-+^_lFku=v1G7Gj>-9Jz=x*&hX1ba(3f-Iw zQic7dBP^Cg=~55kout5W!1I>Z+VUAu(Srx7P_~6rZ9*mm8Z~jR$q&E|N(P0`mi!`4 z6{Ihu)NYw`Z?-*i*8fy`B&P*==)i2t4l@QJ1_^8)LjH_wagOz$Ua94yef(u12)6SD zXo9Gn{%eNkKiIV(O)KYNF=jO^R&;Cg3piC!<1apGA%7kKcTTIV0PZfk>zBKr zxRl0t!T2rv_Z&Jo1^VsVRZ2Cppb^Uk>qb%M^@64T?f8RnO&^?rzQ9Rg3GOZNt{Wu~ z<%z}uwImZCeD?YywR6?m6($hsCtAaqZ#~NgN{tls2v|V@EVt`t@RZ$byX2dv#k~ld z81pkD2FABV4s=&gKGE;Et#{Dx#j#z~f8O7Pk>8Hy6tn8YrS-R)|M>sr&8U^l`{4J! zAFckiJ?brY)I9%a{$DMh;55+KPRPJ7wFO6t(piv?w0cviBVb(tCL1lKuBCjVGsCnM`JYQ)V z7N?=JMzXGajKPHYEMBzxV)_Y1CU%+Z%mLXQh<;dP#V+%w!*i3Zt89VC?1gp^*e zZ$XSF=SI^G{Fy!lNtyiqZBjDjk+aN%AFV^CB&-w$Xy)I30n*vL=ihy?foEY)Kn^0~ zfBA*_5_Hdh`tlDwl23JNQsVI_=m0CJEx^#+$Ejkm*g6~Jm9T6Eq(+7*K#9?5CR@)Z42hl@K@!!WD3wQ1|x4d<68yMu%Rt&5H?}pNTg*I z8}GV?j+ILv_o{>iA%$fWa(NWx-BH({>2R z1$XgnfJ1>-0}PSiT+u}-uBALtGNI5WQwHqXl3(s@qm!O(fVjLw<|~U0*%nc(uBLPC zAVVuc^=z=^LP22?h~Fx^2w*q}IwT2?|{8+aC!NJ`2DDruAk)gq9O#`w(UOot1Lwc!3Sp*RR+b52H)L)sKqg5i zB(#jVYXVX@F4_rViHG1YfqWd0AWy9cUe6nRqUQ^z4Y?8TClz`8vT7hdQ)x2-7Dkyy zqD*86`aAfx4w=(6SLRYvQq0({7fzSYhKTpNJK7MJbD%tve1e_=*U(#ahISs+7@4AS z7pTV^rkva_+a#$J; z&@2nda1V;?Lr}7l#&B|eVeMT!=MhJ5_Z6unze<5W-Y$?u*i2L25?q~c0ZNzEbqzeZ zi-aAp+^jD%GabNm3)~6R{3ew9q(Nu!tJVh)BBIA%Qd9a3VLnyKq9Bvb=7!*vLcTz% z7(&7T>U14VPD1$;mAS}q8Td0{(XUcq6=c$9055YjDe8$a_d)KJG;nbj*16mS%#O>< zZx{Ol9J_uV;pbM&JL`dPEB4V&b7X8CaWRn80_#hJyLjIy$e)KquYCe|35L$ELy>l> zJ!K#U#qMOJ27&# z6s!u|x3?-CxjngLaTlzPpwebe@6X1ssGVE&Q!-106dO@;C)mF@k2!QPK*XmX=DhQk zmXje^a%7LC)=?^CjXVFfq;h`6MXt`*bz5v_EtXk=Si;6d_SuYd@TJOdL8V={_DQi3 zRRI|f0GwhDU3yYOVA@4MT)1M52lQ`{`)6x*w2%+DpHLFXi}~%6{Cq&=d)3Cn)5~+- zI4OIu{iGM+8nMDlsybJX%5&X|uM_VCPs6~CY?RL=N@n$X7&0Sn#^%s|>v6Tx!S4X% z=tO=x@GSB_2*D96mL)2=B$8%61wrn5;@%^tz>pVO#00{SUDWaJbEJEWvb=7O zXY-Ea-N!{WFLx*!w#Y2=;{;aHkbk)%%>}%9mN5>X_KE$LMVeVdhGYQAo!mRoN}kUi z-wy)Q+kJ5%<2A`1JJfZ66@~nYRB5(m8qDhvvwZgbPdtcMChjf>3si ztkF&jhBTWyJkQqE*5>+Ff)vi=xF+IaN!0lCm21TK*0g!g*Jm75Rlgsd#bDab2nPmN zx{GAk-Ce`hvSJAYLghYFVeQ%#!LS|jmf&i-eS%2NN_T-6je|TvreGVN>!#tX@LK_) zJS%|BW46(amO!CUEW|%RD0-y32k34pDY0Zx(j_P;QdtEVr7oKCKwW|$K}&+32T<7| ztLkKc7#BOG1?iH{Wa=*$-+B(NHI;6^pD=P0W+E};27XGOjxnUB!nly<(i}@IT?CCM z#gffDE_{{jB*x|mm58^#181h-SQrx4`T(T<+}HsM$z->Vg8i*-`dBdVVu{lUcgqff zfoWwSM%etE!;rEE^St|d=_Um)J+Cl!hmOhFJuRf^g!T>4IYza{H0N?CljwSYqf59|ng%mvm)FCd_%-8NMc z*hEgyIMg$`b7U96qAHr)ZWi^lv{qUx zB$N_-YLr;$i*wNbIu1kGkcQMl2d~xmZmd(#*q1xP) zM12?M=osl2NsIDac|E$jpP!2%;UxfAI8!YKJc5M=%Q>@(0D@QUjdQrRo|fCw0s1uz z>p@v&bqu(x+j|}y@Yo;$SvU_TV%FRBG9K4{E+#Lar6lD_+0BA z%$+CunpXxoOhLG1wYOe^olr?2H6_LIC~CC0Vf5&+D3%X~fL{^i=jRXYCnIb3Y%YZb z3k7vZ_HPY5#67RR1Ls22Pc2}JFuHwL#$y>l63zQN4L2NK%5&10gX1{zd9UeZBByvX zX~y{xl=j)URpQb^3-OSWkWp$lG_Qccq#5m2p7X9s0wq4AAnG~b#a6bWBh@Hl6mn9M zg|t3^cBTK)!ff<;mu&oe?XDRXYzAe7|GGDrBMOoYg-kd{9;K$|Ujxs-vX}V!Bt))^ z75p>>iMO=_FD`+aspQ4KZl}RF7xgNJ)9facAz8a7)Bst@@e2?Lcmj^bgS*dgSxuU7 zbiV(32_5h^6cB<}ky=*M1yd`wQFafN-hn*w9&sTa6UcB0s6S`8 z*}y&t*b*1Wpl`oAXdPLBrvVOcvP?%{&D|0@W^LJ94J(4;mB2rR5}+#Qo{br0pe%Ne zZxP@{x&by45DnB+!5wBiAc45V_g-_3(063M%H0cQ!+@ne_4cKb1!HdJP3_pvk7$1ELz-&(#d z1uOen9a4MlR}Ik;4lW#vfDp<+>qF?ls|j(La5x92A-EZ)^+u@v6nrudP83z?IW0wZ zK%7E)H&t8y+b2?tHq|4R^VByXh$Ad0#8k_{k58(q^+V61DiTyZ|Hsp)jm_u&{Dhc& zWLkhvz$-#Fi$6dA^7J1H7gBvTJMp*AKb!gQbwz_WPQ%26>{SIGs%OgxHCnk?UR#?W z`v6Urn36@gPZ(Hxiol7aa?2Y9j;`%8-B@m2I}NxwaUu118U&C|ogTu6T?7bp1;{;h z&>ewlV(~OW#j6fLE}Pf4?jqa(F0wgdZ$>Oi0t37kZcwh|hzxk*eL(C&nMTx}S6R_P z)#K0c8qb8n!K~P+gI1Wq-Va`B|C2aC3WIID*4ft$CAO;SY zacbQl)bXfj=L1G{byjh9qFZt;3}V{`?LFvGEE5HxT|HXsL-s!4K*&=KDipI9_{2e< zs21X(E(R+H7Nx({q%@l@txbBFs;fV)!26ZELL_BtnxXEuX)B21@d62Z2i+mPe*FwO zFuCHG3LuL9pojH{IK6B-d7$m>oDzUpibS~qAtQK>K-~b+%HU5J7AW(SYwfpmnNloD zHdzU9S`OY=+#A;fG*4bc6>*ADtd+Yr@)Y%gnz<*?4SEWpQ=0vGva4VBhHeLVfnw3+ z&k!#aBCr7x%aDi1HQy?*v~#Fvtc<{P{>uNNuhUJN0X#PpCM*~pb#E=N!|C$9cw;@1 z9h+rUYf#{98>ndpvVO4?nEyz?0l=x*+`9}`U;X~Y^z&1*TlbiTf$4P=!a&b6K>)mS zPivAhwN{FgvnuBkH7?AZ!dzU2mOsAIka$15J*jvdsaa;kQjV7`sk%G?MH}lY$Uy{N z*U;@pwt3FJzR9ZK@_z9n^zLN@a(Ng=ZKtOIQ5bS`4(yf zHN$QB7vJ9luSu;UJx0#cFyR5Xx(1GGeIR+xrOxJ}IslDvO;8_a1Bz=XJpl~T7BC!* zxo3mAD?W8icutKx2ZxX76qz03dn_su5^lujq&oakd%Y)srOR^2VKB9=*fMQCd9M06 z5#R_=Dde}Jr?GYBF(86Gcs@HaqM4|iZ0Au`UENGG4eD5YF(7I-%nAl6?3I2~$LvRk z>TQLVp+YrQ1~DCR6!0IkeWSz3W#!-+wPAW3iT9}*b2r#0ZuQF#LOlUhCKUTuk{Tv6 zDu7Fd-8M40zT8@@O1=$L`e|v`i1sa5bnD(6k>IgVIs$mvQ|E=PK<3QQ_Rj$S&bmTE zVc{x5!?<-96~IZ3a{$vp67xxLm9v^8gpVQbcqK*D*#$uXR^}oX`6<({$W`_v4~*@R zP99Bh8Y?WvF&O0RGJw%<#L*+MRAvM!X!o>(`!13Ji_YN5$HXG9mpQWFm8{eR+|ihI z9XRuw#*Tvp<6wOnk$Ap0i}K)T(3Q_~h>aaOGSTi4C2G<=soVwR`lczGg(YJSU~&yx(=UL zC0E9?9QP8y+~Zur?K<12XkGXPc1Y+X8Dc0z$*@rtxO)MJz|gulYBfC=*yxoB9lm*R z|AMRH5}Y<8GbeP?3=lZPA%#J2W|OwC--sSNP$9nNoi1z;M=~zRC)rfQDYe=>=fy&eiIN_uG-fZak# zC{)jh{P>R^kahS1Q4gq^5enOA-i?efPw`4Ie(T7IPx3?NjShZC5PLXDOOQy;V;Vdd zN-}k7(f(UAi8_1{o6WRKp^%77YRNW;LpuiwfbR-G1qhg0uS22$w@_r4?mED;)O!vh z#7nR{jJlh7wkiULKB1TzrUBkJtG$HG`pHbF6Q0@#b!r+FLnqh48(r1PE$!#8LoAZf*(|9l8H;819|0Q#$zUU$B2pbzhsHtzQLM}EH6K4P? zM2ESw~ep9xcd5fM)gFl(VBJmNg#3dk%D?S8_L20yUt;~cm$ zR4JJdRDPb)yH{#Tu0*Y-AFU#YSaJ7pz=e7Nz@nIhV1 z6gp6WOp8bxd3_LaR8upys^fw0?;*LfQz!pdtzEoYAAn(mnS<*#W(5IKBs_J8Jw6GR z2v9_Wz%I41wh$v?Sq0HJQ&s$=Ps6@GtzRK=I3;5%@{|ek$B_P*2{?Jd;`7QN8(V?r zOC|5QP%1aEaK{TAY2XB7HSyK^f+`xxjzLaGr9CW+3_4=tH1y)=Q9@7`cyHh!K;^?; zDxr{I!|QnojYr>RLSPelzzP8%WG?Y?-r>ue9BEOckQR_(w8qUNO zVBD1++k+r*YXBAH?0OLRns4kV)p*KA#bVsHWk~r_tx1a@9XdSAP-G{!!5T~zt#g15H5X96wu+*Kn{f{i^|ic!j*QP zyZLI_v6lcJS!sUHSx<=_v>1%Ps&ap*2j5}trP{UqxmWJG5T0v;S?0iR7v&&I@%u$U zi@e9!JQx0^;*)gy{c+nIW;DghjTf$c6&4I{h?^o&W z81ppKO`fX~`G~+bavkzS<}={hdw;5BbCew{o62Eo3JCytEO+6fM|4(23^}JA@LYMyk z1pS8zx`WREFjwlb{+pFTtciN*U$4|ZU;1Az*?)WO{tYSrGnM}@X&OEL|0n3jEc_Q= z{u2fLcS-3#Q4pfUA7t`heEENI_cNr`xtU$q1vL9n?a3@yUe#A_%6ht!+LPR;hj!;~ zj1;a{7fXLxSeRHHt>2k$kl*Rczo55L!_FFAE7d34w^7dAZ~315#1WwBXdAB zer!DSmQ-Qvw-nRa_Ni(J`_1U~NxFSquid=F|2nJA&Gy%Y%@t}l^|IpKYeVKagsv7i z>vzsoSBXhQxRrX9ZUhRI=bGKs6JV;7;)-Tb3ug9vy1W`t5gfpAQ1+CLTm~YlaFzi&C>JBD1ScopZ1_3T{N#_pa@()1x;>qSo^=4rYjC;4;i! zATg;SpFTlX`&)DH`1;hnXjaemZyW4t3mvatIFwLS-GoBV6(rmuU9hy;t}(Vhm6`uQ zDpnT}UP2eo*Qss!-Hv5B*1GY_7M3kOjK(@b?fG2&@nl6SQGhOb)pR+PIrSj_p;X(Q z0ie&texgt4bvCw&`LtHXF}H3MBQUu-9g^eSz?01p!D!ecVJwhAnP%z>{D@)%CkGR`ob(;sj7R!VBKs%@J0Zs&12#KM%CZx&f!_n}v?=X_z5tEK}Q4L;0| z(o$5=<9N0u$%8(a(A_HCo}srKKYuif!e78+$J1j_csdWQ4)3!w2XZRJBF?zoDp2F# z&rjofsEMc853PNU;Aiui|J-L=9!cN_HhSk?df2*0LDziXsP(!P2Y-%!R=n|5&Z=*Y z3C45N7h}k^uGQ1!*~su0`-PsXMka$&!XtAbY|!|Ei2AjmDoT3$0;`==580U7+E!dx z$=>1R&JDXxwfztaQ+a5ZF!uf;KY!Mww3xijE^6o6Y6wlKDKjpD%j0?^g7#UD{8jFl zYo>&5x{TAAxqb|ArZ+VC(Q~skeQ$}GtLbWxX0I-IQ+H;m>!_Ig+~)_i^sGsulLknD zvWdp!Ev-((a98bv44him_A9>=Z^68o(b@`hJl$Q6#ULKcQE=y{h3JA@2PP@P?&Z9ndqG!IiMkbN}au3?8=RNm==&vDOFmyA=RMoS{3EJ zq_Cm4)wsEwz(AuE>a!oc60{~SRO;Y(j2orWn)nL5FE|w=TKKh|;83GSXnK)G&ssQ3 z9o(aO+=1K;u^6cq^O7odY(j!Tf}+h-QvqE#y5EjE@}5<0ykdFq1S9l27vOeg${@F8K|vL5O!fuyjhG06fQ zUH^c6%yruX_|5VDYZhp^-Uf%6?@or-=vhoTGvp(3n%L#&HaEfWt$}$^ zf3CB1>MW6=ddo#Ls^{3j)ZYu-^Cc~n3^g~76zF=;>d&~$IMHoY?fub*2}q>1}-ZtlA|uen?(p091QWi41Gf85A` z&McSH%jLl+?uP=iFN+az?D4LWe(F+nze;m3aWTPRR@aU83a$0sN$Oxe8S)jSoFW-N zp<&eJ5=Bv$%ol9NX9=ycbgSv>L-q8-B74)6SO^1Ev-jpDBMA-rC-uhG` z9pRo#pyyn_Wq_4U_F4{DP6nAo+2iJ!n|rI&qtuFL-=_JCX?0Y*F8`9KkkCM#TYRpJ zbgD~|0BO-x>1$n{>56^i7LS}7X$i_$hh{*gw#YUE>C8@Bel%*Vu&wk+uO#E5z3JFf z!k+G>-G(+gXFIU%FN;;BUfpn^Qqm|L;kF>D?RBB~VUC5l@A`(e>KK1$N72ja;oU(q(Z@zmwvUBMdaRDars%~B(NRJMre$bXassqB~5GBan7-e$iL6+E6h zY7|?3^|y9Knj9SM1cgqx0X6%AhT!|n>6J_%*~8u&cVYcipB-Udxb(+GZQ03Rvf8#5 zANJVdgWH#lv$_#8`pY(BNv&OR+Gq(y`7L9NC2d_(+)F|Sx68B$*5LV4*06)Z4GKG$ z`Wj8qJD$afe3!+F#A&pN#|sVM`JUk;c!DE%WP}`lq z{Fc;5N7$-Pdt2*>R!7;N?LFyzJLkjG=~4JbnfS&BpUgh&EIf?n?;ahY*Gt^-Ue##D zclE1`aT?MM-_06W!R-c~D7dax=6=E|m9&1+v8(kN*F5o!KYBRl$HUrta{?pAD&!Lg zhnBV3V9b9sx|gGRTwT8a-sX?jUpd3AW!8Z9&vrJf^;4^P;wxrs+EJ~Po%y!3w@Tug zDAYr6(TK=U!sz6UTjxNnY)xYTgBl`)?FZp0ZpEKy2s#eKy85*FXa!;w21_bLT=4cH z9@eKK$O1czk>J)4NBR7_<(O>IHQUsm317Dr-}s(vDDCJNxv?;l>00V#l$J7nE+Rrq|X_FGo4X$T?)G>ktW z`qsS0s8w1$GmcWHpt62?I!t2^VO%fx(5J)l39)ra5M7VCsZ@VvWj=-XJKk;bEB6Mx z>8fuP^KIvGEfusFv2pZSEyca9V%(%u4xw87Qi)5X`*7=S7s61SDPjbqjd+0qNwh~` z;W8~w7i6o2d1Sshp)hEunz%8(PvOuuwvuDi4&f?&s$N7Gl8*VzjED=elk*irh18@R z>YECs>}l<3Z*FeOpXCP!ye45^J8>PvUeedRzR0rs>F6GgR&8J0pLbYJ8lOrxHpp?l zTIqwUPd8lezbp}zOdnv|=~Rt|$XjuPx(lVXGSetSYY}`lcVpCw%edR}CZE`y_>2X8 zE{9Jx;az=v@Zq4ls^E!)?hnUNW~@8BGT$_)X||g+k2hFXn|h}9n7`{Y%y`GW{$Q)@ zX)d3WI}FghC*mBY?FWSej!hLL+dSS|veWZxhi2|_%Q4#?&({L%^<7=u>yxkVntrSf zU|wX?GJ7EOEI%8q?G<^;fiFQZ469eMhPmA@9+izUF4ACyOHJ86okk5GiVLwNY^+_i zI%4=SDVer?WViPr?Crg6nUB=)e(wVht3vV_Gy{{g^Qo1pCf`5+gV*iPI&f3QEpPWr zeJov*_1AgObSLWgNsQ6>KJm(oj}2ZoSUA%zHAl!3UxUD^z2s4bS5Ju8#e#n;Sf0 zbJujm8jeu$=Xu)2w!4q$F5m2>lxO8&r&j7q@07#Q`ME8Ms$vD}&;?CYq4tV!cNix<<&Rj1w-$kHpKb42f z`QUQ2$+87M2dz`CjM|&_7Q`FFxd}NRm@AxG<-xwp%q*8qOozLkEhN34G1r6c#a z%mqOBieph!7NoDdZ`m}MG8@5(S;6Dj%Gr*OH%1nw}*Yfi_Ka3D5^!y zyj;-|Ot55kPGRVu2pi4l^a`d!oV1CCV3XPf_lA$>uj&e$jPvhEzuV1+E}7X9!mB5a zzgC#%7&2Z@eJeOmV9RD92li}EeFMHSr>f7fqatj-a%#y26tRbUfH==*ljF)CEc?yke1h{dAPF_ zuG``g&@sHLNZZVWRkO#myF)BXvn;`X_j*XFu(Gh;9*8G^`M)V|x%z-kcw4&X@ZAs6 zzA}>MHr5Fg6lr7m$qhiZrvQiKdkUTy#y5?GtJ@C-9ZkS4sLcj4F8+o$(jB1 z+qB{fG|}m!LPxlnDj=0=to%S%67woHeRfVSQN!#CDvqt1Qe&$$zp(3Rto6aQGU&L0O`SO>N$7x}G-PGl1`>3O&=4LK0wNW$Av3exS^lYk4 z4$ke33hn#3%WtX^G*C+k0P5qMazCclmU;hRFgw3tv9iRQ;iF=%HuG(ZvPFFxsw#Y= zn~N`XANIs8ZKqX|VW;OK2~5q|cLuI5I4e_}&1$stsj%`1Dy7R9T29rmndXTfziQHt zdCgR?ZLL4TgD<0f=l5AJg2wpfG`Z~tB71niV7*77T_?jdVLO&9E7|<_3afgERDgb^ zz0nS0(OxH+zVWMg#$}Ko!EdP)Iqn>^CptZh{tp^D0632Z2nHg6Qsp z3m5o`ey1+z5-q{UFhMssNH(fdFtKBL(baGM%7>$3y3PgNE@wNVyCYNooR$x;cwQS{ z#il<#H<=70oa2ZH)u_rewCS03W=j>#*6_D?f!%Otqqt}#AT8zfwv*St$8=6OTpE1h z#oD;DA$*IoArt-G(`LzhiC;MH?o-zixg=*HVirr&lC5<#+ZfZSp0kr9r$%7@o6OZHpO%F=)g!`p>uN6(_^=cI34x9=YD zu5wLv-;f-+V6NEnwAblCo2}r2|Gu(VGb7>Q^$mHi3#Hrd-V9U7j!V#Wt0jc+V<`>53ggj(maoTcRyk6>v`Q;i?k zB$XC=tfmcN^7`|cr!h$mI|o(M2H;xaK6AgXHHjKk%QvYnwMYx8CUEWkMz=9_A@Ai( zaBo_Vk>8$;#SI<}vtI+4Ev^%ITkupSw>76qD=5iPvB&Up{ZhZ$K}X+_nCGN){`Y3) z=An+A>%)F*Ho9ERhkNJ#ABM ztCjl`Zc$JEN3V4Bom;z_>qQ5Hy8E5y~z83to=j7uR1p?$H$rYwpxdH&QO! zRdMaxXIU{$F&?pN{(c%!)Ulm6O6@wKJF$!#`q)1ryD`3eR`Y4SsQa>sLw!c6f7z(9 zjLRAW?v=ZZyv@f?`xCBhF4~SXk!b!))wznKowqmAeD?N|omr1P2~cv|U(Lf@E;xP4 z#beG)+uYkcyF&bMu%AZq=C+E|iO>^(bs*g1^TlJpnBI{*74ibJfdsH4>SQvnQ%$ zJqgcP_HsNlzBkusr`SALwbXcPY1|CE@VMe7kAh(0`b@zmFE6iS@_oLG{ljd6Mk~Bu z7neqd*AzL})hJ)T#YkMwc&m3s;FQiqEBhDJWwLC?=GGGY%0gCNz3z@0dB06%a#no( z`_tB917V?D*AK28t`xn}#cwXPiE4>IXi>ulci3UD{7R+Al{O!{I=2@`yXVdz@&w-7$ z^!5s+ee?;+o9pD6CNgIf!D(z|W3sO7lA4-C?dD(y_INene)#)x9c}8NOaBSm@`%4o zDa>J$srESxRv*`P^V7)Qq!F`JEsgXVSA}PnQ$$R}5b5Hq37ibo0=nd9`;n0HXjUh} zBc0*60VOj<+w%xjZV^ihAsJQP&K+<`vpODAbvqj!?d`WEa5Ifs#mV-7Q zj_EfmBsl)fk%yTLYyQ^qC7 z#a)7Mr;?JA%RG?qy;FI|uU9kM$0202P5+Fot!)NAbZ2av4#}jux7TZaM^RCc&$dUr z5Ctx4cqHS>%+A>JfF=!f!y661hn_ok?%@MUHY9TcEHJ>G_d^8`i1Gc9EIh5SD!eYT z&Np!pM`TFbDwk;?5&_A@XZ@AQY9hQC+c8VI4ue%> zGC6tjk@CaLN0k<+{0n68)eKGXv$Lzwt64^KHUp?z=r7oegPzQofo7<7fa>COvxVe6 zb|1O93^h^eTTi!rE%9Y#WmTajlk&bzOiYBhp4xFQu}F5B)}|(rw9xyS(+~kz-u&zs z3Od5`ii(N?q7Lh!L(iw_DruDvWM1viptRrJyLTZjtMmRoFw5Mzcc{e4#iicC*Ww|k zj=8zHot@p+Ae4|&z zH_mOpBjOB!c@V)l4Kb8U9fuF_RlWf@#=b+LW%ra>!TN#;CqP<7Qc@C6u_UJ^*IkX| zPw(Ez*#}jcofgC>pip=#VqV@7fFtph(pB0h#X_?vq$~xxR|a{eg>QKJB!h=F-b{DUmqX;ECrDp^p;Q%KH=2K zfU}XZXeMtsqC{B8voxw<3yBEjWn*Le%hjo~kn_v?p|z8Skv?56=DIi|(4it4kIXkn z^c1929HLyC^ z9STOF{6+};g@*B?pTRF!)ML3L05NrrD5t8X7JQ+YI==WTc6JL^x89aSj!@S%etNp} zhHo&>InS;7K3d;3_EEguM*6ZcvWk7LYSibWrH8nLoz3vUQWp@Lp0OI13i;vStXdZ$5DCUVP zuSg^zrb~64zG`b*V=k4a^$dR*mUCSFq$;^v>dfB#pzG7T8Bo(Hew{ z%6sn$RZW7*lAUAKF47lf-FA5pHr>fd<>AbVsa;Hzf=$4b0*G8aZJa9!8x;r}Re3&q0 zEa4U;ar^lC@=SyR1f_)tMk%z`s8K9%1`j;8Amk*S+c@D)+MqtcA=PsXn z?6E!=^827C{Za#lji@b<--2F4qo>eQYnzAC7_Nw##N_x1o$CGW9*J!jHE{*`Eb6wR zVBQW>^Xlcbl}{ z?kr)#uRScV8vXntVW)njFv52`k=bf^Ba@tww7$+Be2%QwWD}bIRDlCjhrmQT%933gQuQoXKk#W z_}1>IP(|NDt~T*_+)APTX!+Zn7Zg+FZ(fwjU2SC4mrn zhn4H@k5mKqIZyNCAJw`Qg9Qs=apgt*Mn^3eiY8NhNkpRF^y_)Hs;DQatZFIJ!K1b)%J@vOP3j z#`p<;_<22;o6*?zVrOS@E_bqcgEOu~HX}bkUx0>$aYFJxIhujQ4(%*uqz23nt>&=XI-95vSf8HT)ju zRM>sH2FAqof%VS{(2d-#!kFj&&~)NHvCHQ{3$&||D=bfQvxpMXx`^@K)$9v9 zFM)P5(Z@c?+J8Vi?DvPpXMXn=xu2{u8>JYH7M+bWU0G5?`iMKHAT8AKR)w7bY<V! z9JmG(b-}f3<^>aiQVu&+{Gbl%IW8zOp!b{VnInY;l0L_SozWoy9Z8Qr(iL*G8Xx3w z*q+*_K+Cwr&-1*@HvRE-quUi7#CNY$ zF?vknle=7Cdn0-?fMwcncexQ?_(jaIHLoak$K%^7kH&cs%kthA=eD<7Fy~58GDSaf zgidB6W@ywpSnPOfQ|xEcy7OU``4ceWwUsuKitu&+IZML^aSarzV2`@(*qlXN@GYGV z?aI~cUn^HXFUN@(^4z)Wg#}{FG8ANjC}ln8UdIz-8`R!OX#_MJOz%f5Dk_R2qBPT+ zA|Lo;bcLNQcUF3pCGY!g&o|q7Zx1Lq6a^Q!H+{J;%bCOTrZ>0|k8lX#7uqcAugvNE zaCV02VDWf6Jvy-YrG+EP%%Qvdkg&cjViSxuZ)jV}(jK8PwK|#S3!6Ae5XZFCj!h&QuC!T&R z@mvhE8MwJt5yJKS?(s-HLe2!{<(4YU!&sHn6#J42&b(sz-DOPmLh*v!vsJ^x0t@9-= zHUmU9+o=;+Z`hxAUP+_e@x_&duG^mv%3^ zgP5I&?^CWF$qixND;46-gS2pfee%p}Csjy!B%bi~*yb=(Gw<(rhaauwWZ61KC%#L+ zE<;R*5pta*YNY(2K@jL>nocm3=_5* zA%sgbf3MomLlJ+*`q&Q^zk7bq(wvPhPvGohMwNS5+P12J|;ShK&iZD5j(7 z6wh{oZqLO$Bw=q(x1y`sj`1o4_yD$p+2~HM0_9}uY#_*x^rj>?d#EN2{L&5VdV00G z0hUunS*Uhr@LakAGvU+z0Ukr*TJROU4tBkrTzF|@m62Q-y$UZrT(h%t$1La9B#3^Q zR06GXP8VXZ_#5GpF;Vm~#XCZ&S*P?g_nWedfGhFz2*(-3HmO%Lm_e zOmCX!N@c}szsCw7S@B1B=aqndba9;Fwc{QkHoN-GwrdalpzYy1lT9B1kk-fr8r!K} zaUIUa}h0*5mk)R{ImLkb6kRvK;xGjgIr^T+8*>WL`>vbl92uD$&c!~Ne)kO z9|<#M0a}GUxmm&HZOVRCyhrR6$8!SFe5kn*y^sbaJGXtV3M=NiXs3a*-&gl0S^@?3 z;Je+ingEmcE^sN9LJlxQ!c6)Kw7Uo0kP7HU&avteT53X;^A^yOcUBL^z%fm;C(%JT z+cKWQ8t{d{^3R#HTW^0&3=vR1%Hmm56q%~!B$N|2BY>Y7tG3j%#sc7o@Lq2ap^@4_ zvAI&!sKu7i$B$#T4w@9BdYn`94850TJ2Tj8_*12&%KBMy1QAg=q|H6J7Ah@j2K2)p z7J3rxeK%ucSBG#kXES+aCH`I{6J*moonpzYg}O7Ab>m=$oBc}a_tCwHqBe`H2DLgD z9M5WDqj+r;qF+i|M?$}lB*dDPl?xp8Ms2}rRC*$XhJ^K7;ooq50CGEDXIzA{UZZYj zPbziUoNlgb_jZ|sm%vG+Wv~3t{*#Tsv6i6)s%y73PE6d=tK4%t)m1q41dqic_{F|D z%&1xOvqmjK(M5Vyr7qRK``QE7`z>}Mnb~t*u9ktp>v#h5#!DslQMPHNnjf?<^t?>N zY!nAa(z|{se;Dg-J4*??b&5$qIL~q@;i2rD%%1l~(;{9v#~W{|)W;(Jpw={SrAT0@ za{TyYe*n4L$o4Ro&b9jH>3jO_l|u%;RIJOpH8bB)aUeNtkR~` zeZ0lcgS+x%aqiiKbGDai5n7m5MyGrc@Nw?iSJ7u_C+mF(5&e#n^@l&a_xT0_(9&zw zn%nd8RX=tGd~;ga3X3_CZg;Xed##LE(7bAink%O*QH?)9nRA>>Gk5ZOAT!ncy1Hr{ zWhD0}8T2e%1{L9k+_IcpPd|3DCkY;I_fsMp=j+y|4FwP4&5aY?W+LVMx_mb@x3vItnDpf>%02zw2(d}7EiBYeyMLhG~kT-K?G4h8$$xi|Z4<-J*t*9l1k=h*ws7QCBObeq0?<|No*+-i1 zufy`OuCDrFew$|4DiBer_BeBl8pQS8`8_>BxPT-Q9qDUcMiElYlq9tnpD*oBQ(5{X z6?PIGA`njCPW(}ns)}l-?UxeiWw~6KAVW;1SHA0<&6_GsB5hf2s|pLL9Q#pzBJW8| z_!KOljy}Dqzo&|w%p}U+ORPEoG3WB z20lg@P^VFakG;Mp+Z7?qToYX&qUwpFbsQlqQ83;IT1Cg2_51G`(OmnUv!6-F>NG-6 z39qMSiG7~%06^-R>*VJXM@zi)!E4+I1^Gg@sn;)xoR_OYTqpKjeQ?)9J!yl{PHC8V ze^SU+@S`gCx@zaZ!0Wg0#nepV^YB-Vl5HRL{kkG5MOVuUtENVX0}3rKm)@-uth4%h z1sS4ot@Ic%tY7J5`#zu@r>0t017D#r_X2!ZONs^fFvN>QY96iUmYBvdrQ4Nquv{HS zCI86Jfen5}uBiRnc_*AA3r{~9{W-b9LA)}VJ@B3@g z-P)$Q#2mXyxp?06Ikn~$qyGi+09xS&%cC$s2V)CGxPFaNB+aU{>s<cXsg+asL%DJ81M+g~ z^^QB~5k9D5>|zH8hY!ZvyQsFS;}OsVZVf>l?hh5)3$FMdMPAq5efu7(^fV&eaT0(z ze5(K~ov&xxlL^5I0Zf<;;#TjMJZ{k96ESShW)=Fif_7Rd3W8Rk)DMr6EY~ z*Wh8(`CE76Nv#@9FvzW`WoI*JCC$J*`F4vB^0HEyAZD&_CMt6<3-!(`Bi{Q{xfVks zJ;{2w3M0olXL<-pH4tfPxuxChVMT+zoVQqhEg326Z`dccM$7(uj@yt$OM^gQ^>7TQxbW4O-}BaPM4dXb7= znGPA!X>o;leYU5jClc&Vr7`IvC!e8VG}@QS;rdFXNGH0=*5EkOqn!$&CXUy4kZ)ed zu-2_9B|490AfrHXg|q(b7uCz)Rkm5AJr<|4`&2J}Hn{it&t`;Nh>!Q!l&iV@5aHyY z<7I@TVCBp7`zq36vv$5KK+onLTq!7~=5$=fuX9bd4%15CMOnnVtkOxo;*|0I872~Y zAXPJ;V6;C;kB%$}V~OzD89?|FJ;kIFIrCjw3HyN}#BSdc$M3x&3tcyEfpeL)&&W>^ zQ_s*iBNu)9OB$iyH(=9hps0TZ-GI|$-C2<7Uz&8E`u*JI&}(5^ePP?l*31_s(fnPa zq;)W^fODgp+;K;ht}*MYrtNyYMEd9yc2Op>V7_)vU%o}x7Yl^XeT+yM%26MKCg8Wt zS^~N|A|f1Rhcf*!Y#s~AApx}g6U8@b;XpG}=>tPaHrE1I+rfy&I|h7wwG;LA-g}ys zCfkKnq{SSFOlW)+S5;;FawJmIYWq;B!gC$gJEMU+( z8s;m7JyMW4apNATeB1izH=(Vp7fPE^k*rI|8!~GK>3&>MqcCQJ!*Ta* z$oybqF`~4d+S(@v6kaZ)Kr^Y>;F*9#c2PDp1rYoLIvoX zw{`l{mbF@*D<>v~{V51VI(}AkAyoi$OlyyW^foBJCbVtFL<4=N{^ngO*7R;Fc%>#h4%3T!g!gc-vCcSm*h4#!5s~!bWJ*r0rA}{*sC< zS`u3m!w~U>b@$sXa!i<=p?d~M1XcME$|Izqn|c(AsM`idoEq<1);Szs`oYyRZ|nK&_QcD_abUhGmar^?K%Ts4P?6xa^<{~c zqyu6MkPOE+iNfS-iiEkir9kLG`PVW;zLY5mfeUVGKzbRY?=X6|Wch47E}hG&vea}s zG=upX-~!r(En_x5cw3;Q`3a{h?F#G)RgqS-&+iCRlF`u0^g1&AOJmk5f+y&;sgVcW zu;3FV7vKzn#YWEB)t4+L8?|VwNgCa1%XZcDIE$jAKGn;LiNgi$5mxxHka@F;2(T-# z?PEU?^p~AJiRi?KJWdX2T$nh8Y}3`#BYVrJ6%KU&**!W?p)po&zU|@SR+4pY-eoo6 zb#n_d%+!lytPp?l)RWKF9v(H~X^%ew!}XVG0~L~(#ho|qeo*0Gx?NZa1t;Vj>Nx7x z-*xd;-*6Gst!SdUV~p9k>HQhAtLa%XByfW_`)S@ox~>OHE}F1B)CD{sr>KQD0;GaY zajWx(7Wpmqf~a)BLyVfUwsrwG7#4WGxAW&fgiOk`W(b{uLd?ld=*|U_l0JuHn{UWL z=0EK>;z~Kw72+J4U;TSU!CFT0b#E8H|3+K*tshfvsabNT-j5pN|2++Kt*VPNPIOGnG?l0D?$WHEai?AtzkKN( zul!jY4V$3!U%SFr0%fESohCQ=V`E;Igd0aq7hux6!^L&$>`vRg_sv~#ukItFdQTlh zn&vCSog{NL7quR{_|YF0LV@OgH_^0!9+WVS@i*Y4sarUHrxCr z3?3M_zLz0zqmFoRd#xz1ZzPta>(98{^8fUjcp zz-Cc{meXcfcbxezWk_bxF66B}nWMiG<0RD}fRx1YQ3o5||FICvL&vM5dT+7cw=YVy zbL;dx2O#;^7o67WGbW!2SG@ktS9M$82P`I@jfB$Bg(~l(+f?)2BXP92ICX$QmhH-|vvgM1Sw8v{5>xFz~*) znGUYWEO1%+o!YU)G>T6RYS4E1ZH zg;+p2)c)P;vEo)PdUm(#SOqiSE4eELk2X8#mD``)Kh>(f&$Lo88;>6VWAh8;-J3H> z@ql0pGFpm#08wHx@ELBQF*;yIxuQBei!f`Sr@PR|dAS9k0c-`~)p6Vg6SGcYJ zHvQBqVc(f{X%Kub!HRdhC#cwTchc!(?^5|jhdoh0g|rMW3|*hbp;2iUz~l2%@06Og zcadwgV#(r+vvZtYOF*mW8X?E^~yKrZd;wEBCHc2KB)4>E8C9XR+tUH?Ceb#J!$kA zNE@c#S3h;I8D*nw%z>dQ4%_97(OkY0S^Yiz(usQm3W!I|zC{W5(_e4Tn-*QPA~DKN z;i}ngfoQ38N88atz_3Meg>}v5KNGbLb-p8g(MP-7=wMC7@pRnZ>I950{LZn$N(IJ8 zFV?vbzgD*sSk~k(7ht+fW0G@&ch6djtPX6)3H4AUG8rD{D-dbMo*O2XHt^g6G2 zj>+b?&z#o?Ty?7UJ@hIu>e;icr{J%Vto4!`^}1@iU*#Y>_($0rxc3}+mY!d4hG>}Z zMrNXxa=hBHz57sRyv?|i;#Opr(xv;lCyK>#k|Xsnhzq1=NBK|E}#n#kp2zzx;fC|w>N<6p|quEhE(VakHPsrBQ$Sp zzLKyroc?eSKpmnGdgXW&$;2-P@T&BeHdF#_P_Z4c2ArnTcA2qm3qm!W<$Yj;kvT>< zv0OWe@a4TG%!{f&-VI?^v6~WiaTu)%T2rnB#NZt@TwmbuAf@upef`?|KS%!4zJ5LP|7cOB{Qg}uG8;tD z%R_dZ@-A--(T~*sLuN`Vdk*~bUiA#=F9fLr=utN5*Iy&oHAuhz8o2}^{r;auE;5on z{q@YRseg@}PEYoqMowQy_FtCpUq}8Yi+W`H(rO{u*Z0pev>cu|q>9*Fx={b|Mvo8- z^h~VF%zweGRbkkF&LU{DX?dKPh zH12mBYjqu#rde#Rl9^A#I>IIREB}OFV861z1sg6jI4EGsN?rTw8|OtUpN75i)A#=V zm^_YrYHRdX_+9<^?XrHf-Kc>-I4f`(C%lz^#nQg#eokDz@0gLlQlr;ar^K;6;7|)EJ6R>FLEiUuHvR0_Dm&P- ze&jm4td4iAS7`SpyDB#fuvOXKt!&A9xkn(Ey*>A7xV zHGDB>?(SKVw#-^)OTN<2PExOP0rH`e_P~{3iH#0j+?}?Und;wyl9*wmLWQLWo5!Mp zdCFCT{B5xXN#8-mtl?a^b^0i)mpwLWpWS<>P-1nb>Q?xtsRUf(O^JHX3d>P;FZ;Z* zzF^zwvRdaBtNk3qsgm<#Krg;swVm=vnT;;%%Y8X{7I;8LG6Ujr*n8Y)OjzKGfm+S{=-Dqp3g1 zUbb7P9~X1{lI!IV?6Ci4^b$*|n{h$8-rOjD4Ua!da}n*iGf;Bn?lE-ZlhocPKKGxt zbrooj*mRqzT3@N@PcR@_^R=>mGw0=cr>)MC0*w3YXKvYy?GX;id@MX##RvN}+BSTW z+8txy5|o@rK2eK}lv`daQ)w8lD3X|sjMYQ3pQJzGjP1ie2Mt~qTF7{*Za2T?rRr?{ z^LaKIh6}obZd%eyE0{!9JfKiG)Mj&1Er_C8R9DHwSah(#B$vK6pG!z~(Vc9KVsjnC zE`Ov6wjm^qS48_mvD6(tRm2ndg(N??WZkaVJ%o{^jVFB6P-6XfFDHx1XTLv??G8X< z)2q}iWx+j+h~M*P(g^!1+sBGm36~}Et(yh zsMoHpy(}1?yTJOLcOYix-gn}him&dVD#|%4S=?5N+exiAfm@icGcuNqw6O;2)|pD+ z#ZtQFY4KGC&Fqi!ppICqr7Y<8FI^$|pKjxGN!mGosJb6iQU4}4ca7%pUcVCeE+fp0 z3OrZu33zD77mfFYBiLp59 zhg~CYSjkQ|mie93#(1^AZNh_}<-M zsbRz-a(3P&TQqLE9-5G)F3x#cyPYLMlTJK!^f7wBf3^YyL3KFco-C~H@10R-uy8=xhP9 za8)pDGuz4!B_WM8SAzKcL%tf9Pm{*gC)mKayBe07MeLqM;l01BtB{fX(WSog71}M- z?VzC(jxeTa(Aiy5-|>O{=-%di>~!boOx zdPE1;locT(ja5C#s>5wr&lbYCtUWJ}27T7K`Op)4aS>TO|c7T#p` z%LeYya8~D5p1sdJ7N%De!&{wqGZ=_gX-OpoUDL2eTSbP1`@4Skt>BH zox}cXwNDz~cPpa)HO9L?#{eIEmd0&vHrm8W+0Et^SVRkLgtxWt9;(F912|}(hV@2s zT*Lk$fOz13E_&192V4}zvN%YTM>X42DI2%Za!!{oSTaz$FOrAklbQ)No-2-+=&7&k zkyEa|#84r%{Nr^6_V|tFvIlZzfgEf1MsmLuTucj?9xwYw4rU?)9lKEtq*s@T^5>{8 zpy*X_`lhnP4)f$);N(R`4}Xg=a2;PtNe>T}&fTh&qY2V`15Dw>>Dvts>qH(bXPAoQ zW{hq{RDl>lm3(;2eF1Ql*_N9%TYVN;1Y06usuoD_A|t3xyc5tc#?@^1DXi5jSX5a4 zm@-d|wj7!C9W@x54D`;`mI2)){gdXY>3fr99#K5`&AGB0$MtV|%l1$U)z=>GK?2oT z{S!rNz=+i-4LQRe29`;Z1(-Z)`;@DJ&HSPn!Kw%nXM7Kmjk5A2#t5E~{q@LA4ZmR0 zXO}=(=akxk2f(K!ZuyU|gYS`;CwSz){q^e_eiS4sLa|7+ssK8M(EjE=E(ovbWcHebe;5^1Wcek~nH6;6k53n9ez>sDrXy{fXF`cxJ8 zOHQO0kk6+*U%j7mz38=4jasqU+6GF;)ACeQpc&?K^15x6+g2JMW7}4|Qk~3qMId34 zd~a$*jjx`B=gk*Qs+HvHRM+A^Cja|1I5%rzdioMoQZ+G!xOeLd|CT)6x+{ivOwFA) zp_)`q`!!LegBRX`^F>H~m^;rrZ>ogx-j0ttmuvE+kxM78Pmwu}0lh#t76-P9+M*fw zC;!%qLkG*QOy|VG1|4m~CLm3Pha4k~CtTbkR~sc}D?U)R_X&LKRv{uH38_n*;6zn5 zeF4!v*1ZhjbR1z88nh0o2pOgFF?$rGg~kWjcE90$;Yd)*T+C_!}%`X+$GzTBz>ed?v9)c)pq7DMY3tFb+=J4i;4A$J2 zfljbY8CR>Kd}Pf))2z>Q5Z9gOUm`7U4&hM!(s-PT?~%H%BHTan>VT{B$?>7{@-X+n zwUezll{*jP`ezdlyK1B z=sM**I^Ub9wDb95x6EcMJ@<(Fq(g@{19n(lWp`f}v_kdwBbRgYPHp#~qEspXTI`!{ zm^flRsdiqrgg3s{O*XK|Q+iN78+75-Y__w^K<0o*Ji&&+?FS_6T_@-6@{hWM>5!Gt zBI4eJU4Oh#)*nARcrI>lZP_;Lj1!qw;IWhx5qC3$P=*~ zemj)Ok;+l~Zmw%4TtYe9^tQ&o_;kxjCPy+ymurwF!w6@C`<|#+s+Ry_O_!$yWLQxE z9%MNeWanVSBki5HpoXo`*%#UBwulH!6F9_`_4`3HWzDwdd*ci}ascv^FracsmAta9 zVs|(tnqq6K$~2ssN96g+!n=jJwYfspjYUE^7_ah>8hPUsyd~!9T#vr zI8@o#5V!xN)6k27P6+X!L&guPBWpI(Zu2G_loHFw&fWmVPEz|hRc^_OtzL5zk^IR> zls>B7b|}V{UIw|U4w9;fsJJ+02}?e`%I88F;F1KRH+I4fX>hO$eiDKT9#{$s<5@d4Bq+2sB)s=hKSu-)m^CINh?*IvD1d3@CA4JckoG857 zEr8_p(vFZmT8wH8V*Qz$@qkac`kQ>VPQD@QvzrHN zGh|o$YTlpHNwZSxypr(HD)vZ0nMmfZ@^11B9Z*(8R33uP^LrP7kP$w;xF#*`Z#-Yn z=FmDlJxVx^3T`o!*+w%`YSv97x$$RdegO`#wU{1ZI~F-|*q{mpC#nL#1R)pr{kHsQ zX~qGD+kFgSg+ldvUB%kwx2+NWC*BsfqsT(+4>$!85keDjkw)XFe6W4yNF_HgNutCLa=P;PWl=M%`+q-f~r;(}ib%#`!^Q1EDzM;v(H} zU1`Uq*xy6vx~4lrWY3Yqd8y#E(g)W)zIyE8;^&c;T&9!N4vCd?`_{1=z9tymQd7m| zIN}P77>sA$2V%eX?c`{s$h80S zn&PwD3;hujRc#^6sV?IdSE)e4L|RPzHyF~#yTN0(>k0&kpIVpoX=VG=8O=otbZxBI zs0`ep!6USa48e_#1tu2A0OgNH8Q0o3z4h+fu>vr|ofkA0_{*VA9L+Sm`qlSXN761l z@VIIu3-x~g%S`=MgHZU+HFZzru3>9DAVocuSNCKAiJzxx?VV-6Z~SDc)_Dkdp7$ze z@^jxGn%eRme;~{gMC^RJxU*WB#>`W_uRVBsX@58lxD{S(;Zr$Iaf`N3RfD8 zL(0Rc^z3KlY~a}>zCJ6zg!Uy8fRzc?_6YcFhTM<~c%sdSt~Hk9jOD@hhW6Qst>l-X z0qQSCyHJGWZOAl(Yd46%J&Z50+23`-rb^}*OY42K6D0BU0tX0R*s6i^N;98;I{hVs z9MvGPKO9o85;6+`TMR8IeTNQK_9Z^gHq`_u|4L}>ng543_`Ox%{W^^1t8Bjy_#`pM zG$AHgP@5DCHSP#!V=-{lx4TUV@0TG;qArRSj~aT)7yYhR>ts>7^z%icFdu%kG74m$ zC(!rAzZV;|s0EU&IKBIJUEpJ2y@Av$Ovy8sS6fUGf_CWoOJd~OuaG{3>TUK98+&&W z?{m27u9tMME4jVTR7o{lK6{(w?K3O`pgkvH9xv7O%^Qkz%I;v#y;<_>0)Fyp^2z6;*YYY)BoAk!LF!3H zMrQZS>Oi`J{t<~lJm0j<*RK)Wz4b({IK%gSnxeYfUvl0Aaz22caa8ExpE77}_mO;> zZK{|38@ty=Q*|fR%lW=!{D1oo$nP@oeZizF#yiKA zn{|3@9O!L|xW;)J)-N75)Yl$-S~HB}H&F9^cb=A=RnKiW`=vbdC&Lyk+?im*$J&1_ z+gbpj_7=oFFBUg;Ycs5r7$7X#c2oik5EK&xq?9wK!P3GKUtdzd&M3(0nu3PpsJ9a9r2wO8;Rv7thywZk6N?vP+Ony`=_?H%#;VIy< zfWyuPDm*PzL270?rQX#jy{AC|GQC42qo|$`Z@X_X_;sbQj$a+I1tyax5&*LtU&5JZ zDJL5jB}jR?=*Z3inN1}gn>!I4RNiyH%zlz+-t0#% zk@92Ch@PV<6wnBm1c#pBi4u zaUDc1->xTv4k~fSKg&T(bddPbA1WLO4V`kCSZ!yqqmpY_6vCO5TcZSZ^1gCF(w~L} z^DzZ{wOAYD0K8`LXeQ<4Wt!?$v3I6Aw&tMWqJQfme50>J>d`j%Tp>qpu6Cu6@`3}l z*?;Ny3D5;I8Im_wEi@5EM6vKEd>MK6cI1yGK+0t~#Vc(ARu6o(EloscazNCxl*9Ny zWtt;MQO8j51R-eD>{lB4Q5~b2H;kaGUQ7!~bL5NVH@H^UVt0gi*L04# zb!tm=K{f;T%TEQX9#;3ucmirI;Ide3-1iO%Gk{^@FC&m%miU=#V&+R+*-FvF?-gSlrNIX z37}!SCY1mPg(F~x#|N7wZPEZN9=Oh=h42R#5=*=Jd7QJKa2cw^3(lp}Z2`s)hh-1v zYFf32ULEka1OE7d+}xyNf0z`bG{DOMz^)8{QzsXR>5X3MK3&N5k6+TG&TzRLXO6vo zRpn&UUWIo~EE+sC?ag(hw_1^s9sQp*ijZk$;>Z#@be?czrI!B*Q@0HY7kst>X;ovAZhobns5}j z-$%$?9h>gA&PRROT=fB=EZ`UGGM=DgjtbsFE8_)XBBjc$9-;oP#Csio=4$4t(drG+ z!hzMbF(ryQzDbbS7`G~t?`H&=H*^LKTvcH}LckngAt~N7kghjr3+USH;7E;&iw|i`>_UWevQEPuL02BbP2JwdimnA5(*FHgPO}Sz+ ze3adf5L@4FMLb#e092PNtL$t`AZ-OvnSmbk_YBmuDuia=fUrI)|!hq-77_X2QLS~*phV>QK-QW${7HCJ@{2o1|4WK zlD{LD0nTfnla`$0c9c(clE-q+fKsBN`yAZZ^=FK_a!?BYTXelU0G%jsKhm&s4}g*x z*gPm7$;e|e-kRQ_f}5wf-#LnGH6BgvMbOcxI@x51{vyjj0>3AY+VKltnWsvIqr$@7 zte)x8inx6ZQO%iDwE)rE&L>c!Yp|$w%5i~_S82>zlA_CkgFF4|l&sCHcFBVm;!Ntlt3CwF;-l$Rx?lcQN~@CjD5 zMRHg%6>+ z0ERp{-tI5@qDnsLGGSv80Cgj#tEi#?c?H^Q;N(vVFk|`jn5ruM)?dv9Kt&%pM0h`U zc%S0AR#ScI0O!Qt&_gmxI^>cmw}O3-jUO8}75n!iQ*{R#v|aP?y7xXDO#+098Rq5i@jUf;swUe_HTfbu*=-Anfe=Ii4|4mg*4NmhhGRg18>V391d};adVL?OjjN1g81l=&u zIz~qVy*$ifn}!A5`55y4H?42S%PmSrK~+{-K907fcTwJ~ZP@woV<7)%n_31^PF{tV z+r#6=IpCc0H1o7TS`B2EDx8)Eon$$!IoBS%6qF@<(yz^{TYi^PE`#^45J@-yN_^Td zD3+tZxiI2g$pKP-Y_`VmL;oprl_K@f##x571G+C-|yym^2dI<^j&iy!gwp5w8r9R+O~0Q5$u}&3 zzqok{Ql!o<mtDLY^ks&x;AuxLj znfE5_0$U*Qe4`SOCXn1QxAq6Iy#Ay|GYcKw7NArxHGFZ6fTi_))^LnpM{?-7^(Bb` zQwBUNSmG~qe4rtIT(FvJryaYPn{wQH7{bNJit(9b7T906_e^Z>Lt*7wQw80W0$asln#r z0}!^#fS7#9{RcpWlQ`t@To6}^+wKo-mWP&3Ao~`3pBCB$l9u`)i0R<)J%(!=x*yKP zr27{L&8r^h*I7%&3FuXnj5*-`6srTL79zzXLVIk{IZ;5+n@PClD`Nf zw2C20nVF}F+I4b}%koO{cd2gZ#WitOD{7;3B;8+vl(YE+9^|xV?(V zqH4qPMCNUJx-m&ecf$M&E8#-DT9-DQ8D)nrbL`dqrMR;xtI3Ws>3_eDR1G1u9+9dO z3=p!@v;h#@O^x@sub`KubxGU08EVVA!`aGve)N#+>2k^Kp6Y(W{fCxrdSKepz;vtOPb|g$He1)u({Gq&ZyzN2Ab2-QcqqJT4*SJg>%;r2(uob zJ5uY55gAi}k$lksWaooPIQM81LiI6IvIe)ux&Zk24Z>(lt1wI5kD@drWDkSk@C5(+ z6i$R{qRZ3tu12K6?IcYV(nyK^0yqTIU3bRO_n%2$104-{?;W{i&*@0P=1xq)mvBXA zNe>op)0QoCdm09-KJJ_CdU%23c!!+J%WXdX21bIo!Q}^9BNYhIB$Gv+_v}X_Qg%*_}=zJ<+ zgk|k;5%6JTh+mVJ{5}?^Z})RQ z&wW4N_kMr8^W&!z*R`${=UVHG<2cvm9Cjg+aNO>zGO{Z{=xnr09IiScPExkSZ%Te? zh5tcdz)0C_3CctEYI;YYZ8^2Z*A&&tll$Q+YhQjp^2e(kA4_)Ery^LKH)>q= zNlNhRgX64Z-wl)0OHbedRRlkOHg53RP~n81&ZphvBKOoM!2~A7A^@j|j-HS4$MVLdS zvnBVUnD=dn(xX7*aqA~XEOyZ-_qHJzIT?M~_`6!buelk%iM1RA0Vw0FSl z#GTu4U!MN6`psG3zFLpYBLAF{IREu?Zi4S-R+R8=XasM(iz$8x*^C>eaaVi z%J+n3+ATr0Gi*(-<(fNKnX9{!KqT+XkzIeBSFSQqCV|kVIvPfqDkMM+qX3Cf z?hkwp(8sK;%xpgod$?!+8nOW^b9Un>j!QUZf~~?>luX7fC9(AZtbzqnXc|tX9?kjz z+(c9$0!kP$b!AbA5O-u5Qvdj@q)dW%ZFzMII6#bECFj^Ei#pBU-q=ppE7nqmo<}D{ z`&M_oH;0f1#SG7BnE>v~%JdL_gb0ria04J8+*Eg-RXc>Pb1e%YKn78{XM=j+WYe1u z0E0_t>jOkoy|Fo?5LgLp#Pa(Zf%G5H0PF z-Ow+!cO9vZUeLsV1FFDneYqt`(WHBL64cZfafiDV>rdDhzPlXDOZl zivH&M%F4u_(Cp@iKC;A0C6&!LB_6+6(LoQFLOqrro~PY8tvB%aT`!c(22ZUBxad(C?-3uitw@ zfQ?V0m`DM>prs*kg=n!5`-pd=GG64&^~68FH^j!;XEa8DYl1fByyG#mhACM7Y>r+- z2JzfTXrd)I3L=Vrp&qk@{;ctdiFgLvi8dZ8ZzZ|`IY>8kDYw#c?9*cZ zzBxXd0A^_{7_1Px6)3on%sSDQ=Gz_u36a(kcDnC8j5JsxIXWlgB@{#RW40Mlv2QCxi*jA98ftn~OH)jUl>Q9gN z$7|p1&gEH&uXJ!BRZGjwZOZ^P#7A~N3KeIFnDuiE+o>Vor1hUV8no`23xSha~t$`6YF+Q$^!9bXm ze)|y)s(vk|V+w%ty%XO)J+|aTajCz3D*X_qf#CWm{OUhV5rfy~`8o3*<(Pj=iY86% z#6itp(oC&{SVR1*oVFXnsp_;Ljw78Q&_ObR`q$Vh!z+;~X_kj;20wZZ7mpr!ZcvW1rMNoAs8D5acZBsHmubJ3)kIQiM8+oU(sB>#zpq(HX;v&*a^XFf0(Lip)%& z?9Ss`Gn>~CwjTP@<14R+q0j?5lwT6y8ljIjePxeesqjX&eSOBhvKue-g?nvPOWE5R z8;|H)w4Ck=hU%%_@njUYC${bg$t!dM48k0#-=Cqn_BVoX6x$aKAvjDD+YxrgaKINX zv;fiB_(FsT+w5tc#ZTne#-HQCgZkt_LJ7+dV=obW-c#V}&qf=;9844GgSa~VT=`)p zi4U!4R;*t&xbcTqfIfgucn_!^G_FxbDv+c78ld69N+zo(rG^V`f}QiBw64UjxcXnI2lW z@e;;+_Qzd!(X8Oozm#ip`y4b-MtZGjbRW#&^t80P;tgWot+*_h3pojZ9wBG)lYYpe z|Hea#Ci)S~6siyk4wR(JvMWG5pr4_>l~$C_R|7n~7%K4!07H~M1bt-h0V;-#4+Xt+ZJpvf2s@}HQ_$_On27=IXLVu3icliiC;15NOEc`adyY#q z2Arzk&mbiYhlN(tqJ{7J6Eb>=jPswKpO%q=0IXu9P+C^j840gh=2Z1GpBa{ygX_n4 zHWyRnvF^N?L161Yiy(+g1nd%y1?=u(T9b2P+SxXo^5ky zJ*L^|&BMBwM_lP2PB|&(f19NLI1s8|%l6e9YAC!u3f=7Io{mige1i3&2m`mc}y~^1?VWC-tsl%W1)t$(%0c6{Tj!o7n{>MLn- zO3K`QSj(IaM6RSvZDfAZ4?-I?;m|3xjU64MgRuM09}${S?Oru_jA!vhqRuLOHz7(P zr2tPLH&~E*AI&U8lzW%FINEet?8|ouWX*d+c`2j}%S~5)Jc&XF;wdTZvvK5ZUrsK8 zG&Gm8xpibJTK0R%Z$6SzH}I7m)ojYggD54X!h%1R`@%FSBlEd_V}|NMJ=K4Fr$*?+g6&_~9WAlujdP zvIPZV=AQ#jyB_bqbmrI@M=~Zdi}T7UO7GvlxAz(SvM%jG{>Ak3w=<5(wa6_Y+RDd{ z9ecYhRx*1gWYee9RB-2xKhVSNE;3}tMH=6v=XeCZ<#ur4KX!=e@qVPPt4n7RX?1O% zw`z|aWY+IIj=3XvOkyATKA{J=dGmAU&Ru#!mZi>R*MEtOIicz4r#-`hnY&PV$%_;1 z4b^X>JjlPo^B>Mblc=>Y1-4@<`ubLk>e$lCV^TWtM<4H(b-1|)b60t+EiKZGrP{^I zYxXFEKb?e<&mM+X*1odx^8O*}tQ_9a+5T4^SAP5Sai0()+UKf!PWS1O&_4krs3s&$`?Jxaf^n}TX z;NOjw<~{o{Gh2g#A3F6_{2_}6)jTNPj_a>`Ga4;^YkzK30uT6dqqv&>CtZ>cx(2& z%a2FHc(#cR(d_d^dbpe!I#U;ejHLA!9x@8#W*_ODUMDiJFpe~MU5Wo>ID7Nv9uCyD zN#m;v>Ow3>pZM(Ddc5P05N%AqONXq>-7?e#l}lXgfpm$U+DZWO$Znb6JZi-Ez+AU`Xz%9 zMX>C>C`J54euZ>(6Y`KACY~j=y=EMrWeVZLHa)}IZ(}0!BCT(=-j9Eegm@d)wY(&p z7emg0T5Sh){FcJ7Od!){ssrF#*;g6CYHcnMN1;5AJdqFguNW%XoU-CV?Nf9ji*c1d zhXiHroHW=C+L*NwO~`KOE+1aho_%B--!WT-`1Qgj=Q)TVU&B@sR-*}Ky^Pc$7Y~#r z)?P8RwklqFiMuuXqVii+4TdtJ3gE7cJSJeyh1|d#{2|1)fNlMvnvb(4&i2z1&VcDo z!GpR$CowE|jGFqfjIvqp#nQ_w3$fc0TJ?b3dZPBGh{Ar=uV`@nF0dXn_Qi$irL6K9$Lf^iKi9z*&TGJq@9=q!{OM zfJ7bGW|+2yfH$lA2LKoK!EWBK2eGk5BoGBS3Xko>+sy^Icc7L>_8rB40+1=rJr>mV zY;E$IP(EqfQ6)#&esnZ^1NKkn8-@EGCo$piXS@LQ*$WV7FgGA%Jc6L$z!_oDots9@ zZc$fbU3#Dgj5AP^W8Jq_`7KJ#$u4C*rlNk#&PS=xv$48R_L|oC$?W}DKt_D_rZhNE z7cHJgjZR2Vm3Q^wYV1G+Y4HdEMy|WOG1#k&%|3M=O&ikH)%A`WWl+A673ju|rHT0B z0LV>a#c29my2H#;8-RL}9=zrPabf=+uQFq9l>4%Yd)wsLg8?pLqBc1=K32yy@Lh{~ z(pz(ZW!aV)YSwfYLqv}WXl35~lq9v3;7on8Iin0id#d9 zr8M^nX@AsT(9m_-CKd^xo{ce?1jRFdzM6}YCZtXN9EoC0-^(mU_cutinHUOwS*fkP zcB^$h$RuA|98<-jjUXN@4HL>*n{@Qpj?xN)a2he)@2?qJb-ofhbI}tw|H<$aR){m}!;- zm<2;dr6dTD7l9qy8M5iC$_L?^E09_NjB+Dgx9}`Ywsi#cE>uDC*VfjM8_Fc+ABcqU z(rWRy-;j`PW`%6(K?J(pQrHl7<}@}tlV;$|zS6n-hdGiZ(udvqslrit}Pl11{z z2y6|P=)!~u4kZ}@n3%7kv%w|l>(0o^(eZ+u+1d5w0fB~A4gF}t2|SOgd^S2^%20d@ zQ`X|kd*(*P4=Gw(TeC)?0Bzx$VOka;WvMu9*`2!yedwD@k`4*)jz>BGSD+mLj1l&a zXFoI@Dm|7prsb}=>u_SAdWCkbg*SnXD$)v^?F3t8N=Al1E%V~3|J-*w+e<|m* zWGVO2nTv4@RHkpLBxi<=EHu8oJ%G&ZxVSjC_US<2N+);&zp^D?n6y+@tl)y<3%|e; z0EiQ1CAZ?i@gd_ryAGDly-uT0Bb`z?zG5mK#HCiwi84?3&Y=|^x2TReA(c+apq)(tLB^xf1VT)UA?@9gcX>*>APX=BEpniJ&W`8LZAx=eOt zb+&mDR_h&bjAR27nM)Jx_@w{Y&V*^8 z(vPe)^^6WZM>hp4FO~-4B4L=*j;P89IPnGIPlpKYR2Gze?ns>Vz-Mm{_g+_kNxw_W z4n|CWTp>j3C(s-ygM`SzRS4J#SUQPXQaw>Z`&Yzn46&QZiGX8vv4#JDjdTxyKp1+7 z;KudTRM_yX^xJ<6-L?5vf1hGEdsL%6E*9HQs{bYT8K6|0N5q@$`D@xS!G?V`?H%bK zZyZH5ip*uDSGf5~_l?RX#Zc}WL4za~T{)cnFLka2vnvL~et&-e&AM{sc2Qg}fM?3L z&vV@o6zXmBV?KFAn(^WlfWjEs?to!DHR}=7z^Duo^pN3&trcp3fF8uiHo?Dkw>)cb zHR6tSt;_1Mo#6tfT)sVUR!yuFCWk}y>yhsddNBaNGsi@Z zE(`j^R>~Uys3ZNxKC&`Qh!9D^p&u0!jiDur3B835>_yEAunitx&?~91-&?=4Sa!L& zd%H$2nFIJOZQYn{u#1` zg2QKQm+Nn!r&&9eQr-3|mas*^oHHttGNd~Re1T5b;D~dB-C4;)_y3w(y@BWJG?Wql z%7jlf(tkNJonWV~l&lncIA}ojf5%aE3v*t;`B!@D3;4LNfrp`C#(JQT#Pbm0upS2G zIy+=uN^+4})Opc#V*`KRs$|vJl)KaZDUYpk6^wH2&M3>8MYjmZD0^p+swVkfm&+3Js7ZZqiB5O-NTL2HSyRGe8^S>OWN-wX?XtsN2gNDfv;-yOAC{`N|Eh|`yF*`HdDu4yqb}tZ8kFNF&6%` zhjmns0EQIJO0ly=eF5P3G3j3WX$Jgavs{xuIincn>F{yz0$Cz4cStSAj9I*;mfR@t}-8!%gOWJl1aG=|;{QB@dj+(=6SSKL!j5kH{;N=(#Pm zj_xx^LqJ3(I`yz|ZfdwMY4i(a6wOzIS2Et+{d>5{KoU>AV^$G+tm|3q?Q!9fIe&HN zv4A67kbQ0p_oJIEB7@>Qjf*VGArg#Vt+To!O!_$gUXAz6<`Tq|r3SA;sIPF)>qefO za?0$^_FWq;(4<)AZsPKm*lN8p*{M2&3(ELQqHcnz11c~Lx8^J?C;SESrl$cjsJP)c z0DGMDa))7)QDB+7j{D|PlOm*(p(L;!up0pM`E^T452@~L(3MLNhV2~!O*C*Ai_%oJ!Ciu4J`3ALv!6@=4+$?)?XwJN26V{IM*Vlp%=0zbf z7DXjUft=w!beh>43kGj9?AyFmA&Q4$9vzM;x-J3=Djd_~eysN!sXnn3s0S@~j}XGg zk^QMF?*r_)uq%d$?rb!m`|E?C8JLiN2B{PtOkK{rwFTC+C&8{=H{reP;m1k0WDF~G55`YO-=@5x zke&3prrjE_t64ebrmv}qvyos)R1(406>Lv7X|3vu|+_0 zP0@d78bymwrjuBWxQC-31lgUlsvXde_EePUdG$(r``Y(np@3iO?w???(y1pRY?|Dy zGW(A}tC#^gPWfovX&<7d{IdGfhyhDyOn0R9q@9hb+BcfCi2n*=8$kNJLH?X#-($p?!f3K%p_4$wH_iV**+<0D6(F?JOqllNOnub6 zWDoeskiHu={n@58ZQ;}eS(<$hb_%fBDIou2Q!@42Jxr+I+(-$<{5Un zMWw)DM(WzsS<=(qikeC5}+qASP&=PcYb=}v$o@9{h+Ru$t zNX3VQVZlfn6BB#3{I_q0Z(#ZsVpc>5p+D|pv#9lwUz_JfN$Kts-0Y7-?ml%2B8WTx z!yDwvrw|?dFK@tQx%11@UvK{P5)RZ(88`^0{`1XmrT=)$|KgJW|7xN#R3v2cT~0Mg z>YPDo*8M@O$-rmYr0Y&~-KS3ds7T6n*X?yL4UN20g;xSB#?H!MG!);Ygtr&2R2LLP zpT6QDzo0JBuf6Y(51);>pz%Hgw&LWRx6BeBj%*HnQjVAUig#}y<=aTJS(^H4 zc8yTdpJZvp?Lf{z<9@Qgm-q85UFUmq;`x!qNvGa&T7tMMF}y0cyJV<{41@rURzCVX zNU^1E*dv#mp>5fye!OY0a+g zcYG+MYC^-5Qj0F&4}3UDxG}82Au3Pn=v?YCKo+GSr^aY}a$W-;&_=p`;|N zEWR^crSVr4RPLgUv<(MO8&@*eM7% zpHDAfEwS)Px8QSVrorqti@c3!Z_0&vHfBgUAqmHKxi`+v&(NJ6a$uKto{HY15fd&l zm;2;NP9CdmnBx0cbB~7Wv3lJ@#fR3^)I9T8@nwUJVP{;f_#cdB!8}bQqz-KzPI~ni zrmeCv=JM+vAKB79WL+h-nVW8gZf;%1qeXqi`ZY4jRnc+<_WpeRaZT~0YlpqQ9}&tJ z3=}CUSTnqFr%#oV?RBvWGrdSAnmo^C=!*s6>hTXlLAwtH&Zc-KHrbk_Egxg~Ik|jf zBVRfZX8X;&ECpS#Z-Tf`{pTu})7N@uwtBAx2NAY5X?2dgQJD9NY{@pUCG^3Wu+qy3 zpG&3O4vI;nGxMLA3U?P;cM#dj_;-mhd3Ql-@ewxU2~eJ4gJfNMTAgHnLtWVYITr#0 zzGmw)!9zmY$=Wq?s{nlcV$1fr_xhZeW5wRzV;%+p3}*hM|JUe1Ou{alPcibPJx6It zRQGU#8YU)2(}=lE0;Z3188@i&uF4~CrBvaHA)A1Cq}%*j=H zD6+4beGYPef%NQhgo_gTbR4LOrF*FTlX{wnRc-rWv9_m^{@`*vqAv zSn;~MCvtBwEaGfd#zj?l3$f+ohs~N27_?t3J>Fl*Y-E$@o6v)uW>vpyZpv|4nWTL3 z;qCPhYTIW&cbpg6%4VV2SWnxvDMA2YIAu~pkS`5qJ(j<}MOx{><1u}p_crF+aSWbg zZ7H#%UjyIV%NbnraPN!)mM#6aMzE6CY&ZrBG?a)K$vRPm)YMDb-geogpt#Y|ku~Q< z2f3y}`UTjgJlR*ToSKR+njY}AGBq$p$aP{n@@ZBSl3k&{_2|@+0y_bi2Ky1mfh#qo?e)s{p%rD=|`wK?2FjwGp8*pvyB^8 z)I5Hzp*yqGKs%qU7Jo+EhUts47N{o6Ilk}Rt-ErJXQ*?RTI@em+t)Cbd2OpROj=Bb zg|jdZ7mrAJt;D!*h5wmE#r4}9?JB6v@t^m7^Tt|VxEQ1urq9<{m+~OYMLPi${I1QrBl~s#CN{?Q{RE=ud(AmS!#XnQ;tV=p9snKcgJW# zo^{tt^3A^D)zzzCO{o54LtZhu*o}{#!e}WfM$zGoMiv^}&Bnhr)Q3Gk&8HA<{mE&h zd}gUInC<;$e2=8{?G5980m3qit}+h1Jjdrf0zw^oGLL+4@ni<9^o zQ`p@+P~s*XI_&&3?kbtY3PTvV(YF1-%L|VO@`~PB5m*{b^k(|12p>Jl2H%GZRN79w zw7(EzfBe^yI;@U448Yag;O<1$N@{_3JPY~Pf@6+LOHn;0Z*Naxe8{*s_LZ|L>kz93 z=AwN7-Sg#J>?PZqfdTZC(pIfWiSnev4EOHJjm=LxNXHIKI@wkD<<#`e$l0H((jCFB zD-&R;TOG~nS55t@Z>-F}TIVCn^ip(gb7os}z;`9YRC%?>kDjOaTh){(U(AQ0p6@nxk9={r&E@;I zzZ*4tl&SQ)=0ZXqaIE#Wb=a)sEbKTl$3)+uHKw_ma6xPJ1`CaGGtw)LA%1K2>x z7k>-46NXaahF7{wY*#W3oH$XHyv2h330i+k&1J1{fnat-XDW`rBFS%kn?tU$(%+@L z>g`YU2GQSl^c>_3q7m%8e!cnYm*3tX`ty&S?jLUuRc`0?>&?z)`0pvI1x3Oz+f?C7PZQ#(aUb);n7PJc zN9Oii8?mLnuAr88Q6i54IE8CDUJh;#YE$D)Pq(-Daq<4^9ZG=#a-3K>GcV*P6+5HW z2fLsJSZp;FA#2A%$!T9<|7E)6_^52WY?eYNm}vmCPVB9H3K?MYYyoT6s3LYtxk(dX zcG6=X{({^6cL2Np*O6!n&(kYtEt6yYd#Nfo3nU%YL+_VEf&9E^L?!i?LY?X%7ffCk`{A3p2kGIMnehCSg zLJ1Lu0Qb#{MZm#1%mC>l#8iRX$m&D?6ToHzJfw$jkCM;3I;&5Bc`vg?vIs(0$pMgL zE(ytiGV|gG%qGMlr9*mw8~~wa9R>4%#!o6M1(0m88H-U>UBK7?NE`CnyvwX!(cbmq z8Owt9J*b7YRq=ZOefOO~?*)`{3xafppzW23B=AQ;$}J?pgOv5qRo7zbt(noLcwN|SP1!(fylxLNnX;d+dO({xXM|DjGW3^!&140Hz^_Hg9mC}IhZ z0E7d{6(+!yEbcga(>9V{&AFv3HJSUyJC>z;0IgsCzK0t2v5ALtHMnhfP-^8d|b8WK*$Qh;LMZnR4bn}*vJsPt8 z`6!hz)+7^8OzqA=)WO&aGHh*q^@q%i%Zbv5D0Mcz_`iT4+kATA^g&kk!QJsQHo13u zkE!ng)Q`{MC!Hm3E?xf8vk%EXJ47n*Nc0$e`}lB56Z2?P&3!SRd5K~73wh;yNV=gS zTnKR_fxcIbEnC1MDJ?oiL#L1ZuqnCH6YN6!pu}xG9kM5^$?4d}gw0x%y?Nc4m&}tI z*%)7H8Net`gI|aFR5oF0YI>DOF9{ez(fWItfDgMDNzJ8xIfJV^XnkpdwzwF;N_JZ9 z1^rJLAf|2)5SFB9krBvOpa~3;uXP7k&hA39h9ZF>F)`MvDS*>BSKa`&gXIPxH8nNQ zqBF=)9U;>+RAz7WBs)5J^7IyaSX5kq2_zRalTpyAtnd2ER8{zER@pw0Jf;jgBJ_ zr=q4tZQO3*#YSV$g=8_s`A;U63e7>v!Lw2eAnQq%PDvyorz`W0Sl;OMSfgr76735H z8piij1nq>LQ5@Hg@F{sll1G-O$b~ZKrFAoF!zAB{8@a7xQKHADG>=XI83)21Y0Dhs z{kfDuyt=!Jwr^6uhT>o_R`8N1H3Be_1S|niA>e3nUa^V$VfjevGfRJJ#uVne_Ao4f z=1tefOO+J(Y$veSrUAX>z=Aa~(3E!E9sq3B0e2;gs$UE#emi~s25GQ;SV7sJOo?+e zQ`D-B;nMvZxv<8VVh=}dp<|ZInsE#s&PVz@X756xK{yNOt}}3v;{9iUi;~E*4T*k4 zffa@^E?s15I{}m9z|0T~Fm42-`MY060gNkfn?}W>W{oUYFKt7JLBueQ8+3l^( z$W6b}NcYVfz+&-_sa=|RdgnZcOMXLbE)e{_9x5G|DjC1F{sbja4RJgNV7hq(@;SSx zwkw(!)Jl|GH@{T7!K#BPH7|sjr#-ii)&1!x*E+xO`!y%X$jKA&wUmmm$fw;Tn>uFW z+oqY4fA%eT1Qus!XV;`P0?ZdP0;gAO#rBD>yyyg=eKf_sPcNF6RTX%q9M&AEYesGG z-O3(20Za16+eHJpoG&fdh8W5C7Ud`2VpCB>sG`5|3`Fkirn=5;N$$kGrZ}J3H_Aas zVN|p!UN}0v`&|JEX<&@nsIJ)|%a0Fs1xn0cEj@iv0LvOQcXKV1mFj&-BKy=NgeNfR z1I)HX$%4gHn#4s&w9z-Z4@t*US|cc`)Gy!;L>8bBuxxN?X{&1(_$bPA7);+?QuVLj z_i|2=Z;k;Wg;gHo>JBBwgy-Bsl5Qo+0Te^k3#ebNP$&W@?#Z z38gtjr`asxV3iYoLvOGkobzJz*#u60y(E!WL z5#fCowoY|EIHInprJ;J6{<3)hpnq;_8=lh*M;``!`J|bNRCD_NmizzZ7|eDK8|uFQT@h=vmf7EzlR#zA6}a3~t^5(^)-X`na5>vKU00kmZ(N%`@p!s~ zD`o8r3;&D^-`FMQwMGaDVm?$>%PE_w6QckG1Qi_?ag)utg1~ybcu-9asy+unv}(&< zFv-uDMH0)SR)HmUbEdDDetT5!gBc!(S4}z6kkQYA{0wB=C6A@HJhTD~qJlvHFWX2I z`)PN>it>4;doeTo>Y|at`~;h@HU{`e)NYz{86p?U`~rloagkkX3){2*upbRWaX3=V z@SSQRscs1CUfxnO?pL`0(9&1D4nT=epfJJxqpJC^UTYUuM2OtKC8%+jj{j8|O~`1p zPO>M{=Y9y@cmE_}LG37u8Y;C6O5Y)zqIZPitku#F++Zg(02Jpz2r zT{ncfIZ3q(Dv(TvIhFSUWUN+j6jRN`zzo0#pAR^vzS#brzdx_0HufHtbeN@U_uoz>%eSJBZsfM`9Wk<_l(fBArJ;5${W^2oH+dJ0MTy850(UJPt-7BI^-Ay(#+U6=7Wlmw^|y?E3n0xHei`xJyNm(U2C^2=mGznz}Vk z{cRR*qQuM20LK6Ee)533Q9~-X&a;xXxCpkch@^gj-F}tl?TvWLm7MH4n!83QDYI~vnHtG;SPz&|0a}`j?ZQS0&qh)x7mjCJ2iyI$8 zNkW7cLT@X32fV&6UX*bU_jq=|{Yh0EkOql23|b`CDnXUIY?4Jbkwh)ChX?1TdLJpQTW((&2AvVQ$Ss}%-*I1C zUAQSF{i1<(%tvYso#&M}3VVA1n-8HG-k3D7b0D2fQfO*#!&a50URSz3d&ECXi z`$#OX#}YtPqUK)ijb0w~ia=7Bu1@$6&a8Ofj~{t9MHlii{jMBPsW77J$X7vNY7hX+n^$Ysefko7{C)At|L3yL1B`vl`rB1qg^5$9_EY66kr} zY^@;QCN6hj^=~!t6l%3~dp`SCe{PDH`}XdDzNBXfW&?NDarWD26(G0-wF-?~&muE* zAK6pACQv~RZk~;6Hr@*2Mm62g_qoS!#IhDN1X;DJiC$TWEk^gMSVJ!hxMa7e=Lp~9 zYSX-68{U6>fAeK83Yz?6=ngknMw7y3{8Sa$o5w zFJbn|W7%VzPrz8{o%B?K_;kGG8YmGX=5`CSW#l8nVGST-CnRFO@&KQq?JFsnLzN?a z6bob)^Pz4!Obc5c&*HCM%o*5SW+@CUH?2NA5ojUos~;eYjqYl+#1)!wKSg@v@R)h; zF>}Zm*JK+$XS)UaE`T=yWkWJk1r|qQ@|U~J^q@9lKVwCd#Xul|Ty5_Aeft@@Kw(Yz!3UoC-pgq0G@8;mkpn__!6+JG@wQ;2lKF-yV*hOqFrIW`EUb|G^I%2~|cHtxq!mjOZqxH1FMhqi?a zp4@NpsfIv*=^D4Qx@g<}KfpEj=FJ;qMUKwr(gdCHOwuOt$vi0g%?ctk>Ia*iKa8{Z z%tt{Xi&^Ax&@6?#=K~ItJ^40^qxAYNFt-p|Ys>;vHF7$KrtNi#ZoNW`M7S$HW%PBG z!CqZ`{n){m{Z*9>Wi;2r_)j`0lPu2=nDN(dzcIcX`pL}MpmKNdbtMc{hz?hp>Hf^G zUu_^qUt0Pt;{bC8Ir1A@+MKjzhWv+)6(X zc?}Ydw~2JtC%(6vXd4~#WEi1yF>5b?V`9Wrt*kV$i)w0_pwK5e@O`fN6Z~RT2xGb_ z#V4DDvIR-xFgI;*r^Y@bv-HE-cxv`kW7Iu=Y7aGZu9pa?{`12*5V@4P=GxNAaz2GB zq~{{#fyi?C1}p6-GieLq%I)iIi^>stCs~XjS-6?IvMgnnTm^0Ga;v;!7U)nlbB%C* zxqgt1MTKX;i`-`x;vXtWZxbIxfV1kw75(!@I-iWhXWt#+qXgGl0=>a_CrD?PLuHDt zp|C~Av*oFK5imI`@zaW%oLnzrLGtX!npq>7niJb;sQqeq3%R%6yNsW_Secxfim1T8 zsu+bRkvT}OKHXO={&vs1sTfkF0yJxFs)3D!g(+n4a0VT8N_+}AKLJuFJ9KGro?9@H z_;9K@Nimk_z}FPJU6vC&^sWece{qK34tW5~$fLWcEkS(D2=c5=kTarD4L76q$ReIK zCu6kx^=Oo^sE@282>^`Zd_l($1ge7xt=k;K-pcCD4umKbw2g>MA;>x7CpEzG+-rw0RY75ZDHF zC0JjF)q*#WQC#Tl+0vFsNEk8Q7eZ*2hb7l>6gUqEBcMo{M=RJ^@b;HyV1MvvW}Yab z?ySGfh`RNr0okcmI^-s_`u&dqy+?D#i&oreekjrF4b7C%Bfu5_RaNOOF1pQHKOxRe zgJ};Hbt)J5iL~B=b&w)C2bm6{T{JAwdttct{(<&9b>saVwKrIpzuBExNy$4jYbb#O zc6tRyZ3^kGlFrmn4%808n*&9qw6PY#3G?jw*<~tfn55LeyFJJ{bw>!S>+1V%bbpn5 zD@@2zi-R3TXax-0h~7c2vjFaOGLerKYjrt*5Uv5BbMw0f2I1@eEC}K7?))J&gTM0q zBmi^y`RE;{`GeLF-0t7MKyWfY|9%t+rSAOgbi|K)k>7v)_~YxaRmc?c`;&e=53Rfl zd~?74l>e?;Di?b1dGICwo4Wq91i19~;(rv(zrck;7{xox@&CErRG&Oe=o6t;$XEBC?bP^Ip!-~Y-X35 z^G4W45BD>Lf%Svcw=*;x^b3oKh%laMZfxX_AAXaXs=Y!6LVcX+KH%WNTeVz6?jx(` zM|WlbQdWt8KLKI50XTOJoH(%YbpodkG^y^AS>XR<3Xzar=Rs&oj+*C?45YTFV>`HJ{!?<2gFZNm1t`*vg%5Io#Hs9T{*!F>0? zk7V$0JF=Y5eYFN$g2}C$?CGG?8<@>`vWIUXAl7Zmd^z5HmQ_kpE3culV4(u*HxQ3!Jn+GtHoVE`Kr4oz5d-)wg`(wX1h zXxQFZjoo%KSrJjE5JYmpIsNuB=YuZJ|ko>y2q@3r#Hey ztpgX63tayh>`EcM^xEumw=p^WgRM0)+|c-hWDf;v7&_oP($00hL8FZj;~*{gwh$a| zy}bhIG^ML{m&x*0?S?ZIV|lf5lKp2sg72c4Ch~Q15v=;2TqsJm>%>ypl39d>ARqb) zrgAe4xH5=Mf!G)Kst;1u1C1z{&lP#fdRIFIuY_O%OZR6U9ts1>4KDpvS;z1MJ&aZk zO6S8pgP=!iAY3D+Nlg*X0G7sDUQjk`XjKbjk_4~He4Q0mU`^F+0utcNLkiBk$aMBc zesYIl8;>=7=l6uhFHS*O8=YG^gC-3!BinuZWG#jQ7FjRvzq6= zo_D6GUlg65F0kT%e5q-8s+_y{s+l#fipv<{EK3+=7rvs34cInkB_q8PV>ND3^ z!F9y=Y+1LOu-QwHgp{_3yGPX>wD>Z6r{qh7^}dy6+^(4^Z(n#T|YwFFX%QDhXg{>&%MflA8+7;R2(*4C2#_7XDSWv`EY zAL6g$z47ig_|On9**boEJ%`g2s4e-lC603gjbhE;)A>Kez*K{(gV@S39zCkQ->kcH zdyaWK3j}QlJ%HfSlMIp6)&M$}d@FJ`{cFimn$^+_0gpzrTCQFLot+IIi)m5oN2U*0 z+^^FP0w{ilO`jTZt4x^(!vcbO|L(4->4)K$+Ra;QG*Y1~gl2G1OBwa>TG=T(&3@WH zxU$yOUP6;tOi#9)vXJ~zzH ziHeK5oQDUMX<>fM5;G-%tg#p1hy!y5rfm%(nlO#Jr2_h7pFaXMp(jo_E*6_gl^Sq zH*6ag;jh+K5xbof>!#V#5v^|=kh2R!={z>aU_A_WNBd`Ms9`Xl6_E`V zhCaLKrnRd9v3p^YO{Vz@(DR0RC6SbFf zYGoJ?XB$jx1Z@KsT^?HnxxwzZe1{Ds)}Hb7`s{S(9z}-l!eHfHynq8MtLoCVQJ_F` z@FY`hDA5Ty%qM=}HWY73A76ol1`Qd1qU!!Y@^yhTi$ChPjLS!4ey?6+ox}&}Q|O&s zU%=1#Td~A$r|NH_rbtGjY!vj?D}mFURMl<25TV31*@Hv`+*kGCbdI7|*2qy{Rz9Xg zd(rP4L>Kv zlLnj|f6%Q~bz6lr6PTV6dq$-4z&S9a;Wi>p;H2JZgVgxY+;F%_OjSt}Ew2upv=ih? z!eUvK%nD$`Lr$YWmLki(73cG;PKHQ+eCyTj;6ZLA-FM>Vm}Oq?ONsRi8?D-p-a{)o z-In{#Eh7osotV2oJY#7wL8Hh?J7sxt^X6QfKbgC*h zL;v6l#bE!e+^w{z|p`!QGikExC9VoMuB`yu?UDMiEt7F|CE z>7k-C>>)J8h95!5Wun*vkP`WpL|Pl*ELigg4jBQRcM4&>9 zR%-7KxkznqTE)eLP_n1>859PBvpo*MkWZ1Y!_myvrAw`Aajey!cr$09$ zy`BG%uz(yeuo1I|$$F!7sCKbQw|({EFsKXH1Bh1N81liO8_>|kW8E&j)O`6T%4~D3 z9j`D=m@mE=dA#Qx(7SS^<+s+PZNcL(v@~FrC%OqlaE=o!r6eog>mmpF$XvF99>{(o z_q=xKNm_V~d3~fX-xh!fm-Db5YBSC&)0?JO`jfb~@KxJq#+!~;o4c_qbt=ZG1P0|* zwK<-zPW9z2;8>L$5lJ3I_)<)^@g#rDdsZ`V&3X~5kh7`w5WawS-loV!lOlPj8*IEm z$y&%5wXiu4On;ND661!LX!|{_pxiu*DoSM{^ug|&z1^2|^4V3a3)lkZ`pPlmN+}CU zVMW4x3lWe}SdZ3crJQZ?d(&86>_#Q*4*{Eqb=wCyHR2&35e{Rk$jF5q zLZ@WNgHi#=MQ#$K{F8*iSX|MoARQqtJv_2Y4Z)N~a$>_Io{6%lM`oL~>*=cSfM97X zqBJ7hbU2M-81hk+z=1^b5j^K9k}tC+nbgh+_4deZjl0!`omq19grxPKGcqC6QhIR! zNM4HADLAU7Xa1GSNZdVOpUqL0x{u69Ak=betci3fiYfJlkIMOk#aBT+;RrYHVa75X zRsc?!sX5ZV`<0COuB{LXrgp#9ag^QNWB!rsyxvoUDGcuRkKkiyVVfa<4hpgr zB%$J7%vcdoGqvxSqC*cqSFy)Nx#d==cvZs`)*p$RMb4XvMd(pFddnb*iVB0@Wi-*Z zLMT)YCcmJ=C`c}S9ePj$PKA<_8Hf%zr1lit0Vd(bQq$F38P^q5k;&G^fU43zm6)~| z)d^KVwB9Z~lnDE3Eh0Rk!dm|R$IhWvYR&~Mnw;_Z5&8_h~pb4p=4U5=nqm)dM}!O)H{wCVzh3prA354i+C_(X>CwDk01Ja^p(7q4R9_C1J7z22N1 z+8Z*IbuzS6Uw3eKs2To)7AS)AVG)ediGDB6C)Ah5u;)*F08NLKH~?!^Xb>NE&|kQ1 z5FHfE(I(<_7wXuf+b+^`YHA5cm@AP{q_O8)EGj`bjijkSlX;zcsULB-&S18x&Y)i5 zpw3l4>Rg4s2xDpw1nQBk_f^9cXBp1k{sQaDvziH(P}^FaNp8O6*F18}%q$3p8WdBn z&Gi?Ys%QDKYh$JHSF|8cPo|I$a;yfN3giR^69Q+LRTwN6iwDS_Q=tS2<{jV!XNItY zUeX%|yYx4J;w4|=DDv?@xDo~QaH`cSd@y%ruIyg z{=-&L)#1IgI?p63>cY;H1!hLHqY{#-lsM^=bpTUPrK<;FK+^XPTVM)lj>u{%BPTCx zNLuuKn9Gjcw7B$&qC{fsnI|c&o_(j0S%WB~l_<>%z2bXpWWSuAxsMiPkAs8);j6WP z74Wc5H5kS4B&*ZDkO5)_rS(XfESN*EIhs zsOJCa?n}U-e)qn$s1Q!Flq?mKjIFXn){-#7VC*7E_Py*|2_e)lN%mzJ`;sMl5kiRU zYxeB>I+pkT`Tx&zPSf+g?>XnH>v_)Ua=A3~``znzyTA8+e?D=Sgrdb5UY7&nkIuEv zAqv+_K%iLHX|BeefDxPocOdI>SJfrAkDnRNz$2M1`P zwR*N7p6_(x%v$cq0ui7d$U*{(-v(?;$QW%p2!}8G<<-~MkG_BX*~@q(NI_^Uv`6>S zEUA0Ya-qyY8f1z^>swlDDHwxbZt(Utg#3|L@={TFHt%yXgd^t^{7~|OT?pwtOsgtT zBq9`qb-_lbpb5Y|g9=SxctDKlq5bHe|=^W^mE*TC(P6I(QKs zYlvI6i7okKfU@WYK$bu6K4H1_T!x$ME~@M>T+_5oyQ1}#+3hpFq_yK zBZvfKG5Ts`N2fr1*eSkR!@@8oX0A3)QriD=cTuZCkYfq0tY7=Zi&c!=8y-_EQhd++!J0}5A=V`5dm32kom z*n#~B5wH;35Q3=JUNK{CJwX_QU}%v;9fi987{Pt?6NnGkq*DQFV4%xZB zIdBd6V7X&gJDZ+}?v6lKYZ6;i>B7KYWB@u4yA$RcIKE&ki}a)L1}UdT_lwV`yWdWL zl}0XyArQSGle+v$(g1CCy0knZtS4ge#@I8KAep%l5E$I45aO6lhhk2b_wL{{23B+k z@HE_3$ad_~_mxV}?3v{tL$yYKc>y3s%SRK9OgxGKN{Xw84Tix(Z-nO<>@i|HEf63i zJ`h*2lF^TO<%U6DlT#oo0wEO^k){zXJS87X7WP8~0lgTmBr4y!LEmM&O&S`px zZHfXcps$O}W5V*94*+$G(e-1*;LuT|n>mgZnX*`T4$y;g z%>?Y@!Yl%G+bpK67Yq(pfl622IRA+iPxP9W2m_1|Jy_R)AZG^?$=f0D=D}JQ|LT^E}>@305BCzBFjufhmQ_Vyc*qM7z>w)e@xxR#O3$l2` zr*H{k1zCVb{k$F>23XlWD7*z5}+hXW=^e1zNB zu?kM#N@qTB;%TFu3D6XaS5V>d4&$CysV==(04%!`c$bpVd~YaR4dZbFs7u24SRVrcGKAeTs%|Lc2CX{QkgwGt zSy1Ru9?9iJOJG07LY%P|JQ4)JA-k=>vJ!wY8d50Z1|hTTZd?1u=&XskI+YE&&wV`! z`XW~3KjyaHAGU)5D59zpeX(_?ryCj?-f}(~T&pVd2LwyyOlL#$Dz3vUcCZYJ5>?S6 zLZ0_Qc0F9hR;r?0*4Y5ccWfU?2KozjvTpAVOn?;It+=x-AlDDv5JP7xmjEu2k#t&w zfo=+UDij?_#xJ_ zZ?C@7Mv*OtktD#)sVwq>O^Yue56X$(8D%sc(x3{u7_JDNBMI@q1E4PTUb3~heqpB@ z3xLkA`-Ds^TCR>b%2p)G4WOn@F-TDH-+k{n$~(~*5!Md}w~S>eo_=zd)Y5%%%%y+9 z?Sf+8bmV4PS8OW~QOHSy^KnHU#$a3z@B0VffZ5b}Z@cS@cAS%qNQJT;SvIq&zyyl} z)LEz5da`2}6$K%f(BdKpg}pjVMK-+W;kDhwtpkpN3lG5&o>?JaAuN~fH363dUDWT{ zZ{q$6?I@LePCGbJMIX}#`j^+=3DT!u$MJ{`kX`^VWZ1yD5`d^~x{U@w2q)Nb*c`Q< z!OaTSF;Weo@L)~mbV<2KuCQ?WN+G{VRvc>`K%_S`@Lq>F7RAqY-ZK?%I;$Dl(pAaV*ZQ}V8Cxylg#<~Tg= zD3jya&24a`W$Z!V3QfNf2(Q5oIWq~7a};80Z*Kt}`F z=3;|Et12Eqvie&h% zYe!Xm%N!PNjo)V-V%-4i9*YeQ2tFQ2(SylWtVuHj1P<{n3A5HWG)S|7m3$vK%MzmU zJn;^$Za7UxS3HbVEJfDBw=$a;3UzuYyobIvjUhKyzAE~9tpXg3O0x^e?`4B*Wwqz! zsBoc7s7hILapyDvF{Q z`^~}va0YiNyxBGB+lym2oxlnRY*f6&uj@QTnv_cF2O_({RuJTzb}cqcfWecA%N14% zLi$DPjJWeQFvDVyy?i_^g?z3X3wf7cF4k>9(*)M2bWV^1j7lYRkJfGB`PgC&$ zY$}4SrG6**&p_Cuch(9kzd-58=bD zx-0?}8Urp|;S5#CcK;rTX_Y3V#u?6?Sjz*jWSRhX4dmIt0n)m8*9qaxCU1>Aq#va> z6k(Q}&N}B?NrYL5^ENP(l9Jk3%Em&NK->L;J5ezzBv7azr47<}5SvIZ03ZUGAdi56 zLpggW^k0qGeWxTm@|+hs6!snsMEx6ZNP7e8<#fkY=GDohqabuQ;7*sb(#ZB~Cb3}; zD6+y1!^znr6V@lcP5?se?wxjTw@#4j)_y0%1fww?n)phqt4`VlFV^0@EZ6FuKfxo51L<)N=R;tKTJPTqXjcEb3P8fd@kJv5!M6xNC^vA-42;}y z>zta|gqc+IEQhh8Zh(!lJ=!u~kSQK~kB~Lh_hxkIDwD=*zL)!0GRkkt4ac?yAD zNyZ%36c}ULu-u-$I`f`#W3(Q6tx(iM_6F=>B?;TdPM?DneI#dPxni?`+#WvwoQFM1S4dHaf3jc2kZDzIc5aBv@R* zu@aDU9Jy+NM&0m^vhRB2%jnn@IPoYp7wV7&)FHsbv_D6(`;+z8-(c>*=lj1Q>1N%R z@xT5CS^_@#<2R6Y@b~_2NS)BEfBb&zA1v|DzxCI5p>iJk|5w8xiTZ29Ac^|3VShC5 z=SCjHF!Zv35>lj&{_!xRn*UWG(AU2;_|Fak!vU55XD|M)*I>o}x2pG59Fzme_8*1= zYRMmkLuTObK7>YZ-;jV0|19C}3;(ZT|8Cs=EcVY)h4cs%Uj3jU9q7%`wl*j$-#J|h z(o_893Sgho)O8o4QLEE?jTMKA!kiWc1(v;qA+jO;y3M$o_rnx%R zEW{f$;caSAB5bM0Z4Cd4>>b7|4IubCx%C9@yjt$Bm(Q-voQ0k&$?7qE5;In_>w8B# ze>TS~t)MUx>$;~uQG$oJU55`A_8na7%HOG?#d(tcvY%rpF#f;gM+%s-|EQnyKtzAj z4-nm7HmClLNB-x>9q8w88mtq)Zlp+YzfbyqlT!Ut#r{$a{EgE8yF}%mk-%T7fxpF` z{!>%`J0Ava{?}Rde-lITuUY3`YF>Wnu>UZF{|i+6-^8l_Qj7ZQl<0@q{HKcjjZ^`#B+)c>1U)PG6`f78RiRJDJpeg4;+>2G}Nm#X$} z^brb%*MDbV_YWDkU)sH2Dx*J|r{8CT{!u~vcaf_9Fo*F=hy6!&`=x&RUk}=Tox}Kj zQ~ygHMwj{j{9yf?n5y3>{r`}A_)%eg`76Iv1Am|N|LBVTXYyOWR0Dr$-2I1CEDgB* z?4#es|KE+4NJ=>q=-*F}J!6eBWmE^V5jM_w4FRD_Y+mBTV)OQef^dXDZ;@PW`kI zhtQ9u(#Yja04RcO8{6PU0B<1Qmmg~cQk`Eu_U!29Tn=>cjFBHh3^z%vtLFMa+`&7! z9@k-khliMTb+hamxC+hFGN-CU0U$^Ib;I8B0yclgkXvCp2u{M+Y~O>Ucxa+$7B6^E zmgSq{8T_heP|&&gg=E>*`!;eofxI821sHQgfH&f^=fV(#TCH|j#B%Ydey&j zgXyb8Xy42ENT;m@S0}W=W1avr@`4+c{@M4j3OI(2oK-rpN1up|?91oSEc1IUwMpe0 z?NRd{W}SIi^U+K5C2UazM#Hi$H6HHGE_>8W!h<`>jS0?~r?-OxbT`{V#doT{Nbl}W z>e+kj?($e_i^Q64HfVR278;H$Nxb3mYwbfzamLYJpe0mT+3T=1S=$$N;JRY?u1ZH) zdb7Il?$*&eM|Z|YXtH+tqaF6*JcIjBDhvpfHLp1ps&lzGH}s@nFqq4CY^mjz=*b8t zGE@MS1W>B*tmQ!`9RG5Cj7+pg*TE(^Y<{kIyfI<@`OZgQX6gCU%U4})IrQ>QJrhl! z=;d8vak*tbz?-`zek*T~cle8F73BbLsfFVVQ-}S;7yI6$g=zGz*6p#9g_N@|oE0|E z3*JKwRRPcCJr?^%oabCC<_o+j%{BQ2ZpdA93Wuq1)-A5X8h_eG7P<>KBu^;qIuEwj zV0vGP@_v7*nB}@N!n{=VI#~Vf=t5N0wIjASkFj^L*f!y+(k7SUDje2bV)`#HPo-iN zt#3_jig%0?Y R-%1TJEfU9Xxxwo&RF| z!j+U>E@s)C_MtN}uHr+zX*Wt;F!E*%@UC@L zpzp3Ix!ht@crvnBk<(SqgQg*8=*&RzcH>57w&O)PvTp=&M!h>Zrs^7# zV}CVc+Q3e$uPYVmQoN+n2J7;P@|=ZPSoM`nj$YbAM8npty3B*_StWgFXMq!h+W~QPDHsXk04(y(5{t5fe!cNZuPUO#~)ai z%}mU&ru@m29K^i7*s|$h!CalZS0L|y(I995f9laqAS80HFES4!R&Uqh2eiJ`kGG#} zeMc2okhR!~$gv?$>6>R9QK|Dsd+`E= z&E@5`{gtDsFHfisKjtt|V`n1l7?683opNP3xLifaHf+*dIu}n7;p=KXKZ*au%@uOd zMCWw3;XK}LBBYmgWiZb~#n8}jNg?|gM>yF{T0d0+3xPNVm&Jk;v@FU9L9*Z&yS0kE zh>fn7E{fgK4i-XJoyP+ryl{u8^%Mp1OV4FmDcF+lKgvxt+$xLTIi)V@yET>Kyn^ zC9H>0>aJ`nr4aX58I0=V*_B#-delAWO9t?j9fmwN!_?3^vL$(~Gin5~%&w2S4~Ip1 zSjD)F=*B<4mTUUuTqs(jclXoxkZu70x7vo_YZ5cTY_!(TQ643n>>|st6*KmWT((Y&8A1wWEs(flU{p{k9c}ePa&XmeRMK|x(CkwAT zGAU+{KP_Aj-0bS8_K+Dkq9*d+yt=(=pCxovHO6UA-jwD9b=}xYElb#{pGCw@2n2$t ztL4|}-52#!6nek6KIKuTxJHhhb4(fHbhn?xlC~vw%dYqG?6CYahk!*7>gU?*aZl%y z3o=R#8%OV)R2<|TPjIPuDA(Wek!kfEIj8GG12KBBntIifA!;e9siwEm2y}{?A5}?W zV}ab9s*7?6#y>=`CzKSwYK@(Sr#w0*PA~SkvEe8-Lq8@DG5!KmGeTe+z!fRjHJWIE z*E=2%>4GR>WD(cO2<3M1DI5v(5$(Z>)hy@-i8?pr(tS;bfM1ge2@8061E%}P9o)My z$+G@X!BlFrBCQhq(x-`Tp|yUfCAk>8-Mk3rXf!0S$KWmxB0bdFJ@N>Ktob-X2)>%F zj4(*gRo>~gWZyGb)*{r^@3Cl9J#=GCF?xH*jQyqZ@hGcORkibJ+d|O=f(3{!h8$J; zM;O9thMypm+2gEjMZnej?_|xQMT}TJtf~Ny)48IL^#xOJY6zuG>M%xweQ_)lTv}8B z5Mr+FVS6d&wa+YFZbkUKt4nXSXnq~6s&@RguDV}Xrj_|m({Tcz>kVqKgKO5{#P?|c zqZF*jkhj#H*^ao(jb*P*d)T`(mn@JG`4>%?9oMHjc=$U)_4g-+^TQ3nY4ek{7*~#(sbUP$Y0Wi%=MdQCBcj3eUWh;T#;D!JN2&-Q*I-hovqoeZy#zzN5U?nsr@N|(;+Xc}Em_$`HN5|Elhv^Vz!xmD7k??Zm zt8R_k?g)0ZSe|j3X=z5K)m{FUdN_aP!ES3p;ov)jY7M0tT*)`=PGiR=awngX)Jx|` zdn&I|7ASzJ2sNbnbmS+s3y)ih3opOmDwI!M98r$=b|jlyxHhI30MmTNsNOI^rqsT7 z{xPn^j6X&!*&>h#!LR)Sd(l9SiYC)o9}rD52z0y>a%dOSwnVbMhPp~rJ)~Up~27QbV!39HD&RW(0L;AN)7mY#EK(x-8%A946l8b z*3o)P6MW5e8b1i3IEZN5j7wp92!%lTIdb#MSSV&h89_p6@LkRX<(6!mW}zW)K?C;Y z0H^N3+#03TB5gu5loDqPh})&YRGuiW=_-V~JLM}skK?IUTJ^PxSj!;7d_R&snhPfn zKR^&5Vow8r1VO{)`pYgn$_epmTekK3F1P3<_2OAnuRU#4=U>eQfPUD(RI5+qcL`!P0GjgM7Gb&*sCrTvA$z@g zER@O{QEN<=skf$3nvQT)g5@O?yYi$G7!N*v)YjVUnXH9(ZXJ{tv4$Fk)A-KAxSI)6 zX4+|D4*!iffu==tgw2E4rukEo%dOX#cZt4j4hV~nFYk0@MgqKFzhrpRVmu;=(8SS` z!G}MW9k`Mg$dj5FnA$vjVUe zvF0wpmw9CRAY9nCu#}iJeU&--^-VzGdQ*j_mPIvQX;l_CdXG?Cl6>m1VY+Xw1>D(M zihjwQL+uuzg>xU37SmYS256XO+A6U107ARu?TdIgpm_$xT{McG()8nzgNBs$kmP)lphKeR?V6&Kqwq0SN){l*we?j-Si} z6Ye8hlxA$MVM_vg{PT;gQcPjH*cpsofz!BAO6|r0!^ORF3uLeOU`J9xZM@O$)@MTx z_J*a;^vt|58`EJ~?nRtiy)iqQ5waCLz5ID7?$n3JA|8#DN)HFX7e%T!b$_by2yW)J ztvf%8g1z$XYy^*q8*=)PR(=nDV@)U{#QoWBEpq$m#^Qz|g@4RuD^Jg8t_fOix4~*} zwLw~>>w^1@?`_Qt8oTD2>=I)yvNh8#xN|*0Sdg`Q{TLZ1|T_{#L zL4hRclEGVkjYW^yWgbPL+mB)1%}M8LZQs^pKdvHc(cRuK!8XC3Su6G5Okq`_2iABV zzt!2ys1b=IVRXBnM(n}O;a5^2_Ie${s{;dUvP@0T2x@lJt-T?Yl*gS?PJ^9n#rbe8 zfK$9<+@hy0+rQIpGaV{+02(Ee0P*pk1n0?hm{a`eCSfzL`lD9NO5~V(k@M59Spw+c zaR&bQ;`+HZs#QjZz{zqzTk+Y+zfhGRjBSlX76CG0-k5Fg5pDHm1MS3RHyUx&4G(;a znp`x;@HAY1BxvTH<>LFil8X43aG{^3iv2h+-^yoDnI+krkIoAZBatarQ8$wJzCJGT zrJ3#5^YxK0H5q42Q_Ii-j1z!aBV?I+!cmFPfXT)!Ekzwwr%Vt(jG{k?X?x(DZp@R) zKqX+){&5+N1Q!xjMAp@p;_I6Tlk9xlsNun&&SMzT9=w zq5fEM%fl5y!jzmEAy%C#CA9F$M7aC93M9$V;mFBFze8!A3MP7aBOHihaF6l~`4ftJ zG=e?u@|!r-G&SsXY{5@zD1QKBeeewlM$CvY^hy0QNeQgCB+PvH#O&42PJ(uGS)=e( zP41tJ+QW6|-t1?5x#`pTtSn11IGz#(Pu4vdixyf}=XUTQG`NhYBmEB6f*$?+Gk1|D zlSm;x^Y0cT%{n>vq+Hk4ZPCNmeA@^GiwQpToHx>K0HC*;G^aZby|5-NF~D)axftN> zBa@W<{7HC#&+~PB*I&&&eL4O|-2hNQu~adJ9ml{TJuh`v3V%*YZ?6BjQMyvd?#o>;8(F63 z5aV2Zw#zc>k>#YZnOz~)8XdWvkjGF4{_KW{!Gg}19z`n}Qp|`mMNVFaX{Mw9=C(3D`+E2oW+)!e0_PTv8Iu_nU8T z;bs`n7R2lKeClh1j2|t-GUiK%W0O3#xA&MHHpw1+HItz?w8x~ZtIGt;fTYcu{!h~K z7~v`edx8)=(0J;0%ry_Lq7N(5u8HmbbP-NTxf~T8#Qu0pOm2Tw@2kF*k8Vxo{WF8@E{Ez76z~#XW_p43CMD?$acBWa8(hKS3PBVn ze;DJf7&gn)Q7}sDTDoM`NQnto3_Xjw1zfrd9yb?2$3uZk!Qo`Q5NkiZ9C#4(`7%7j zLysKu7-YmH3>`_~stJ3SpCHC*2p#GLZ-ijy^H_Ajm7N#(o<#R*v3GfKxn{<_$(9y& zBh?vJ?{KTDbaQS`_kx%g_cZ2fqtqEtz~xU$LXuSYTohYcDxw=HMxQ{Hv3cyrkyNHe zI&BqDqt=ddSm0dew>vWEJ&ug92p4MY*lNlG^Zh-$h@;lc0sw;u-GB-GB)xZ$ehQb( zybyb46!4`YKfA~E(j|N?H#nO62a@vQltpP)eMolB<4`CRn1w?3_JUAC4}kQ!#Xn29q6EHLlCM$#xkv4B(@FT$wZ;!lX{M z0`oa}bdFwY37O(po<3huxVvqWA=YUeoaMUqeqyggzAjkl^eg)2x3)c2t+BaaeFW~o zAFsZQYJG6=$d4m#63+qG+wfHz`uwO9`PO}w?yb92uXFcjy04Zyq(~B%MDV5*hCWQ0 zaPdfud#cjR2X+jGh>+cbvAM=`A4l(Q(E|Zmr28=wj|jN|%UdbRwO%TA`}85UH8(ET zK&N*M$RIMBKQM-3!jWBo!QI#A9=qmUmIF)=prUP(H*#1$XcR zd&EBu1^WS)qnC(72_(Sqlmi2Z=PbK)m2Y;vokLE`8igs!zRt$~pv8tsBln zpk6X@w%eC8>?usnFk#6s6?+;dxi&Ux`eCKt4Dv8^&8{3z>T?Yn&f^m06_5Ye;np@J z$wX8*k|e`5hcmKrj;ST?W?bvYNuYEe)-iCDJ*5@1z+!>^9RRmc2*_Lba39IUp8P0V z)F)87+fEUTlKu{_5m87d{D44|Fki7Uw`j?uB^LN)+;Our0*f^{MvXDz;v^VAVMQ1> zu%p~pJhXh@cE#TT&hEp?5MFO#B6rinG`+T1UCyN=E8C_@I?uL}To#wEk#mrs_l|K( z&3j)=rB6_*q?`bhq&vxZu~Y(u=>lY~WM=Z~{w?zus#kVtsacY9t0)(?f7_ibL2$Mi8?L)ay($=q6 zrK4oxhfHCL&E1XKiv(0IRB#Z25{4|MI1$H&I^iR9ll^+($jvaOqXSOW*BkL0L&O=p z;x*#^JxMb2c=|s3?nJ$#xWtULQkFV2%j_F+`l#a z-AZN)CshLB>k-$dlhn39fgN^hX$n zhx)8fMOpY;muF>bUVScR*AywVD6^H<&cqv+n}3a91v>6qUwoSKDQ^#BwZnQR6ojyb z+Ja@1TB(Mbd5msXto~c>2y6Aj7{~o!>x0tRvk|-Q~DTBE57gVM)BG%DQQQMOhfM)mCub!hu}4OVd3?yz2o~8tcWUNl#VE5ri;sNK zOg}Rq(c{pXx-4M`FiB3J0014M;wGlSG#I++o#@TF2-Fc^|IShE{|+IXZ|tu@zLftVgeE{Aqm(sK5}?b z`r0w*hPdfP)V;Je!A14s7;6N5lROLVsa*%ac20`TeK@(b7?#YPdMZV^HB7|^q}tLM zzOt3ia}>21*s{9y^nJ|d0>_o0LX>e&dE2a0>6cT_K1$+X873@Mo}Q@e~h z3mH1%jmes#mJptZrSh!1EspYArAgcSH`V!RQNtw_tzgPktAxx1?fSurp%r^(U?&Ss zr;6CLMRd8QNxNA9PCWq169oKwn?bpX`~Ka2(jyppwq3*x7=02|lS&ndnI{4)ZmMTG zQ;!soQ?jT+3B1*Uh^v#`8|7I<7~}FRM`BFrtyVASvOcK+W@cnE;rg+cs`o(aJ2&RC zKzx_ToqC*aM_pE7jYgnvN=eTJ0AD3+mZMLetUZKUVw9!a@2(D50-{KgQ(Inaf!ByI z*tdf{E=q>41K?P`ZuPwO@!QVlis#J+N}XEV^q|1@!i7v5PXvfY0XQbO`k#^$i^e-{ zjj~oR_UR2hFDNZ_?ROa@O)l-dgJO@veox~`PCw=6xK86e6`cv)71+U=?{CV{KXjcrdNsl-S&Cw%#&J9UhKB3z{db zN{WjD_$$Cc#Om9Ue2ZaL`$hS{AqY=}Ro444VlFM^ql1SOQkEh`uheldcQV+Q_gfqJ z-CON+(*_gS6fFX8)(2QX{gg;Q?&OaR$%*~zn0eBDbcMu9Nm#8uyzboUq7-InX-O)8 zHp6KGonaMbaRy{9&$~U9KPm8K>E&P~0Dm4&|JZVA%d^?}_U+^Q6Gw_;RaZ%|7_0q0 zmu0P60x*26gU%?}(=|ot3XPr|LUobfm2&>t*=u;_D5L13)-ECpfRL|wOC`PT29l+@ zee0M8qJ2OIZtHOp#&a@*Z6w4mj`BJ z?1f-&FoOGQjpBI@yL-d8u^&P5K5}1q;HQ9KB~jf$ks$q~Q*WlV@#vO1et1HhHn^iE zcrMfSkcLv@3jZZ+sK1h165zU&U&H8(Ue*c}14XTGmhTTLaQF=KyZ*jywae6OJgHB= z4w6@ipHT?lI-n`iAnenaWB}8zLkv~ zmSW=dZRwmZ=YjAoloKRi>m?P#PW}m#6>6C}r-M;`mPFc{3Kfl3Sf5)n^aIhS-|U_c zTU!|NmCwHa5%y9!{_+ag;WAbm{@lP`r@x}l!G=+uaMSy%oce*JTs(qL(FXMFp%P5P z5VQ@zE#47c`fMFv`sl8pm8CR=Jl>cNDl>~=3nLqHff)DE;QN_X+ds85ooX*gWO**@ zGqI|GdDmZfP|WGdN=hf1+(rW6c1H4Dmqwpj7whnUV!bOb+}Ii0mLK}a%lUTnrSnc- zJE8D{-7g_uC0>f@EoDNpO_cP0KSyq0ndnr9y{Eq3YCoFxIc9Au>3V;q>~g{N?Jp>f zFO&$dXC%1rX>w#29XZKSRB4b$?bb6EI(lh~9)mWFs+D2iANxveEWmXmPssvPCs-Sp z3|9zBje*El*cC0^r@xt|y~}JpjF7z}N5Z`E*?~DQ6m9>t6EL$JVoAV$A8BDP(o?8& zAEev2Mhi&xZ&H|zBiYsuM1cFM4q-}hP2&_PAb)_VLDhfD{caa6THqnV2P=+53;2`| zg{c5@y?62t^Ua95GQmy(n_v%;PW`At!)8Pzr)hX&ul{O7Wz{20P01@Nw@SO3J{opd z^dv`dBvmL?)E8tx2xU0k`hX9 zwdG&?#Yh3Ty|GXR-US=~=A{tICzN8X2KpM!}QJ>@X&Y7$H8G9b))er$XKIpOVrNr(7{vfxhZV zoOaVowNleoBwH*}v^ip71Eg6`D`XXx_jeSpuDZ`K42ZdU4jCM09)F%wAqO`EZtVd4 zrSk9?BMbKaY01F_uua0OEPDg8lz3UgN2`++TrLwYbLFjGsg^Tx-I;$m$l_`|lt9LO zcjHu*ujV}BJ2TDxB(kTsjP7yro)y)ztz40!-nG33}j~T#VLw@=^?&i)^ ziDATmjHq(ewC{D9JHh%Xamy`iv|`0ajzEvx!)TRki`NUN#W-2HreJA z@TP>j>I_NtX8l>zk+kjQMMD;*E*BHS-m@1{Y_Oqa;Dp6d8nMhB`9EZIJO}Z8AWgZG zR6tEnt9wZxa>ngx@Ojs>Eo>*n;-Bo}@EpX5Ou3nMKi}(jQJ!l>JUj>6;;XEs5{ukX z`=g8p1&E0t!*vg8T-&766S{8^gpI?SsV6nts*pVt8w7#3YXQq zp2HLGt7ismR|cwl|03ppL$!79>Atv0>0`c7@>~Sq``tC2D#006g%*o)l0H{Y4Ln$J zuR23%Rgt|PSfGe-Pj{2dy4^Zw@)3?Ga`SMGZr`gk16tbxRoWC*k;?m}RhwbH0G#rf zmg#yiN4I$cv#D|1E4u`BN8v?n{N8N;yxE;q#`iV`=!jP@ibn?oXh_A3Y0d?@b!3R+ zzFFvmqH?})s&trsu(wD?3b#lk^qs;6)?MO{n~b(JH*hInpDw6hdzdYLm$E>#>+9o- z3Q1RL#7yd`9=*ydTJM_|T3%zbJEHD-O94oq?dzwd8VEhcKYpluDiYlCpRs9KoE^anOKf< z4tlltp@E#LKk@+4*{qD*^rigPLQ3wa+>L2Pab~X0LD#*y{LEXWafX`~Y%w|>cCMVp ztF{LG_YCi0?p0q=`6MreBO-b+ueKRubv#t})SYX=i-j?6+)QHccB#^X-OEC`_qJx0 z%Uta(Hk}vb>J}&STPr*US7XHcp1s((;Qm~%Mx1N#f+d54pKwk8$aOK-^#DqyfB^5B zcmv{G^|slYpm=1Yn&ytC{@~^VldTf019#}|QV2$lqhpMN=@OFwlL&3gyXJY*F$C0c zY9Vzs596-rC~S`DB;Eka?3%SQ;@4{R0O6s-xw9#at=NlS3D)CwgLjH&al2JtT{PIf zvdeDym#YD1YVQmO*{nOh@NqW}+b=9nEsa{1!~Dz*Vj`l9`2CqSroo&?fgW>EK^^y0 zQ(}j{EIO|QO)J5dIxjEEQ)0x3h(4*!Z@FejbO`w$v|V33zhcC zCUZ1X!+AP!q1OkRi1iG#drsS;Nx>(dbKvWXs7zK>PH?{UTEtWEb*yJbYu`PSk9H>c zv)7^FSip5zQGYRLq(0I3 zL3K;y$WZTp*}?`rOdYWQ<%@aaMQP}%FWbhKoxt}*jq|i`X`m1CJTG%VA3#+RL4TYD zrToACfJSEjng7opo}h0;OH`na|CRq<1lksf2ng7N$aHaW5fBKRJ_bhPHdc8ur;*uZD=RA)p76*>J_gUk=RrZbCTNYk z%|kCcTl#QURaT08X(m|E4~LC>vTfX>?1*4E=7bKw>@ zs0Ttr4dc<%Z5~qZ6J=s$suz*{3ZUwhk6|>(J;;crOu*&^NEDQ{Gop)Lme0-20Vw($ z-J|+|D5f`C-z()j3d}lrq0LAQpds!X*fSRprBY8pSx&MJX+b*&!v9h$BE;b)J3kJd}@7(E4nIL9WIN6Bek=)3)m8=~TXQdNV-5ov&c@H5 z@7vv`C0jap32MqWfz`D&KxFbZu zUhB{iRYw5MVBvKLauYj9HXHMTb!h3Q~eA42C=6$ zG6e;AWiTHo>Tt61lH%x%w2wFBnc{%Hz6)<^YI^zd<>uz5xxLI01!cfB-;?h376{XR zv-bu8<-!7>2)SP*dBqNWMMR|U@4nT`1N?r+xvkhxGCE#j{gzz!TE>H+;dgCH2o`&@ zSj#{{)B13(ujm0|WOaNaGgPAnVD(hwv+=#Z%L*F_6U$UfYybSm7nemvxlngC-zsqV z09K@i?GsSjX#jS4G4ipY64?!6=yjP&fDh_T%9++~s=MV4)s=Y9*!WY#vjZj3N0v|tqoRY#GcSG(pBV*(4rr2pnGG(QCiyf51Cud1L)*_ek5@>lpZ-aQ~wcL@(!=#* zO8~ic+J!;j^5vG!PNRavn$t8y{(q`14V+rh$Y=sEiA+EGi#=d8a+FYkU2nn>A@6&6 zc@;jq>~fNYuBWF5!k`uwE@=vLnFXBbzzFP5^#x-qD`tB7HxUsh;ncSs9M%E&Egc=5 z)#`hWIa@$%WEI6`({9S|jr40@C?tw~?Ck8^VJl;V9Bl&7zJPQyrzVZ0x)E@-o_4r= zwH3^w%AZz+-V$)YU9C9fed48FiT$ke)?{*RZLL{;58#F=DwVCh8WIw+*l=w1!-wlC z=k>0Xv`wn}(JZJFofQV88`06xyGs<4!_@&khhkMxq(t&x+uK(+MuK@bInxK&L; Date: Sun, 21 Feb 2021 18:36:34 +0100 Subject: [PATCH 11/28] Reorganize barcode tab of edit view --- CHANGELOG.md | 1 + .../card_locker/LoyaltyCardEditActivity.java | 9 --------- .../res/layout/loyalty_card_edit_activity.xml | 19 ++++++++++++++----- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1c65227..8c78c7af7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changes: - Add balance support +- Reorganize barcode tab of edit view ## v1.8.1 (2021-02-12) diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index dd7e7606b..7e922facc 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -33,16 +33,13 @@ import android.view.ViewTreeObserver; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; -import android.widget.CalendarView; import android.widget.DatePicker; import android.widget.EditText; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.google.android.material.tabs.TabLayout; -import com.google.android.material.textfield.TextInputLayout; import com.google.zxing.BarcodeFormat; import com.jaredrummler.android.colorpicker.ColorPickerDialog; import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; @@ -50,14 +47,10 @@ import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; import java.io.InvalidObjectException; import java.math.BigDecimal; import java.text.DateFormat; -import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; -import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Currency; import java.util.Date; import java.util.HashMap; @@ -77,7 +70,6 @@ public class LoyaltyCardEditActivity extends AppCompatActivity AutoCompleteTextView expiryField; EditText balanceField; AutoCompleteTextView balanceCurrencyField; - View cardAndBarcodeLayout; TextView cardIdFieldView; AutoCompleteTextView barcodeTypeField; ImageView barcodeImage; @@ -151,7 +143,6 @@ public class LoyaltyCardEditActivity extends AppCompatActivity expiryField = findViewById(R.id.expiryField); balanceField = findViewById(R.id.balanceField); balanceCurrencyField = findViewById(R.id.balanceCurrencyField); - cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout); cardIdFieldView = findViewById(R.id.cardIdView); barcodeTypeField = findViewById(R.id.barcodeTypeField); barcodeImage = findViewById(R.id.barcode); diff --git a/app/src/main/res/layout/loyalty_card_edit_activity.xml b/app/src/main/res/layout/loyalty_card_edit_activity.xml index ae421711c..50f850b63 100644 --- a/app/src/main/res/layout/loyalty_card_edit_activity.xml +++ b/app/src/main/res/layout/loyalty_card_edit_activity.xml @@ -190,6 +190,7 @@ android:paddingTop="@dimen/inputPadding" android:orientation="horizontal"> + - + + + android:orientation="horizontal"> + + + + Date: Mon, 22 Feb 2021 20:21:24 +0100 Subject: [PATCH 12/28] Simplify balance parsing logic --- .../card_locker/LoyaltyCardEditActivity.java | 20 ++++------- .../main/java/protect/card_locker/Utils.java | 19 +++++----- .../java/protect/card_locker/UtilsTest.java | 36 +++++++++++++++++++ 3 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 app/src/test/java/protect/card_locker/UtilsTest.java diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 7e922facc..d5b2fc853 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -214,11 +214,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity hasChanged = true; try { - BigDecimal balance = Utils.parseCurrencyInUserLocale(s.toString()); + BigDecimal balance = Utils.parseCurrency(s.toString()); validBalance = true; balanceField.setTag(balance); - } catch (ParseException | NumberFormatException e) { + } catch (NumberFormatException e) { validBalance = false; e.printStackTrace(); } @@ -500,13 +500,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity break; } } - chip.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - hasChanged = true; + chip.setOnTouchListener((v, event) -> { + hasChanged = true; - return false; - } + return false; }); groupsChips.addView(chip); @@ -565,12 +562,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity barcodeTypeField.setAdapter(barcodeAdapter); FloatingActionButton saveButton = findViewById(R.id.fabSave); - saveButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - doSave(); - } - }); + saveButton.setOnClickListener(v -> doSave()); generateIcon(storeFieldEdit.getText().toString()); } diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index abbfb2edb..468555e8c 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -13,6 +13,7 @@ import java.util.Calendar; import java.util.Currency; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; import java.util.Locale; import androidx.core.graphics.ColorUtils; @@ -111,15 +112,17 @@ public class Utils { return numberFormat.format(value); } - static public BigDecimal parseCurrencyInUserLocale(String value) throws ParseException, NumberFormatException { - // BigDecimal only likes to parse in US locale - // So we have to translate whatever the input was to US locale - NumberFormat numberInputFormat = NumberFormat.getNumberInstance(); - NumberFormat numberToBigDecimalFormat = NumberFormat.getNumberInstance(Locale.US); + static public BigDecimal parseCurrency(String value) throws NumberFormatException { + // There are many ways users can write a currency, so we fix it up a bit + // 1. Replace all commas with dots + value = value.replace(',', '.'); - // BigDecimal won't understand values like 1,000 instead of 1000 - numberToBigDecimalFormat.setGroupingUsed(false); + // 2. Remove all but the last dot + while (value.split("\\.").length > 2) { + value = value.replaceFirst("\\.", ""); + } - return new BigDecimal(numberToBigDecimalFormat.format(numberInputFormat.parse(value))); + // Parse as BigDecimal + return new BigDecimal(value); } } diff --git a/app/src/test/java/protect/card_locker/UtilsTest.java b/app/src/test/java/protect/card_locker/UtilsTest.java new file mode 100644 index 000000000..d38b766e4 --- /dev/null +++ b/app/src/test/java/protect/card_locker/UtilsTest.java @@ -0,0 +1,36 @@ +package protect.card_locker; + + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 23) +public class UtilsTest +{ + @Test + public void parseBalances() + { + assertEquals("1000.00", Utils.parseCurrency("1.000.00").toPlainString()); + assertEquals("1000.50", Utils.parseCurrency("1.000.50").toPlainString()); + assertEquals("1000.50", Utils.parseCurrency("1.000,50").toPlainString()); + assertEquals("1000.50", Utils.parseCurrency("1,000,50").toPlainString()); + assertEquals("1000.50", Utils.parseCurrency("1,000.50").toPlainString()); + assertEquals("1000", Utils.parseCurrency("1000").toPlainString()); + assertEquals("995", Utils.parseCurrency("995").toPlainString()); + assertEquals("9.95", Utils.parseCurrency("9.95").toPlainString()); + assertEquals("9.95", Utils.parseCurrency("9,95").toPlainString()); + + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("")); + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(".")); + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(",")); + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(".,")); + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("a")); + assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("......................")); + } +} \ No newline at end of file From b4a992abf8b96734114a45a5b8b9386ee2292a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Sun, 21 Feb 2021 17:47:03 +0000 Subject: [PATCH 13/28] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegian?= =?UTF-8?q?=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 98.4% (126 of 128 strings) Translation: Catima/Catima Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/ --- app/src/main/res/values-nb-rNO/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 170d09ddf..19c1ec8d9 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -113,4 +113,10 @@ Utløpt: %s Sentrer strekkoden på skjermen Flytt strekkoden til toppen av skjermen + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> ser ikke ut til å være en gyldig saldo. + Poeng + Valuta + Saldo + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> poeng + Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> \ No newline at end of file From 8ac2fe575c47713f9aa693a9475d1101ab5a9f74 Mon Sep 17 00:00:00 2001 From: Heimen Stoffels Date: Sun, 21 Feb 2021 20:40:36 +0000 Subject: [PATCH 14/28] Translated using Weblate (Dutch) Currently translated at 100.0% (128 of 128 strings) Translation: Catima/Catima Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/ --- app/src/main/res/values-nl/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 700f63c87..d84906e62 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -113,4 +113,10 @@ Verlopen: %s Barcode verplaatsen naar midden van scherm Barcode verplaatsen naar bovenkant van scherm + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> lijkt geen geldig saldo te zijn. + Aantal punten + Valuta + Saldo + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> punten + Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> \ No newline at end of file From 6c53b8e9ca61f00a17666e1447dcd9097976f1f7 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 22 Feb 2021 21:54:13 +0100 Subject: [PATCH 15/28] Fix incorrect escaping from Weblate --- app/src/main/res/values-nb-rNO/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 19c1ec8d9..ccdbd22e9 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -113,10 +113,10 @@ Utløpt: %s Sentrer strekkoden på skjermen Flytt strekkoden til toppen av skjermen - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> ser ikke ut til å være en gyldig saldo. + %s ser ikke ut til å være en gyldig saldo. Poeng Valuta Saldo - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> poeng - Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> - \ No newline at end of file + %s poeng + Saldo: %s + From 3fffcdbd67ed592bb75595bef77ec991a3147065 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 22 Feb 2021 21:56:20 +0100 Subject: [PATCH 16/28] Fix incorrect escaping from Weblate --- app/src/main/res/values-nl/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d84906e62..0e6158c74 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -113,10 +113,10 @@ Verlopen: %s Barcode verplaatsen naar midden van scherm Barcode verplaatsen naar bovenkant van scherm - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> lijkt geen geldig saldo te zijn. + %s lijkt geen geldig saldo te zijn. Aantal punten Valuta Saldo - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> punten - Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> - \ No newline at end of file + %s punten + Saldo: %s + From d2f5cd05b575a6fcff62488dcc79742623e38850 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 22 Feb 2021 23:01:24 +0100 Subject: [PATCH 17/28] Release 1.9 --- CHANGELOG.md | 2 +- app/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c78c7af7..4a9af34e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v1.9 (2021-02-22) Changes: diff --git a/app/build.gradle b/app/build.gradle index 829169df6..2a4bdfaac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "me.hackerchick.catima" minSdkVersion 19 targetSdkVersion 29 - versionCode 58 - versionName "1.8.1" + versionCode 59 + versionName "1.9" vectorDrawables.useSupportLibrary true } From b265fadec3a39606a9aeb1f869bab7fd671db0f6 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 23 Feb 2021 18:48:49 +0100 Subject: [PATCH 18/28] Improve currency parsing logic --- CHANGELOG.md | 6 ++ app/build.gradle | 4 +- .../card_locker/LoyaltyCardEditActivity.java | 2 +- .../main/java/protect/card_locker/Utils.java | 16 +++- .../java/protect/card_locker/UtilsTest.java | 85 +++++++++++++++---- 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9af34e5..94740dec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.9.1 (2021-02-23) + +Changes: + +- Improve balance parsing logic + ## v1.9 (2021-02-22) Changes: diff --git a/app/build.gradle b/app/build.gradle index 2a4bdfaac..3340fda43 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "me.hackerchick.catima" minSdkVersion 19 targetSdkVersion 29 - versionCode 59 - versionName "1.9" + versionCode 60 + versionName "1.9.1" vectorDrawables.useSupportLibrary true } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index d5b2fc853..33ae5fcc0 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -214,7 +214,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity hasChanged = true; try { - BigDecimal balance = Utils.parseCurrency(s.toString()); + BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals((Currency) balanceCurrencyField.getTag())); validBalance = true; balanceField.setTag(balance); diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 468555e8c..c045ff900 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -112,7 +112,21 @@ public class Utils { return numberFormat.format(value); } - static public BigDecimal parseCurrency(String value) throws NumberFormatException { + static public Boolean currencyHasDecimals(Currency currency) { + if (currency == null) { + return false; + } + + return currency.getDefaultFractionDigits() != 0; + } + + static public BigDecimal parseCurrency(String value, Boolean hasDecimals) throws NumberFormatException { + // If there are no decimals expected, remove all separators before parsing + if (!hasDecimals) { + value = value.replaceAll("\\.", "").replaceAll(",", ""); + return new BigDecimal(value); + } + // There are many ways users can write a currency, so we fix it up a bit // 1. Replace all commas with dots value = value.replace(',', '.'); diff --git a/app/src/test/java/protect/card_locker/UtilsTest.java b/app/src/test/java/protect/card_locker/UtilsTest.java index d38b766e4..f4df8773d 100644 --- a/app/src/test/java/protect/card_locker/UtilsTest.java +++ b/app/src/test/java/protect/card_locker/UtilsTest.java @@ -5,6 +5,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.util.Util; + +import java.math.BigDecimal; +import java.util.Currency; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; @@ -16,21 +20,72 @@ public class UtilsTest @Test public void parseBalances() { - assertEquals("1000.00", Utils.parseCurrency("1.000.00").toPlainString()); - assertEquals("1000.50", Utils.parseCurrency("1.000.50").toPlainString()); - assertEquals("1000.50", Utils.parseCurrency("1.000,50").toPlainString()); - assertEquals("1000.50", Utils.parseCurrency("1,000,50").toPlainString()); - assertEquals("1000.50", Utils.parseCurrency("1,000.50").toPlainString()); - assertEquals("1000", Utils.parseCurrency("1000").toPlainString()); - assertEquals("995", Utils.parseCurrency("995").toPlainString()); - assertEquals("9.95", Utils.parseCurrency("9.95").toPlainString()); - assertEquals("9.95", Utils.parseCurrency("9,95").toPlainString()); + assertEquals("1", Utils.parseCurrency("1", false).toPlainString()); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("")); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(".")); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(",")); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(".,")); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("a")); - assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("......................")); + assertEquals("1", Utils.parseCurrency("1", true).toPlainString()); + assertEquals("1.00", Utils.parseCurrency("1.00", true).toPlainString()); + assertEquals("1.00", Utils.parseCurrency("1,00", true).toPlainString()); + + assertEquals("25", Utils.parseCurrency("2.5", false).toPlainString()); + assertEquals("25", Utils.parseCurrency("2,5", false).toPlainString()); + assertEquals("205", Utils.parseCurrency("2.05", false).toPlainString()); + assertEquals("205", Utils.parseCurrency("2,05", false).toPlainString()); + + assertEquals("2.5", Utils.parseCurrency("2.5", true).toPlainString()); + assertEquals("2.5", Utils.parseCurrency("2,5", true).toPlainString()); + assertEquals("2.05", Utils.parseCurrency("2.05", true).toPlainString()); + assertEquals("2.05", Utils.parseCurrency("2,05", true).toPlainString()); + assertEquals("2.50", Utils.parseCurrency("2.50", true).toPlainString()); + assertEquals("2.50", Utils.parseCurrency("2,50", true).toPlainString()); + + assertEquals("995", Utils.parseCurrency("9.95", false).toPlainString()); + assertEquals("995", Utils.parseCurrency("9,95", false).toPlainString()); + + assertEquals("9.95", Utils.parseCurrency("9.95", true).toPlainString()); + assertEquals("9.95", Utils.parseCurrency("9,95", true).toPlainString()); + + assertEquals("1234", Utils.parseCurrency("1234", false).toPlainString()); + assertEquals("1234", Utils.parseCurrency("1.234", false).toPlainString()); + assertEquals("1234", Utils.parseCurrency("1,234", false).toPlainString()); + + assertEquals("1234", Utils.parseCurrency("1234", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1234.00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1.234.00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1.234,00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1,234.00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1,234,00", true).toPlainString()); + } + + @Test + public void formatBalances() + { + Currency euro = Currency.getInstance("EUR"); + + assertEquals("1", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), null)); + assertEquals("1.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), euro)); + + assertEquals("25", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), null)); + assertEquals("25.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), euro)); + + assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), null)); + assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), euro)); + + assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), null)); + assertEquals("2.05", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), euro)); + + assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), null)); + assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), euro)); + + assertEquals("995", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), null)); + assertEquals("995.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), euro)); + + assertEquals("10", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), null)); + assertEquals("9.95", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), euro)); + + assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), null)); + assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), euro)); + + assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), null)); + assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), euro)); } } \ No newline at end of file From bc808d30452b9600044b26c439b4708d088b965f Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 23 Feb 2021 20:55:01 +0100 Subject: [PATCH 19/28] Really fix --- CHANGELOG.md | 1 + app/build.gradle | 2 +- .../card_locker/LoyaltyCardEditActivity.java | 22 +++++++++++-------- .../main/java/protect/card_locker/Utils.java | 2 ++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94740dec9..2bc678e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changes: - Improve balance parsing logic +- Fix currency decimal display on main screen ## v1.9 (2021-02-22) diff --git a/app/build.gradle b/app/build.gradle index 3340fda43..b24251fdb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { applicationId "me.hackerchick.catima" minSdkVersion 19 targetSdkVersion 29 - versionCode 60 + versionCode 61 versionName "1.9.1" vectorDrawables.useSupportLibrary true diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 33ae5fcc0..7bf92e5c4 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -246,7 +246,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity balanceCurrencyField.setTag(currency); - balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), currency)); + BigDecimal balance = (BigDecimal) balanceField.getTag(); + + if (balance != null) { + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(balance, currency)); + } } @Override @@ -366,10 +370,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity noteFieldEdit.setText(""); expiryField.setTag(null); expiryField.setText(""); - balanceField.setTag(null); - balanceField.setText(""); balanceCurrencyField.setTag(null); balanceCurrencyField.setText(""); + balanceField.setTag(null); + balanceField.setText(""); cardIdFieldView.setText(""); barcodeTypeField.setText(""); } @@ -409,18 +413,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity formatExpiryField(loyaltyCard.expiry); } - if(balanceField.getText().length() == 0) - { - balanceField.setTag(loyaltyCard.balance); - balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType)); - } - if(balanceCurrencyField.getText().length() == 0) { balanceCurrencyField.setTag(loyaltyCard.balanceType); formatBalanceCurrencyField(loyaltyCard.balanceType); } + if(balanceField.getText().length() == 0) + { + balanceField.setTag(loyaltyCard.balance); + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType)); + } + if(cardIdFieldView.getText().length() == 0) { cardIdFieldView.setText(loyaltyCard.cardId); diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index c045ff900..a6ba67032 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -94,6 +94,8 @@ public class Utils { NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); currencyFormat.setCurrency(currency); + currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits()); + currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits()); return currencyFormat.format(value); } From 1c92787254eb86ba6db34ccfcc1623435d9994de Mon Sep 17 00:00:00 2001 From: inesre Date: Mon, 22 Feb 2021 23:01:50 +0000 Subject: [PATCH 20/28] Translated using Weblate (Spanish) Currently translated at 94.5% (121 of 128 strings) Translation: Catima/Catima Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/ --- app/src/main/res/values-es/strings.xml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 04069fc5c..b6a18d5ee 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -103,4 +103,14 @@ %d tarjeta %d tarjetas - + Puntos + Centrar el código de barras en la pantalla + Mover el código de barras a la zona superior de la pantalla + Elegir fecha de caducidad + Nunca + Fecha de caducidad + Editar el código de barras + Código de barras + Tarjeta + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> puntos + \ No newline at end of file From d03d96b194a066c779ceb49c48a1767083a3598b Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Wed, 24 Feb 2021 12:28:04 +0100 Subject: [PATCH 21/28] Fix Weblate incorrect escaping --- app/src/main/res/values-es/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b6a18d5ee..d1c9a4992 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -112,5 +112,5 @@ Editar el código de barras Código de barras Tarjeta - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> puntos - \ No newline at end of file + %s puntos + From efc8e6ae33a033b4dd817f848126a9b8f897d3a3 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Wed, 24 Feb 2021 19:17:16 +0100 Subject: [PATCH 22/28] Fix parsing balance for countries using space as separator --- CHANGELOG.md | 6 ++++++ app/build.gradle | 4 ++-- app/src/main/java/protect/card_locker/Utils.java | 6 +++--- .../test/java/protect/card_locker/UtilsTest.java | 15 ++++++++++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc678e39..5c7ceefa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.9.2 (2021-02-24) + +Changes: + +- Fix parsing balance for countries using space as separator + ## v1.9.1 (2021-02-23) Changes: diff --git a/app/build.gradle b/app/build.gradle index b24251fdb..a7d9df8c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "me.hackerchick.catima" minSdkVersion 19 targetSdkVersion 29 - versionCode 61 - versionName "1.9.1" + versionCode 62 + versionName "1.9.2" vectorDrawables.useSupportLibrary true } diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index a6ba67032..287816965 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -125,13 +125,13 @@ public class Utils { static public BigDecimal parseCurrency(String value, Boolean hasDecimals) throws NumberFormatException { // If there are no decimals expected, remove all separators before parsing if (!hasDecimals) { - value = value.replaceAll("\\.", "").replaceAll(",", ""); + value = value.replaceAll("[^0-9]", ""); return new BigDecimal(value); } // There are many ways users can write a currency, so we fix it up a bit - // 1. Replace all commas with dots - value = value.replace(',', '.'); + // 1. Replace all non-numbers with dots + value = value.replaceAll("[^0-9]", "."); // 2. Remove all but the last dot while (value.split("\\.").length > 2) { diff --git a/app/src/test/java/protect/card_locker/UtilsTest.java b/app/src/test/java/protect/card_locker/UtilsTest.java index f4df8773d..761633feb 100644 --- a/app/src/test/java/protect/card_locker/UtilsTest.java +++ b/app/src/test/java/protect/card_locker/UtilsTest.java @@ -25,28 +25,37 @@ public class UtilsTest assertEquals("1", Utils.parseCurrency("1", true).toPlainString()); assertEquals("1.00", Utils.parseCurrency("1.00", true).toPlainString()); assertEquals("1.00", Utils.parseCurrency("1,00", true).toPlainString()); + assertEquals("1.00", Utils.parseCurrency("1 00", true).toPlainString()); assertEquals("25", Utils.parseCurrency("2.5", false).toPlainString()); assertEquals("25", Utils.parseCurrency("2,5", false).toPlainString()); + assertEquals("25", Utils.parseCurrency("2 5", false).toPlainString()); assertEquals("205", Utils.parseCurrency("2.05", false).toPlainString()); assertEquals("205", Utils.parseCurrency("2,05", false).toPlainString()); + assertEquals("205", Utils.parseCurrency("2 05", false).toPlainString()); assertEquals("2.5", Utils.parseCurrency("2.5", true).toPlainString()); assertEquals("2.5", Utils.parseCurrency("2,5", true).toPlainString()); + assertEquals("2.5", Utils.parseCurrency("2 5", true).toPlainString()); assertEquals("2.05", Utils.parseCurrency("2.05", true).toPlainString()); assertEquals("2.05", Utils.parseCurrency("2,05", true).toPlainString()); + assertEquals("2.05", Utils.parseCurrency("2 05", true).toPlainString()); assertEquals("2.50", Utils.parseCurrency("2.50", true).toPlainString()); assertEquals("2.50", Utils.parseCurrency("2,50", true).toPlainString()); + assertEquals("2.50", Utils.parseCurrency("2 50", true).toPlainString()); assertEquals("995", Utils.parseCurrency("9.95", false).toPlainString()); assertEquals("995", Utils.parseCurrency("9,95", false).toPlainString()); + assertEquals("995", Utils.parseCurrency("9 95", false).toPlainString()); assertEquals("9.95", Utils.parseCurrency("9.95", true).toPlainString()); assertEquals("9.95", Utils.parseCurrency("9,95", true).toPlainString()); + assertEquals("9.95", Utils.parseCurrency("9 95", true).toPlainString()); assertEquals("1234", Utils.parseCurrency("1234", false).toPlainString()); assertEquals("1234", Utils.parseCurrency("1.234", false).toPlainString()); assertEquals("1234", Utils.parseCurrency("1,234", false).toPlainString()); + assertEquals("1234", Utils.parseCurrency("1 234", false).toPlainString()); assertEquals("1234", Utils.parseCurrency("1234", true).toPlainString()); assertEquals("1234.00", Utils.parseCurrency("1234.00", true).toPlainString()); @@ -54,6 +63,10 @@ public class UtilsTest assertEquals("1234.00", Utils.parseCurrency("1.234,00", true).toPlainString()); assertEquals("1234.00", Utils.parseCurrency("1,234.00", true).toPlainString()); assertEquals("1234.00", Utils.parseCurrency("1,234,00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString()); + assertEquals("1234.00", Utils.parseCurrency("1 234 00", true).toPlainString()); + } @Test @@ -88,4 +101,4 @@ public class UtilsTest assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), null)); assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), euro)); } -} \ No newline at end of file +} From b558d2178a08c763763406d0a46ad469fc3d7a85 Mon Sep 17 00:00:00 2001 From: Simone Dotto Date: Wed, 24 Feb 2021 13:35:12 +0000 Subject: [PATCH 23/28] Translated using Weblate (Italian) Currently translated at 100.0% (128 of 128 strings) Translation: Catima/Catima Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/ --- app/src/main/res/values-it/strings.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 81e834f79..6ced08af2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -103,4 +103,20 @@ %d carta %d carte - + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> non sembra un saldo corretto. + Punti + Valuta + Saldo + Centra il codice a barre sullo schermo + Sposta il codice a barre in cima allo schermo + Scegli data di scadenza + Mai + Data di scadenza + Modifica il codice a barre + Codice a barre + Carta + <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> punti + Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> + Scaduta: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> + Scade: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> + \ No newline at end of file From 01bd91ff661f8ef4fb05c1739b8f0adfc2156df5 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 25 Feb 2021 19:54:46 +0100 Subject: [PATCH 24/28] Fix Weblate incorrectly escaping XML --- app/src/main/res/values-it/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6ced08af2..e854bfad5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -115,8 +115,8 @@ Modifica il codice a barre Codice a barre Carta - <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> punti - Saldo: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> - Scaduta: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> - Scade: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> - \ No newline at end of file + %s punti + Saldo: %s + Scaduta: %s + Scade: %s + From db22703ec0e62555dc49e3d65d877e9f25a1a440 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 1 Mar 2021 23:02:03 +0100 Subject: [PATCH 25/28] Create Voucher Vault import code and test --- .../java/protect/card_locker/DBHelper.java | 6 +- .../protect/card_locker/ImportExportTask.java | 3 + .../CsvDatabaseExporter.java | 6 +- .../CsvDatabaseImporter.java | 6 +- .../{ => importexport}/DatabaseExporter.java | 4 +- .../{ => importexport}/DatabaseImporter.java | 10 +- .../MultiFormatExporter.java | 7 +- .../MultiFormatImporter.java | 13 +- .../importexport/VoucherVaultImporter.java | 130 +++++++++++++++ .../protect/card_locker/ImportExportTest.java | 148 ++++++++++++------ 10 files changed, 276 insertions(+), 57 deletions(-) rename app/src/main/java/protect/card_locker/{ => importexport}/CsvDatabaseExporter.java (95%) rename app/src/main/java/protect/card_locker/{ => importexport}/CsvDatabaseImporter.java (98%) rename app/src/main/java/protect/card_locker/{ => importexport}/DatabaseExporter.java (83%) rename app/src/main/java/protect/card_locker/{ => importexport}/DatabaseImporter.java (60%) rename app/src/main/java/protect/card_locker/{ => importexport}/MultiFormatExporter.java (85%) rename app/src/main/java/protect/card_locker/{ => importexport}/MultiFormatImporter.java (76%) create mode 100644 app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 10a631eca..ce27a76cf 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -20,14 +20,14 @@ public class DBHelper extends SQLiteOpenHelper public static final int ORIGINAL_DATABASE_VERSION = 1; public static final int DATABASE_VERSION = 8; - static class LoyaltyCardDbGroups + public static class LoyaltyCardDbGroups { public static final String TABLE = "groups"; public static final String ID = "_id"; public static final String ORDER = "orderId"; } - static class LoyaltyCardDbIds + public static class LoyaltyCardDbIds { public static final String TABLE = "cards"; public static final String ID = "_id"; @@ -43,7 +43,7 @@ public class DBHelper extends SQLiteOpenHelper public static final String STAR_STATUS = "starstatus"; } - static class LoyaltyCardDbIdsGroups + public static class LoyaltyCardDbIdsGroups { public static final String TABLE = "cardsGroups"; public static final String cardID = "cardId"; diff --git a/app/src/main/java/protect/card_locker/ImportExportTask.java b/app/src/main/java/protect/card_locker/ImportExportTask.java index 8537fbe99..402d57181 100644 --- a/app/src/main/java/protect/card_locker/ImportExportTask.java +++ b/app/src/main/java/protect/card_locker/ImportExportTask.java @@ -13,6 +13,9 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import protect.card_locker.importexport.MultiFormatExporter; +import protect.card_locker.importexport.MultiFormatImporter; + class ImportExportTask extends AsyncTask { private static final String TAG = "Catima"; diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java b/app/src/main/java/protect/card_locker/importexport/CsvDatabaseExporter.java similarity index 95% rename from app/src/main/java/protect/card_locker/CsvDatabaseExporter.java rename to app/src/main/java/protect/card_locker/importexport/CsvDatabaseExporter.java index dd8a3037c..0d58f4403 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java +++ b/app/src/main/java/protect/card_locker/importexport/CsvDatabaseExporter.java @@ -1,4 +1,4 @@ -package protect.card_locker; +package protect.card_locker.importexport; import android.database.Cursor; @@ -8,6 +8,10 @@ import org.apache.commons.csv.CSVPrinter; import java.io.IOException; import java.io.OutputStreamWriter; +import protect.card_locker.DBHelper; +import protect.card_locker.Group; +import protect.card_locker.LoyaltyCard; + /** * Class for exporting the database into CSV (Comma Separate Values) * format. diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/importexport/CsvDatabaseImporter.java similarity index 98% rename from app/src/main/java/protect/card_locker/CsvDatabaseImporter.java rename to app/src/main/java/protect/card_locker/importexport/CsvDatabaseImporter.java index 5ba74393e..c2491bc42 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/CsvDatabaseImporter.java @@ -1,4 +1,4 @@ -package protect.card_locker; +package protect.card_locker.importexport; import android.database.sqlite.SQLiteDatabase; @@ -15,6 +15,10 @@ import java.util.Currency; import java.util.Date; import java.util.List; +import protect.card_locker.DBHelper; +import protect.card_locker.FormatException; +import protect.card_locker.Group; + /** * Class for importing a database from CSV (Comma Separate Values) * formatted data. diff --git a/app/src/main/java/protect/card_locker/DatabaseExporter.java b/app/src/main/java/protect/card_locker/importexport/DatabaseExporter.java similarity index 83% rename from app/src/main/java/protect/card_locker/DatabaseExporter.java rename to app/src/main/java/protect/card_locker/importexport/DatabaseExporter.java index 94e4949d6..a12a124af 100644 --- a/app/src/main/java/protect/card_locker/DatabaseExporter.java +++ b/app/src/main/java/protect/card_locker/importexport/DatabaseExporter.java @@ -1,8 +1,10 @@ -package protect.card_locker; +package protect.card_locker.importexport; import java.io.IOException; import java.io.OutputStreamWriter; +import protect.card_locker.DBHelper; + /** * Interface for a class which can export the contents of the database * in a given format. diff --git a/app/src/main/java/protect/card_locker/DatabaseImporter.java b/app/src/main/java/protect/card_locker/importexport/DatabaseImporter.java similarity index 60% rename from app/src/main/java/protect/card_locker/DatabaseImporter.java rename to app/src/main/java/protect/card_locker/importexport/DatabaseImporter.java index 33d2797bf..867454385 100644 --- a/app/src/main/java/protect/card_locker/DatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/DatabaseImporter.java @@ -1,7 +1,13 @@ -package protect.card_locker; +package protect.card_locker.importexport; + +import org.json.JSONException; import java.io.IOException; import java.io.InputStreamReader; +import java.text.ParseException; + +import protect.card_locker.DBHelper; +import protect.card_locker.FormatException; /** * Interface for a class which can import the contents of a stream @@ -15,5 +21,5 @@ public interface DatabaseImporter * @throws IOException * @throws FormatException */ - void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException; + void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException, JSONException, ParseException; } diff --git a/app/src/main/java/protect/card_locker/MultiFormatExporter.java b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java similarity index 85% rename from app/src/main/java/protect/card_locker/MultiFormatExporter.java rename to app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java index 5298e2ad7..45f702b4d 100644 --- a/app/src/main/java/protect/card_locker/MultiFormatExporter.java +++ b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java @@ -1,10 +1,15 @@ -package protect.card_locker; +package protect.card_locker.importexport; import android.util.Log; import java.io.IOException; import java.io.OutputStreamWriter; +import protect.card_locker.DBHelper; +import protect.card_locker.DataFormat; +import protect.card_locker.importexport.CsvDatabaseExporter; +import protect.card_locker.importexport.DatabaseExporter; + public class MultiFormatExporter { private static final String TAG = "Catima"; diff --git a/app/src/main/java/protect/card_locker/MultiFormatImporter.java b/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java similarity index 76% rename from app/src/main/java/protect/card_locker/MultiFormatImporter.java rename to app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java index 6a27af7e4..62d5af905 100644 --- a/app/src/main/java/protect/card_locker/MultiFormatImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java @@ -1,9 +1,18 @@ -package protect.card_locker; +package protect.card_locker.importexport; import android.util.Log; +import org.json.JSONException; + import java.io.IOException; import java.io.InputStreamReader; +import java.text.ParseException; + +import protect.card_locker.DBHelper; +import protect.card_locker.DataFormat; +import protect.card_locker.FormatException; +import protect.card_locker.importexport.CsvDatabaseImporter; +import protect.card_locker.importexport.DatabaseImporter; public class MultiFormatImporter { @@ -38,7 +47,7 @@ public class MultiFormatImporter importer.importData(db, input); return true; } - catch(IOException | FormatException | InterruptedException e) + catch(IOException | FormatException | InterruptedException | JSONException | ParseException e) { Log.e(TAG, "Failed to import data", e); } diff --git a/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java b/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java new file mode 100644 index 000000000..837cddc59 --- /dev/null +++ b/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java @@ -0,0 +1,130 @@ +package protect.card_locker.importexport; + +import android.annotation.SuppressLint; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Color; + +import com.google.zxing.BarcodeFormat; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Currency; +import java.util.Date; + +import protect.card_locker.DBHelper; +import protect.card_locker.FormatException; + +/** + * Class for importing a database from CSV (Comma Separate Values) + * formatted data. + * + * The database's loyalty cards are expected to appear in the CSV data. + * A header is expected for the each table showing the names of the columns. + */ +public class VoucherVaultImporter implements DatabaseImporter +{ + public void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, JSONException, ParseException { + BufferedReader bufferedReader = new BufferedReader(input); + + StringBuilder sb = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line); + } + JSONArray jsonArray = new JSONArray(sb.toString()); + + SQLiteDatabase database = db.getWritableDatabase(); + database.beginTransaction(); + + // See https://github.com/tim-smart/vouchervault/issues/4#issuecomment-788226503 for more info + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jsonCard = jsonArray.getJSONObject(i); + + String store = jsonCard.getString("description"); + + Date expiry = null; + if (!jsonCard.isNull("expires")) { + @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + expiry = dateFormat.parse(jsonCard.getString("expires")); + } + + BigDecimal balance; + if (!jsonCard.isNull("balance")) { + balance = new BigDecimal(String.valueOf(jsonCard.getDouble("balance"))); + } else { + balance = new BigDecimal("0"); + } + + Currency balanceType = Currency.getInstance("USD"); + + String cardId = jsonCard.getString("code"); + + String barcodeType = null; + + String codeTypeFromJSON = jsonCard.getString("codeType"); + switch (codeTypeFromJSON) { + case "CODE128": + barcodeType = BarcodeFormat.CODE_128.name(); + break; + case "CODE39": + barcodeType = BarcodeFormat.CODE_39.name(); + break; + case "EAN13": + barcodeType = BarcodeFormat.EAN_13.name(); + break; + case "QR": + barcodeType = BarcodeFormat.QR_CODE.name(); + break; + case "TEXT": + break; + default: + throw new FormatException("Unknown barcode type found: " + codeTypeFromJSON); + } + + int headerColor; + + String colorFromJSON = jsonCard.getString("color"); + switch (colorFromJSON) { + case "GREY": + headerColor = Color.GRAY; + break; + case "BLUE": + headerColor = Color.BLUE; + break; + case "GREEN": + headerColor = Color.GREEN; + break; + case "ORANGE": + headerColor = Color.rgb(255, 165, 0); + break; + case "PURPLE": + headerColor = Color.rgb(128, 0, 128); + break; + case "RED": + headerColor = Color.RED; + break; + case "YELLOW": + headerColor = Color.YELLOW; + break; + default: + throw new FormatException("Unknown colour type foun: " + colorFromJSON); + } + + db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, barcodeType, headerColor, 0); + } + + database.setTransactionSuccessful(); + database.endTransaction(); + database.close(); + + bufferedReader.close(); + } +} \ No newline at end of file diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index 2baf9ee73..e31e048ea 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -3,10 +3,12 @@ package protect.card_locker; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.graphics.Color; import android.os.Environment; import com.google.zxing.BarcodeFormat; +import org.json.JSONException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,11 +28,17 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Currency; import java.util.Date; import java.util.List; +import protect.card_locker.importexport.MultiFormatExporter; +import protect.card_locker.importexport.MultiFormatImporter; +import protect.card_locker.importexport.VoucherVaultImporter; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -112,20 +120,65 @@ public class ImportExportTest boolean result = (id != -1); assertTrue(result); + LoyaltyCard card = db.getLoyaltyCard((int) id); + assertEquals("No Expiry", card.store); + assertEquals("", card.note); + assertEquals(null, card.expiry); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals(BARCODE_DATA, card.cardId); + assertEquals(BARCODE_TYPE, card.barcodeType); + assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(0, card.starStatus); + id = db.insertLoyaltyCard("Past", "", new Date((long) 1), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); + card = db.getLoyaltyCard((int) id); + assertEquals("Past", card.store); + assertEquals("", card.note); + assertTrue(card.expiry.before(new Date())); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals(BARCODE_DATA, card.cardId); + assertEquals(BARCODE_TYPE, card.barcodeType); + assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(0, card.starStatus); + id = db.insertLoyaltyCard("Today", "", new Date(), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); + card = db.getLoyaltyCard((int) id); + assertEquals("Today", card.store); + assertEquals("", card.note); + assertTrue(card.expiry.before(new Date(new Date().getTime()+86400))); + assertTrue(card.expiry.after(new Date(new Date().getTime()-86400))); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals(BARCODE_DATA, card.cardId); + assertEquals(BARCODE_TYPE, card.barcodeType); + assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(0, card.starStatus); + // This will break after 19 January 2038 // If someone is still maintaining this code base by then: I love you - id = db.insertLoyaltyCard("Future", "", new Date(2147483648L), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); + id = db.insertLoyaltyCard("Future", "", new Date(2147483648000L), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0); result = (id != -1); assertTrue(result); + card = db.getLoyaltyCard((int) id); + assertEquals("Future", card.store); + assertEquals("", card.note); + assertTrue(card.expiry.after(new Date(new Date().getTime()+86400))); + assertEquals(new BigDecimal("0"), card.balance); + assertEquals(null, card.balanceType); + assertEquals(BARCODE_DATA, card.cardId); + assertEquals(BARCODE_TYPE, card.barcodeType); + assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(0, card.starStatus); + assertEquals(4, db.getLoyaltyCardCount()); } @@ -830,59 +883,62 @@ public class ImportExportTest clearDatabase(); } + @Test + public void importVoucherVault() throws IOException, FormatException, JSONException, ParseException { + String jsonText = "[\n" + + " {\n" + + " \"uuid\": \"ae1ae525-3f27-481e-853a-8c30b7fa12d8\",\n" + + " \"description\": \"Clothes Store\",\n" + + " \"code\": \"123456\",\n" + + " \"codeType\": \"CODE128\",\n" + + " \"expires\": null,\n" + + " \"removeOnceExpired\": true,\n" + + " \"balance\": null,\n" + + " \"color\": \"GREY\"\n" + + " },\n" + + " {\n" + + " \"uuid\": \"29a5d3b3-eace-4311-a15c-4c7e6a010531\",\n" + + " \"description\": \"Department Store\",\n" + + " \"code\": \"26846363\",\n" + + " \"codeType\": \"CODE39\",\n" + + " \"expires\": \"2021-03-26T00:00:00.000\",\n" + + " \"removeOnceExpired\": true,\n" + + " \"balance\": 3.5,\n" + + " \"color\": \"PURPLE\"\n" + + " }\n" + + "]"; - private void checkLoyaltyCardsExpiry() - { - Cursor cursor = db.getLoyaltyCardCursor(); - cursor.moveToNext(); - LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor); - assertEquals("Never", card.store); + ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonText.getBytes(StandardCharsets.UTF_8)); + InputStreamReader inStream = new InputStreamReader(inputStream); + + // Import the Voucher Vault data + new VoucherVaultImporter().importData(db, inStream); + assertEquals(2, db.getLoyaltyCardCount()); + + LoyaltyCard card = db.getLoyaltyCard(1); + + assertEquals("Clothes Store", card.store); assertEquals("", card.note); assertEquals(null, card.expiry); assertEquals(new BigDecimal("0"), card.balance); - assertEquals(null, card.balanceType); - assertEquals(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(Currency.getInstance("USD"), card.balanceType); + assertEquals("123456", card.cardId); + assertEquals(BarcodeFormat.CODE_128.name(), card.barcodeType); assertEquals(0, card.starStatus); + assertEquals(Color.GRAY, (long) card.headerColor); - cursor.moveToNext(); - card = LoyaltyCard.toLoyaltyCard(cursor); - assertEquals("Past", card.store); + card = db.getLoyaltyCard(2); + + assertEquals("Department Store", card.store); assertEquals("", card.note); - assertTrue(card.expiry.before(new Date())); - assertEquals(new BigDecimal("0"), card.balance); - assertEquals(null, card.balanceType); - assertEquals(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(new Date(1616713200000L), card.expiry); + assertEquals(new BigDecimal("3.5"), card.balance); + assertEquals(Currency.getInstance("USD"), card.balanceType); + assertEquals("26846363", card.cardId); + assertEquals(BarcodeFormat.CODE_39.name(), card.barcodeType); assertEquals(0, card.starStatus); + assertEquals(Color.rgb(128, 0, 128), (long) card.headerColor); - cursor.moveToNext(); - card = LoyaltyCard.toLoyaltyCard(cursor); - assertEquals("Today", card.store); - assertEquals("", card.note); - assertTrue(card.expiry.before(new Date(new Date().getTime()+86400))); - assertTrue(card.expiry.after(new Date(new Date().getTime()-86400))); - assertEquals(new BigDecimal("0"), card.balance); - assertEquals(null, card.balanceType); - assertEquals(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); - assertEquals(0, card.starStatus); - - cursor.moveToNext(); - card = LoyaltyCard.toLoyaltyCard(cursor); - assertEquals("Future", card.store); - assertEquals("", card.note); - assertTrue(card.expiry.after(new Date(new Date().getTime()+86400))); - assertEquals(new BigDecimal("0"), card.balance); - assertEquals(null, card.balanceType); - assertEquals(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); - assertEquals(0, card.starStatus); - - cursor.close(); + clearDatabase(); } } From 40de4a8dc4925460766870cf05fffebd363f9a12 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 2 Mar 2021 21:10:14 +0100 Subject: [PATCH 26/28] Fix date parsing --- .../protect/card_locker/importexport/VoucherVaultImporter.java | 2 ++ app/src/test/java/protect/card_locker/ImportExportTest.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java b/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java index 837cddc59..317adb65a 100644 --- a/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java @@ -18,6 +18,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Currency; import java.util.Date; +import java.util.TimeZone; import protect.card_locker.DBHelper; import protect.card_locker.FormatException; @@ -53,6 +54,7 @@ public class VoucherVaultImporter implements DatabaseImporter Date expiry = null; if (!jsonCard.isNull("expires")) { @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); expiry = dateFormat.parse(jsonCard.getString("expires")); } diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index e31e048ea..17823f253 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -931,7 +931,7 @@ public class ImportExportTest assertEquals("Department Store", card.store); assertEquals("", card.note); - assertEquals(new Date(1616713200000L), card.expiry); + assertEquals(new Date(1616716800000L), card.expiry); assertEquals(new BigDecimal("3.5"), card.balance); assertEquals(Currency.getInstance("USD"), card.balanceType); assertEquals("26846363", card.cardId); From ffa39000f7813842410d827c987fd2e3c9d2d04e Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 4 Mar 2021 23:35:44 +0100 Subject: [PATCH 27/28] Add Voucher Vault import to UI --- CHANGELOG.md | 6 + .../java/protect/card_locker/DataFormat.java | 4 +- .../card_locker/ImportExportActivity.java | 44 ++- .../importexport/MultiFormatExporter.java | 2 +- .../importexport/MultiFormatImporter.java | 5 +- app/src/main/res/values/arrays.xml | 8 + app/src/main/res/values/strings.xml | 1 + .../protect/card_locker/ImportExportTest.java | 297 +++++++++--------- 8 files changed, 198 insertions(+), 169 deletions(-) create mode 100644 app/src/main/res/values/arrays.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c7ceefa3..56951fb39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +Changes: + +- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports + ## v1.9.2 (2021-02-24) Changes: diff --git a/app/src/main/java/protect/card_locker/DataFormat.java b/app/src/main/java/protect/card_locker/DataFormat.java index 722700f00..4b4b23e08 100644 --- a/app/src/main/java/protect/card_locker/DataFormat.java +++ b/app/src/main/java/protect/card_locker/DataFormat.java @@ -2,7 +2,7 @@ package protect.card_locker; public enum DataFormat { - CSV, - + Catima, + VoucherVault ; } diff --git a/app/src/main/java/protect/card_locker/ImportExportActivity.java b/app/src/main/java/protect/card_locker/ImportExportActivity.java index f5d89238d..cb70b396f 100644 --- a/app/src/main/java/protect/card_locker/ImportExportActivity.java +++ b/app/src/main/java/protect/card_locker/ImportExportActivity.java @@ -35,10 +35,12 @@ public class ImportExportActivity extends AppCompatActivity private static final int PERMISSIONS_EXTERNAL_STORAGE = 1; private static final int CHOOSE_EXPORT_LOCATION = 2; - private static final int CHOOSE_EXPORTED_FILE = 3; + private static final int IMPORT = 3; private ImportExportTask importExporter; + private DataFormat importDataFormat; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -93,7 +95,7 @@ public class ImportExportActivity extends AppCompatActivity @Override public void onClick(View v) { - chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE); + chooseImportType(intentGetContentAction); } }); @@ -106,12 +108,35 @@ public class ImportExportActivity extends AppCompatActivity @Override public void onClick(View v) { - chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE); + chooseImportType(intentPickAction); } }); } - private void startImport(final InputStream target, final Uri targetUri) + private void chooseImportType(Intent baseIntent) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.chooseImportType) + .setItems(R.array.import_types_array, (dialog, which) -> { + switch (which) { + // Catima + case 0: + // Loyalty Card Keychain + case 1: + importDataFormat = DataFormat.Catima; + break; + // Voucher Vault + case 2: + importDataFormat = DataFormat.VoucherVault; + break; + default: + throw new IllegalArgumentException("Unknown DataFormat"); + } + chooseFileWithIntent(baseIntent, IMPORT); + }); + builder.show(); + } + + private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat) { ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener() { @@ -123,7 +148,7 @@ public class ImportExportActivity extends AppCompatActivity }; importExporter = new ImportExportTask(ImportExportActivity.this, - DataFormat.CSV, target, listener); + dataFormat, target, listener); importExporter.execute(); } @@ -139,7 +164,7 @@ public class ImportExportActivity extends AppCompatActivity }; importExporter = new ImportExportTask(ImportExportActivity.this, - DataFormat.CSV, target, listener); + DataFormat.Catima, target, listener); importExporter.execute(); } @@ -294,7 +319,7 @@ public class ImportExportActivity extends AppCompatActivity { super.onActivityResult(requestCode, resultCode, data); - if (resultCode != RESULT_OK || (requestCode != CHOOSE_EXPORT_LOCATION && requestCode != CHOOSE_EXPORTED_FILE)) + if (resultCode != RESULT_OK) { Log.w(TAG, "Failed onActivityResult(), result=" + resultCode); return; @@ -336,8 +361,9 @@ public class ImportExportActivity extends AppCompatActivity reader = new FileInputStream(new File(uri.toString())); } - Log.e(TAG, "Starting file export with: " + uri.toString()); - startImport(reader, uri); + Log.e(TAG, "Starting file import with: " + uri.toString()); + + startImport(reader, uri, importDataFormat); } } catch(FileNotFoundException e) diff --git a/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java index 45f702b4d..f6509d561 100644 --- a/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java +++ b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java @@ -30,7 +30,7 @@ public class MultiFormatExporter switch(format) { - case CSV: + case Catima: exporter = new CsvDatabaseExporter(); break; } diff --git a/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java b/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java index 62d5af905..3186440cd 100644 --- a/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java +++ b/app/src/main/java/protect/card_locker/importexport/MultiFormatImporter.java @@ -35,9 +35,12 @@ public class MultiFormatImporter switch(format) { - case CSV: + case Catima: importer = new CsvDatabaseImporter(); break; + case VoucherVault: + importer = new VoucherVaultImporter(); + break; } if (importer != null) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 000000000..303cc27bf --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,8 @@ + + + + Catima + Loyalty Card Keychain + Voucher Vault + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 71260b998..db69108fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,4 +151,5 @@ Points %s does not seem to be a valid balance. + Import data from which app? diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index 17823f253..11413b6b8 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -37,7 +37,6 @@ import java.util.List; import protect.card_locker.importexport.MultiFormatExporter; import protect.card_locker.importexport.MultiFormatImporter; -import protect.card_locker.importexport.VoucherVaultImporter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -322,34 +321,31 @@ public class ImportExportTest { final int NUM_CARDS = 10; - for(DataFormat format : DataFormat.values()) - { - addLoyaltyCards(NUM_CARDS); + addLoyaltyCards(NUM_CARDS); - ByteArrayOutputStream outData = new ByteArrayOutputStream(); - OutputStreamWriter outStream = new OutputStreamWriter(outData); + ByteArrayOutputStream outData = new ByteArrayOutputStream(); + OutputStreamWriter outStream = new OutputStreamWriter(outData); - // Export data to CSV format - boolean result = MultiFormatExporter.exportData(db, outStream, format); - assertTrue(result); - outStream.close(); + // Export data to CSV format + boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima); + assertTrue(result); + outStream.close(); - clearDatabase(); + clearDatabase(); - ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); - InputStreamReader inStream = new InputStreamReader(inData); + ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); + InputStreamReader inStream = new InputStreamReader(inData); - // Import the CSV data - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); - assertTrue(result); + // Import the CSV data + result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); + assertTrue(result); - assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); + assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); - checkLoyaltyCards(); + checkLoyaltyCards(); - // Clear the database for the next format under test - clearDatabase(); - } + // Clear the database for the next format under test + clearDatabase(); } @Test @@ -357,34 +353,31 @@ public class ImportExportTest { final int NUM_CARDS = 9; - for(DataFormat format : DataFormat.values()) - { - addLoyaltyCardsFiveStarred(); + addLoyaltyCardsFiveStarred(); - ByteArrayOutputStream outData = new ByteArrayOutputStream(); - OutputStreamWriter outStream = new OutputStreamWriter(outData); + ByteArrayOutputStream outData = new ByteArrayOutputStream(); + OutputStreamWriter outStream = new OutputStreamWriter(outData); - // Export data to CSV format - boolean result = MultiFormatExporter.exportData(db, outStream, format); - assertTrue(result); - outStream.close(); + // Export data to CSV format + boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima); + assertTrue(result); + outStream.close(); - clearDatabase(); + clearDatabase(); - ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); - InputStreamReader inStream = new InputStreamReader(inData); + ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); + InputStreamReader inStream = new InputStreamReader(inData); - // Import the CSV data - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); - assertTrue(result); + // Import the CSV data + result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); + assertTrue(result); - assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); + assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); - checkLoyaltyCardsFiveStarred(); + checkLoyaltyCardsFiveStarred(); - // Clear the database for the next format under test - clearDatabase(); - } + // Clear the database for the next format under test + clearDatabase(); } private List groupsToGroupNames(List groups) @@ -404,77 +397,74 @@ public class ImportExportTest final int NUM_CARDS = 10; final int NUM_GROUPS = 3; - for(DataFormat format : DataFormat.values()) - { - addLoyaltyCards(NUM_CARDS); - addGroups(NUM_GROUPS); + addLoyaltyCards(NUM_CARDS); + addGroups(NUM_GROUPS); - List emptyGroup = new ArrayList<>(); + List emptyGroup = new ArrayList<>(); - List groupsForOne = new ArrayList<>(); - groupsForOne.add(db.getGroup("group, \" 1")); + List groupsForOne = new ArrayList<>(); + groupsForOne.add(db.getGroup("group, \" 1")); - List groupsForTwo = new ArrayList<>(); - groupsForTwo.add(db.getGroup("group, \" 1")); - groupsForTwo.add(db.getGroup("group, \" 2")); + List groupsForTwo = new ArrayList<>(); + groupsForTwo.add(db.getGroup("group, \" 1")); + groupsForTwo.add(db.getGroup("group, \" 2")); - List groupsForThree = new ArrayList<>(); - groupsForThree.add(db.getGroup("group, \" 1")); - groupsForThree.add(db.getGroup("group, \" 2")); - groupsForThree.add(db.getGroup("group, \" 3")); + List groupsForThree = new ArrayList<>(); + groupsForThree.add(db.getGroup("group, \" 1")); + groupsForThree.add(db.getGroup("group, \" 2")); + groupsForThree.add(db.getGroup("group, \" 3")); - List groupsForFour = new ArrayList<>(); - groupsForFour.add(db.getGroup("group, \" 1")); - groupsForFour.add(db.getGroup("group, \" 2")); - groupsForFour.add(db.getGroup("group, \" 3")); + List groupsForFour = new ArrayList<>(); + groupsForFour.add(db.getGroup("group, \" 1")); + groupsForFour.add(db.getGroup("group, \" 2")); + groupsForFour.add(db.getGroup("group, \" 3")); - List groupsForFive = new ArrayList<>(); - groupsForFive.add(db.getGroup("group, \" 1")); - groupsForFive.add(db.getGroup("group, \" 3")); + List groupsForFive = new ArrayList<>(); + groupsForFive.add(db.getGroup("group, \" 1")); + groupsForFive.add(db.getGroup("group, \" 3")); - db.setLoyaltyCardGroups(1, groupsForOne); - db.setLoyaltyCardGroups(2, groupsForTwo); - db.setLoyaltyCardGroups(3, groupsForThree); - db.setLoyaltyCardGroups(4, groupsForFour); - db.setLoyaltyCardGroups(5, groupsForFive); + db.setLoyaltyCardGroups(1, groupsForOne); + db.setLoyaltyCardGroups(2, groupsForTwo); + db.setLoyaltyCardGroups(3, groupsForThree); + db.setLoyaltyCardGroups(4, groupsForFour); + db.setLoyaltyCardGroups(5, groupsForFive); - ByteArrayOutputStream outData = new ByteArrayOutputStream(); - OutputStreamWriter outStream = new OutputStreamWriter(outData); + ByteArrayOutputStream outData = new ByteArrayOutputStream(); + OutputStreamWriter outStream = new OutputStreamWriter(outData); - // Export data to CSV format - boolean result = MultiFormatExporter.exportData(db, outStream, format); - assertTrue(result); - outStream.close(); + // Export data to CSV format + boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima); + assertTrue(result); + outStream.close(); - clearDatabase(); + clearDatabase(); - ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); - InputStreamReader inStream = new InputStreamReader(inData); + ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); + InputStreamReader inStream = new InputStreamReader(inData); - // Import the CSV data - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); - assertTrue(result); + // Import the CSV data + result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); + assertTrue(result); - assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); - assertEquals(NUM_GROUPS, db.getGroupCount()); + assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); + assertEquals(NUM_GROUPS, db.getGroupCount()); - checkLoyaltyCards(); - checkGroups(); + checkLoyaltyCards(); + checkGroups(); - assertEquals(groupsToGroupNames(groupsForOne), groupsToGroupNames(db.getLoyaltyCardGroups(1))); - assertEquals(groupsToGroupNames(groupsForTwo), groupsToGroupNames(db.getLoyaltyCardGroups(2))); - assertEquals(groupsToGroupNames(groupsForThree), groupsToGroupNames(db.getLoyaltyCardGroups(3))); - assertEquals(groupsToGroupNames(groupsForFour), groupsToGroupNames(db.getLoyaltyCardGroups(4))); - assertEquals(groupsToGroupNames(groupsForFive), groupsToGroupNames(db.getLoyaltyCardGroups(5))); - assertEquals(emptyGroup, db.getLoyaltyCardGroups(6)); - assertEquals(emptyGroup, db.getLoyaltyCardGroups(7)); - assertEquals(emptyGroup, db.getLoyaltyCardGroups(8)); - assertEquals(emptyGroup, db.getLoyaltyCardGroups(9)); - assertEquals(emptyGroup, db.getLoyaltyCardGroups(10)); + assertEquals(groupsToGroupNames(groupsForOne), groupsToGroupNames(db.getLoyaltyCardGroups(1))); + assertEquals(groupsToGroupNames(groupsForTwo), groupsToGroupNames(db.getLoyaltyCardGroups(2))); + assertEquals(groupsToGroupNames(groupsForThree), groupsToGroupNames(db.getLoyaltyCardGroups(3))); + assertEquals(groupsToGroupNames(groupsForFour), groupsToGroupNames(db.getLoyaltyCardGroups(4))); + assertEquals(groupsToGroupNames(groupsForFive), groupsToGroupNames(db.getLoyaltyCardGroups(5))); + assertEquals(emptyGroup, db.getLoyaltyCardGroups(6)); + assertEquals(emptyGroup, db.getLoyaltyCardGroups(7)); + assertEquals(emptyGroup, db.getLoyaltyCardGroups(8)); + assertEquals(emptyGroup, db.getLoyaltyCardGroups(9)); + assertEquals(emptyGroup, db.getLoyaltyCardGroups(10)); - // Clear the database for the next format under test - clearDatabase(); - } + // Clear the database for the next format under test + clearDatabase(); } @Test @@ -482,32 +472,29 @@ public class ImportExportTest { final int NUM_CARDS = 10; - for(DataFormat format : DataFormat.values()) - { - addLoyaltyCards(NUM_CARDS); + addLoyaltyCards(NUM_CARDS); - ByteArrayOutputStream outData = new ByteArrayOutputStream(); - OutputStreamWriter outStream = new OutputStreamWriter(outData); + ByteArrayOutputStream outData = new ByteArrayOutputStream(); + OutputStreamWriter outStream = new OutputStreamWriter(outData); - // Export into CSV data - boolean result = MultiFormatExporter.exportData(db, outStream, format); - assertTrue(result); - outStream.close(); + // Export into CSV data + boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima); + assertTrue(result); + outStream.close(); - ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); - InputStreamReader inStream = new InputStreamReader(inData); + ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray()); + InputStreamReader inStream = new InputStreamReader(inData); - // Import the CSV data on top of the existing database - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); - assertTrue(result); + // Import the CSV data on top of the existing database + result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); + assertTrue(result); - assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); + assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); - checkLoyaltyCards(); + checkLoyaltyCards(); - // Clear the database for the next format under test - clearDatabase(); - } + // Clear the database for the next format under test + clearDatabase(); } @Test @@ -523,7 +510,7 @@ public class ImportExportTest OutputStreamWriter outStream = new OutputStreamWriter(outData); // Export data to CSV format - boolean result = MultiFormatExporter.exportData(db, outStream, format); + boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima); assertTrue(result); clearDatabase(); @@ -537,8 +524,8 @@ public class ImportExportTest ByteArrayInputStream inData = new ByteArrayInputStream((outData.toString() + corruptEntry).getBytes()); InputStreamReader inStream = new InputStreamReader(inData); - // Attempt to import the CSV data - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + // Attempt to import the data + result = MultiFormatImporter.importData(db, inStream, format); assertEquals(false, result); assertEquals(0, db.getLoyaltyCardCount()); @@ -566,49 +553,46 @@ public class ImportExportTest final File sdcardDir = Environment.getExternalStorageDirectory(); final File exportFile = new File(sdcardDir, "Catima.csv"); - for(DataFormat format : DataFormat.values()) - { - addLoyaltyCards(NUM_CARDS); + addLoyaltyCards(NUM_CARDS); - TestTaskCompleteListener listener = new TestTaskCompleteListener(); + TestTaskCompleteListener listener = new TestTaskCompleteListener(); - // Export to the file - FileOutputStream fileOutputStream = new FileOutputStream(exportFile); - ImportExportTask task = new ImportExportTask(activity, format, fileOutputStream, listener); - task.execute(); + // Export to the file + FileOutputStream fileOutputStream = new FileOutputStream(exportFile); + ImportExportTask task = new ImportExportTask(activity, DataFormat.Catima, fileOutputStream, listener); + task.execute(); - // Actually run the task to completion - Robolectric.flushBackgroundThreadScheduler(); + // Actually run the task to completion + Robolectric.flushBackgroundThreadScheduler(); - // Check that the listener was executed - assertNotNull(listener.success); - assertEquals(true, listener.success); + // Check that the listener was executed + assertNotNull(listener.success); + assertEquals(true, listener.success); - clearDatabase(); + clearDatabase(); - // Import everything back from the default location + // Import everything back from the default location - listener = new TestTaskCompleteListener(); + listener = new TestTaskCompleteListener(); - FileInputStream fileStream = new FileInputStream(exportFile); + FileInputStream fileStream = new FileInputStream(exportFile); - task = new ImportExportTask(activity, format, fileStream, listener); - task.execute(); + task = new ImportExportTask(activity, DataFormat.Catima, fileStream, listener); + task.execute(); - // Actually run the task to completion - Robolectric.flushBackgroundThreadScheduler(); + // Actually run the task to completion + Robolectric.flushBackgroundThreadScheduler(); - // Check that the listener was executed - assertNotNull(listener.success); - assertEquals(true, listener.success); + // Check that the listener was executed + assertNotNull(listener.success); + assertEquals(true, listener.success); - assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); + assertEquals(NUM_CARDS, db.getLoyaltyCardCount()); - checkLoyaltyCards(); + checkLoyaltyCards(); - // Clear the database for the next format under test - clearDatabase(); - } + // Clear the database for the next format under test + clearDatabase(); } @Test @@ -628,7 +612,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -666,7 +650,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -704,7 +688,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertEquals(false, result); assertEquals(0, db.getLoyaltyCardCount()); @@ -730,7 +714,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertEquals(true, result); assertEquals(1, db.getLoyaltyCardCount()); @@ -768,7 +752,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertEquals(true, result); assertEquals(1, db.getLoyaltyCardCount()); @@ -806,7 +790,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertEquals(true, result); assertEquals(1, db.getLoyaltyCardCount()); @@ -844,7 +828,7 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the CSV data - boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -864,7 +848,7 @@ public class ImportExportTest inStream = new InputStreamReader(inputStream); // Import the CSV data - result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); + result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -912,7 +896,8 @@ public class ImportExportTest InputStreamReader inStream = new InputStreamReader(inputStream); // Import the Voucher Vault data - new VoucherVaultImporter().importData(db, inStream); + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.VoucherVault); + assertTrue(result); assertEquals(2, db.getLoyaltyCardCount()); LoyaltyCard card = db.getLoyaltyCard(1); From c13b5dda1811d57076e4825853583b51f9bb5c87 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 4 Mar 2021 23:45:03 +0100 Subject: [PATCH 28/28] Make Spotbugs happy --- .../protect/card_locker/importexport/MultiFormatExporter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java index f6509d561..fedc446e7 100644 --- a/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java +++ b/app/src/main/java/protect/card_locker/importexport/MultiFormatExporter.java @@ -33,6 +33,9 @@ public class MultiFormatExporter case Catima: exporter = new CsvDatabaseExporter(); break; + default: + Log.e(TAG, "Failed to export data, unknown format " + format.name()); + break; } if(exporter != null)