diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f3888cac..4dd9a8c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Target Android 14 - Open card icon in gallery on touch - Improve design of Photos tab in edit view +- Update spending screen to also support receiving ## v2.27.0 - 132 (2024-01-30) diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 0d1ae8f47..e5e12f27b 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -29,6 +29,7 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowInsetsController; import android.view.WindowManager; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; @@ -420,7 +421,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements private void showBalanceUpdateDialog() { AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); + + // Header builder.setTitle(R.string.updateBalanceTitle); + + // Layout FrameLayout container = new FrameLayout(this); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -438,61 +443,91 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements currentTextview.setText(getString(R.string.currentBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType))); layout.addView(currentTextview); - TextView updateTextView = new TextView(this); - updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType))); - layout.addView(updateTextView); - final TextInputEditText input = new TextInputEditText(this); - Context dialogContext = this; input.setInputType(InputType.TYPE_CLASS_NUMBER); input.setKeyListener(DigitsKeyListener.getInstance("0123456789,.")); input.setHint(R.string.updateBalanceHint); - input.addTextChangedListener(new SimpleTextWatcher() { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - BigDecimal newBalance; - try { - newBalance = calculateNewBalance(loyaltyCard.balance, loyaltyCard.balanceType, s.toString()); - } catch (ParseException e) { - input.setTag(null); - updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, loyaltyCard.balance, loyaltyCard.balanceType))); - return; - } - // Save new balance into this element - input.setTag(newBalance); - updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, newBalance, loyaltyCard.balanceType))); - } - }); layout.addView(input); layout.setLayoutParams(params); container.addView(layout); + // Set layout builder.setView(container); - builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> { - // Grab calculated balance from input field - BigDecimal newBalance = (BigDecimal) input.getTag(); - if (newBalance == null) { - return; + + // Buttons + builder.setPositiveButton(R.string.spend, (dialogInterface, i) -> { + // Calculate and update balance + try { + BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType); + BigDecimal newBalance = loyaltyCard.balance.subtract(balanceChange).max(new BigDecimal(0)); + DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance); + } catch (ParseException e) { + Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show(); } - // Actually update balance - DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance); - // Reload UI + // Reload state this.onResume(); + + // Show new balance + Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show(); }); - builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel()); + builder.setNegativeButton(R.string.receive, (dialogInterface, i) -> { + // Calculate and update balance + try { + BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType); + BigDecimal newBalance = loyaltyCard.balance.add(balanceChange); + DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance); + } catch (ParseException e) { + Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show(); + } + + // Reload state + this.onResume(); + + // Show new balance + Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show(); + }); + builder.setNeutralButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel()); AlertDialog dialog = builder.create(); + + // Now that the dialog exists, we can bind something that affects the buttons + input.addTextChangedListener(new SimpleTextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + BigDecimal balanceChange; + + try { + balanceChange = Utils.parseBalance(s.toString(), loyaltyCard.balanceType); + } catch (ParseException e) { + input.setError(getString(R.string.amountParsingFailed)); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false); + return; + } + + input.setError(null); + if (balanceChange.equals(new BigDecimal(0))) { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false); + } else { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(true); + } + } + }); + dialog.show(); + + // Disable buttons (must be done **after** dialog is shown to prevent crash + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false); + + // Set focus on input field dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); input.requestFocus(); } - private BigDecimal calculateNewBalance(BigDecimal currentBalance, Currency currency, String unparsedSubtraction) throws ParseException { - BigDecimal subtraction = Utils.parseBalance(unparsedSubtraction, currency); - return currentBalance.subtract(subtraction).max(new BigDecimal(0)); - } - private void setBottomAppBarButtonState() { if (!loyaltyCard.note.isEmpty() || !loyaltyCardGroups.isEmpty() || hasBalance(loyaltyCard) || loyaltyCard.validFrom != null || loyaltyCard.expiry != null) { binding.bottomAppBarInfoButton.setVisibility(View.VISIBLE); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fde49355d..b6e009e19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -296,7 +296,7 @@ Welcome to Catima Import cards - How much did you spend? + How much did you spend or receive? Enter amount Current balance: %s New balance: %s @@ -338,4 +338,7 @@ Scanning is recommended For some stores, the barcode value differs from the number written on the card. Because of this, entering a barcode manually may not always work. It is strongly recommended to scan the barcode with your camera instead. Do you still want to continue? Continue + Spend + Receive + Invalid amount