diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc08c7ec..56951fb39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## Unreleased + +Changes: + +- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports + +## v1.9.2 (2021-02-24) + +Changes: + +- Fix parsing balance for countries using space as separator + +## v1.9.1 (2021-02-23) + +Changes: + +- Improve balance parsing logic +- Fix currency decimal display on main screen + +## v1.9 (2021-02-22) + +Changes: + +- Add balance support +- Reorganize barcode tab of edit view + ## v1.8.1 (2021-02-12) Changes: diff --git a/app/build.gradle b/app/build.gradle index 829169df6..a7d9df8c6 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 62 + versionName "1.9.2" vectorDrawables.useSupportLibrary true } diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 1c91b48e6..ce27a76cf 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -8,6 +8,8 @@ import android.database.sqlite.SQLiteException; 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; @@ -16,21 +18,23 @@ 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 + 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"; public static final String STORE = "store"; public static final String EXPIRY = "expiry"; + public static final String BALANCE = "balance"; + 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"; @@ -39,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"; @@ -60,11 +64,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'," + + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," + LoyaltyCardDbIds.CARD_ID + " TEXT not null," + @@ -128,9 +135,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'"); + 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 Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -139,6 +155,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 != null ? balanceType.getCurrencyCode() : null); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -149,7 +167,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 Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final int starStatus) { @@ -158,6 +177,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 != null ? balanceType.getCurrencyCode() : null); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); @@ -168,7 +189,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 Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor) { SQLiteDatabase db = getWritableDatabase(); @@ -176,6 +198,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 != 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/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/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/ImportURIHelper.java b/app/src/main/java/protect/card_locker/ImportURIHelper.java index ba77d83cf..54add450f 100644 --- a/app/src/main/java/protect/card_locker/ImportURIHelper.java +++ b/app/src/main/java/protect/card_locker/ImportURIHelper.java @@ -4,12 +4,16 @@ import android.content.Context; 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 { 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.BALANCE_TYPE; private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID; private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE; @@ -43,6 +47,8 @@ public class ImportURIHelper { try { // These values are allowed to be null Date expiry = null; + BigDecimal balance = new BigDecimal("0"); + Currency balanceType = null; Integer headerColor = null; Integer headerTextColor = null; @@ -52,18 +58,29 @@ 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 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 = 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); 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"); } @@ -77,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 b8dcaf1d8..64461a0e1 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 Currency balanceType, final String cardId, final String barcodeType, final Integer headerColor, final Integer headerTextColor, final int starStatus) { @@ -32,6 +36,8 @@ public class LoyaltyCard this.store = store; this.note = note; this.expiry = expiry; + this.balance = balance; + this.balanceType = balanceType; this.cardId = cardId; this.barcodeType = barcodeType; this.headerColor = headerColor; @@ -45,17 +51,25 @@ 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 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); @@ -71,6 +85,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..e20a03769 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(context, 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..7bf92e5c4 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; @@ -30,26 +33,29 @@ 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; import java.io.InvalidObjectException; +import java.math.BigDecimal; import java.text.DateFormat; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +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,7 +68,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity EditText noteFieldEdit; ChipGroup groupsChips; AutoCompleteTextView expiryField; - View cardAndBarcodeLayout; + EditText balanceField; + AutoCompleteTextView balanceCurrencyField; TextView cardIdFieldView; AutoCompleteTextView barcodeTypeField; ImageView barcodeImage; @@ -86,6 +93,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,13 +131,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); - cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout); + balanceField = findViewById(R.id.balanceField); + balanceCurrencyField = findViewById(R.id.balanceCurrencyField); cardIdFieldView = findViewById(R.id.cardIdView); barcodeTypeField = findViewById(R.id.barcodeTypeField); barcodeImage = findViewById(R.id.barcode); @@ -183,6 +199,98 @@ 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) { } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + hasChanged = true; + + try { + BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals((Currency) balanceCurrencyField.getTag())); + validBalance = true; + + balanceField.setTag(balance); + } catch (NumberFormatException e) { + validBalance = false; + e.printStackTrace(); + } + } + + @Override + public void afterTextChanged(Editable s) { } + }); + + balanceCurrencyField.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) { + hasChanged = true; + + Currency currency; + + if (s.toString().equals(getString(R.string.points))) { + currency = null; + } else { + currency = currencies.get(s.toString()); + } + + balanceCurrencyField.setTag(currency); + + BigDecimal balance = (BigDecimal) balanceField.getTag(); + + if (balance != null) { + balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(balance, 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 +370,15 @@ public class LoyaltyCardEditActivity extends AppCompatActivity noteFieldEdit.setText(""); expiryField.setTag(null); expiryField.setText(""); + balanceCurrencyField.setTag(null); + balanceCurrencyField.setText(""); + balanceField.setTag(null); + balanceField.setText(""); cardIdFieldView.setText(""); barcodeTypeField.setText(""); } + @SuppressLint("DefaultLocale") @Override public void onResume() { @@ -297,11 +410,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(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) @@ -340,11 +461,10 @@ 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.balance); + balanceCurrencyField.setTag(importCard.balanceType); + formatBalanceCurrencyField(importCard.balanceType); cardIdFieldView.setText(importCard.cardId); barcodeTypeField.setText(importCard.barcodeType); headingColorValue = importCard.headerColor; @@ -354,6 +474,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity setTitle(R.string.addCardTitle); expiryField.setTag(null); expiryField.setText(getString(R.string.never)); + balanceField.setTag(new BigDecimal("0")); + balanceCurrencyField.setTag(null); + formatBalanceCurrencyField(null); hideBarcode(); } @@ -381,13 +504,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); @@ -446,16 +566,27 @@ 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()); } + 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 +715,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(); + Currency balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()) : null; String cardId = cardIdFieldView.getText().toString(); String barcodeType = barcodeTypeField.getText().toString(); @@ -612,6 +744,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 +759,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 3ab695250..9c8943506 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); @@ -299,6 +302,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(this, loyaltyCard.balance, loyaltyCard.balanceType))); + } + else + { + balanceView.setVisibility(View.GONE); + } + if(loyaltyCard.expiry != null) { expiryView.setVisibility(View.VISIBLE); @@ -535,7 +547,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..287816965 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -6,9 +6,15 @@ 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.List; +import java.util.Locale; import androidx.core.graphics.ColorUtils; @@ -77,4 +83,62 @@ public class Utils { return expiryDate.before(date.getTime()); } + + static public String formatBalance(Context context, BigDecimal value, Currency currency) { + NumberFormat numberFormat = NumberFormat.getInstance(); + + if (currency == null) { + numberFormat.setMaximumFractionDigits(0); + return context.getString(R.string.balancePoints, numberFormat.format(value)); + } + + NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); + currencyFormat.setCurrency(currency); + currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits()); + currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits()); + + 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 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("[^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 non-numbers with dots + value = value.replaceAll("[^0-9]", "."); + + // 2. Remove all but the last dot + while (value.split("\\.").length > 2) { + value = value.replaceFirst("\\.", ""); + } + + // Parse as BigDecimal + return new BigDecimal(value); + } } 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 90% rename from app/src/main/java/protect/card_locker/CsvDatabaseExporter.java rename to app/src/main/java/protect/card_locker/importexport/CsvDatabaseExporter.java index a4c8fdb28..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. @@ -50,6 +54,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 +71,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/importexport/CsvDatabaseImporter.java similarity index 92% rename from app/src/main/java/protect/card_locker/CsvDatabaseImporter.java rename to app/src/main/java/protect/card_locker/importexport/CsvDatabaseImporter.java index 5040ddfcd..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; @@ -10,9 +10,15 @@ import java.io.BufferedReader; 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; +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. @@ -301,6 +307,21 @@ public class CsvDatabaseImporter implements DatabaseImporter expiry = new Date(extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true)); } catch (NullPointerException | FormatException e) { } + BigDecimal balance; + try { + balance = new BigDecimal(extractString(DBHelper.LoyaltyCardDbIds.BALANCE, 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"); + } + + 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()) { @@ -324,7 +345,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/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 77% 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..fedc446e7 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"; @@ -25,9 +30,12 @@ public class MultiFormatExporter switch(format) { - case CSV: + case Catima: exporter = new CsvDatabaseExporter(); break; + default: + Log.e(TAG, "Failed to export data, unknown format " + format.name()); + break; } if(exporter != null) 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 70% 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..3186440cd 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 { @@ -26,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) @@ -38,7 +50,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..317adb65a --- /dev/null +++ b/app/src/main/java/protect/card_locker/importexport/VoucherVaultImporter.java @@ -0,0 +1,132 @@ +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 java.util.TimeZone; + +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"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + 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/main/res/layout/loyalty_card_edit_activity.xml b/app/src/main/res/layout/loyalty_card_edit_activity.xml index 7e02fb469..50f850b63 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,7 @@ android:textSize="@dimen/inputSize" /> + - + + + + + + + + + + + + + + + + + + + - + + + android:orientation="horizontal"> + android:hint="@string/cardId" + android:labelFor="@+id/cardIdView"> + + + + + + + + %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 + %s puntos diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 81e834f79..e854bfad5 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 + %s punti + Saldo: %s + Scaduta: %s + Scade: %s diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 170d09ddf..ccdbd22e9 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 - \ No newline at end of file + %s ser ikke ut til å være en gyldig saldo. + Poeng + Valuta + Saldo + %s poeng + Saldo: %s + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 700f63c87..0e6158c74 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 - \ No newline at end of file + %s lijkt geen geldig saldo te zijn. + Aantal punten + Valuta + Saldo + %s punten + Saldo: %s + 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 20dbf76bc..f0ffec37c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -136,6 +136,8 @@ Groups: %s Expires: %s Expired: %s + Balance: %s + %s points Card Barcode @@ -145,4 +147,11 @@ 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. + Import data from which app? diff --git a/app/src/test/java/protect/card_locker/DatabaseTest.java b/app/src/test/java/protect/card_locker/DatabaseTest.java index c8db55818..a1f6b11da 100644 --- a/app/src/test/java/protect/card_locker/DatabaseTest.java +++ b/app/src/test/java/protect/card_locker/DatabaseTest.java @@ -15,7 +15,9 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; 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; @@ -43,7 +45,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()); @@ -53,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); @@ -66,12 +70,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("10.00"), Currency.getInstance("EUR"), "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR); assertTrue(result); assertEquals(1, db.getLoyaltyCardCount()); @@ -80,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); @@ -88,7 +94,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()); @@ -102,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); @@ -112,7 +120,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 +129,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()); @@ -131,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); } @@ -144,7 +154,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); @@ -164,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))); @@ -187,12 +199,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); @@ -211,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))); @@ -222,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))); @@ -288,7 +304,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 +421,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()); @@ -465,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 ddf3b6c3f..11413b6b8 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; @@ -24,12 +26,18 @@ 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.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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -72,7 +80,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 +96,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,33 +105,79 @@ 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); } assertEquals(cardsToAdd, db.getLoyaltyCardCount()); } - private void addLoyaltyCardsWithExpiryNeverPastTodayFuture() + @Test + public 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); + 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); - id = db.insertLoyaltyCard("Today", "", new Date(), BARCODE_DATA, BARCODE_TYPE, 0, 0); + 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), 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()); } @@ -160,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); @@ -190,6 +246,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); @@ -208,6 +266,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); @@ -261,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 @@ -296,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) @@ -343,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 @@ -421,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 @@ -462,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(); @@ -476,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()); @@ -505,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 @@ -567,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()); @@ -576,6 +621,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); @@ -603,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()); @@ -612,6 +659,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); @@ -639,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()); @@ -665,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()); @@ -674,6 +723,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); @@ -701,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()); @@ -710,6 +761,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); @@ -737,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()); @@ -746,6 +799,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); @@ -773,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()); @@ -793,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()); @@ -802,6 +857,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); @@ -810,51 +867,63 @@ 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 + boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.VoucherVault); + assertTrue(result); + assertEquals(2, db.getLoyaltyCardCount()); + + LoyaltyCard card = db.getLoyaltyCard(1); + + assertEquals("Clothes Store", card.store); assertEquals("", card.note); assertEquals(null, card.expiry); - assertEquals(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(new BigDecimal("0"), card.balance); + 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(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); + assertEquals(new Date(1616716800000L), 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(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(BARCODE_DATA, card.cardId); - assertEquals(BARCODE_TYPE, card.barcodeType); - assertEquals(Integer.valueOf(0), card.headerColor); - assertEquals(0, card.starStatus); - - cursor.close(); + clearDatabase(); } } diff --git a/app/src/test/java/protect/card_locker/ImportURITest.java b/app/src/test/java/protect/card_locker/ImportURITest.java index 02bd27462..2b29c47cb 100644 --- a/app/src/test/java/protect/card_locker/ImportURITest.java +++ b/app/src/test/java/protect/card_locker/ImportURITest.java @@ -11,6 +11,8 @@ import org.robolectric.Robolectric; 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; @@ -38,7 +40,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("100"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1); // Get card LoyaltyCard card = db.getLoyaltyCard(1); @@ -55,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); @@ -64,7 +68,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("10.00"), Currency.getInstance("EUR"), BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0); // Get card LoyaltyCard card = db.getLoyaltyCard(1); @@ -80,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); @@ -125,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 3d8c25e2a..8055e8026 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -21,7 +21,9 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; 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; @@ -60,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) { @@ -77,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()); @@ -87,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()); @@ -96,13 +99,23 @@ 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()); + } } @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(); @@ -110,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(); } @@ -118,7 +131,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(); @@ -126,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(); } @@ -138,7 +151,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(); @@ -147,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(); } @@ -159,9 +172,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(); @@ -181,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 TestCursorAdapter10USD() + { + 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(); + 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 d203f0393..3fa5926ae 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -39,7 +39,9 @@ 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.Currency; import java.util.Date; import org.junit.Before; import org.junit.Test; @@ -108,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) @@ -125,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); @@ -143,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 @@ -253,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) { @@ -266,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); @@ -283,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 @@ -301,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()); @@ -345,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 @@ -367,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 @@ -386,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()); @@ -438,15 +462,16 @@ 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, 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(); - 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(); } @@ -456,15 +481,16 @@ 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, 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(); - 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(); } @@ -474,20 +500,21 @@ 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, 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(); 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(); } @@ -497,20 +524,21 @@ 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, 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(); 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()); @@ -537,13 +565,13 @@ 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(); 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); @@ -557,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(); } @@ -570,19 +598,111 @@ 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(); 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", 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", context.getString(R.string.never), "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(); } @@ -594,7 +714,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 +762,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 +782,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,14 +802,14 @@ 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(); 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(); } @@ -701,14 +821,14 @@ 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(); 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(); } @@ -721,24 +841,24 @@ 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(); 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(); } @@ -750,7 +870,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 +910,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 +947,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 +982,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(); @@ -960,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=USD&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1"); Intent intent = new Intent(); intent.setData(importUri); @@ -974,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()); } @@ -995,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()); } } 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<>(); 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..761633feb --- /dev/null +++ b/app/src/test/java/protect/card_locker/UtilsTest.java @@ -0,0 +1,104 @@ +package protect.card_locker; + + +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; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 23) +public class UtilsTest +{ + @Test + public void parseBalances() + { + assertEquals("1", Utils.parseCurrency("1", false).toPlainString()); + + 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()); + 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()); + 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)); + } +} 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 9be572857..505c8dd9f 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png differ 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 b52d8d37f..aa3222545 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png differ