]> git.ktnx.net Git - mobile-ledger.git/commitdiff
posting-level flag for amount validity; drop custom amount text filter
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 14 May 2020 10:50:12 +0000 (13:50 +0300)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 14 May 2020 17:39:53 +0000 (17:39 +0000)
the custom text filter in amount inputs seems to break with some
Samsung keyboards.

with a flag that is honoured by the code that checks for transaction
submittability it is better, and allows for a nice warning icon on
invalid input

app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemsAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionModel.java
app/src/main/res/drawable-anydpi-v21/ic_error_outline_black_24dp.xml [new file with mode: 0644]
app/src/main/res/layout/new_transaction_row.xml

index c97b7e4a5e7afd801ab73ca4234da776c66753ed..1791cfbc5962e79b5b3c16ab4516c5e921a1765b 100644 (file)
@@ -30,11 +30,13 @@ public class LedgerTransactionAccount {
     private boolean amountSet = false;
     private String currency;
     private String comment;
+    private boolean amountValid = true;
     public LedgerTransactionAccount(String accountName, float amount, String currency,
                                     String comment) {
         this.setAccountName(accountName);
         this.amount = amount;
         this.amountSet = true;
+        this.amountValid = true;
         this.currency = Misc.emptyIsNull(currency);
         this.comment = Misc.emptyIsNull(comment);
     }
@@ -51,6 +53,7 @@ public class LedgerTransactionAccount {
         setComment(origin.getComment());
         if (origin.isAmountSet())
             setAmount(origin.getAmount());
+        amountValid = origin.amountValid;
         currency = origin.getCurrency();
     }
     public String getComment() {
@@ -78,13 +81,19 @@ public class LedgerTransactionAccount {
     public void setAmount(float account_amount) {
         this.amount = account_amount;
         this.amountSet = true;
+        this.amountValid = true;
     }
     public void resetAmount() {
         this.amountSet = false;
+        this.amountValid = true;
+    }
+    public void invalidateAmount() {
+        this.amountValid = false;
     }
     public boolean isAmountSet() {
         return amountSet;
     }
+    public boolean isAmountValid() { return amountValid; }
     public String getCurrency() {
         return currency;
     }
index 3e6cab36328f7079e569f4efaad78de619de16aa..a3dd711d9ebe1ad7102bbb933b567e21e4991600 100644 (file)
@@ -92,6 +92,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
     private Observer<Currency> currencyObserver;
     private Observer<Boolean> showCurrencyObserver;
     private Observer<String> commentObserver;
+    private Observer<Boolean> amountValidityObserver;
     private boolean inUpdate = false;
     private boolean syncingData = false;
     private View commentButton;
@@ -126,10 +127,10 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
         });
 
         transactionCommentLayout.findViewById(R.id.comment_button)
-                .setOnClickListener(v -> {
-                    tvTransactionComment.setVisibility(View.VISIBLE);
-                    tvTransactionComment.requestFocus();
-                });
+                                .setOnClickListener(v -> {
+                                    tvTransactionComment.setVisibility(View.VISIBLE);
+                                    tvTransactionComment.requestFocus();
+                                });
 
         mProfile = Data.profile.getValue();
         if (mProfile == null)
@@ -224,19 +225,6 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
             public void onTextChanged(CharSequence s, int start, int before, int count) {}
             @Override
             public void afterTextChanged(Editable s) {
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
-                    // only one decimal separator is allowed
-                    // plus and minus are allowed only at the beginning
-                    String allowed = "0123456789";
-                    String val = s.toString();
-                    Logger.debug("input", val);
-                    if (val.isEmpty() || (tvAmount.getSelectionStart() == 0))
-                        allowed += "-";
-                    if (!val.contains(decimalSeparator) && !val.contains(decimalDot))
-                        allowed += decimalSeparator + decimalDot;
-
-                    tvAmount.setKeyListener(DigitsKeyListener.getInstance(allowed));
-                }
 
                 if (syncData())
                     adapter.checkTransactionSubmittable();
@@ -445,6 +433,12 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
             transactionCommentLayout.setVisibility(show ? View.VISIBLE : View.GONE);
         };
+
+        amountValidityObserver = valid -> {
+            tvAmount.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                    valid ? 0 : R.drawable.ic_error_outline_black_24dp, 0, 0, 0);
+            tvAmount.setMinEms(valid ? 4 : 5);
+        };
     }
     private void commentFocusChanged(View layout, TextView textView, boolean hasFocus) {
         int textColor;
@@ -574,18 +568,20 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
                     if (amount.isEmpty()) {
                         account.resetAmount();
-//                        account.setCurrency(null);
+                        item.validateAmount();
                     }
                     else {
                         try {
                             amount = amount.replace(decimalSeparator, decimalDot);
                             account.setAmount(Float.parseFloat(amount));
+                            item.validateAmount();
                         }
                         catch (NumberFormatException e) {
                             Logger.debug("new-trans", String.format(
                                     "assuming amount is not set due to number format exception. " +
                                     "input was '%s'", amount));
-                            account.resetAmount();
+                            account.invalidateAmount();
+                            item.invalidateAmount();
                         }
                         final String curr = String.valueOf(tvCurrency.getText());
                         if (curr.equals(tvCurrency.getContext()
@@ -641,6 +637,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                 this.item.getModel().showCurrency.removeObserver(showCurrencyObserver);
                 this.item.stopObservingComment(commentObserver);
                 this.item.getModel().showComments.removeObserver(showCommentsObserver);
+                this.item.stopObservingAmountValidity(amountValidityObserver);
 
                 this.item = null;
             }
@@ -708,6 +705,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                         item.observeComment(activity, commentObserver);
                         item.getModel()
                             .observeAccountCount(activity, accountCountObserver);
+                        item.observeAmountValidity(activity, amountValidityObserver);
                         break;
                 }
             }
index 5caa38a5581ae19d3b4fb403d5125a90e741ce0d..c684b79fe0a3d70f70af8ba8b8b0c84d9a68e8bf 100644 (file)
@@ -473,7 +473,12 @@ class NewTransactionItemsAdapter extends RecyclerView.Adapter<NewTransactionItem
                     itemsWithAccountForCurrency.add(currName, item);
                 }
 
-                if (acc.isAmountSet()) {
+                if (!acc.isAmountValid()) {
+                    Logger.debug("submittable",
+                            String.format("Not submittable: row %d has an invalid amount", i + 1));
+                    submittable = false;
+                }
+                else if (acc.isAmountSet()) {
                     itemsWithAmountForCurrency.add(currName, item);
                     balance.add(currName, acc.getAmount());
                 }
index 03887c19410137857cc6eacbcc1c2e9673b8d47f..83ffbb8f7bed90cdf747e60cc0daf6ef3d61e182 100644 (file)
@@ -241,6 +241,7 @@ public class NewTransactionModel extends ViewModel {
         private FocusedElement focusedElement = FocusedElement.Account;
         private MutableLiveData<String> comment = new MutableLiveData<>(null);
         private MutableLiveData<Currency> currency = new MutableLiveData<>(null);
+        private MutableLiveData<Boolean> amountValid = new MutableLiveData<>(true);
         private boolean amountHintIsSet = false;
         Item(NewTransactionModel model) {
             this.model = model;
@@ -489,5 +490,17 @@ public class NewTransactionModel extends ViewModel {
         boolean isAmountHintSet() {
             return amountHintIsSet;
         }
+        void validateAmount() {
+            amountValid.setValue(true);
+        }
+        void invalidateAmount() {
+            amountValid.setValue(false);
+        }
+        void observeAmountValidity(NewTransactionActivity activity, Observer<Boolean> observer) {
+            amountValid.observe(activity, observer);
+        }
+        void stopObservingAmountValidity(Observer<Boolean> observer) {
+            amountValid.removeObserver(observer);
+        }
     }
 }
diff --git a/app/src/main/res/drawable-anydpi-v21/ic_error_outline_black_24dp.xml b/app/src/main/res/drawable-anydpi-v21/ic_error_outline_black_24dp.xml
new file mode 100644 (file)
index 0000000..76054bd
--- /dev/null
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright Google Inc.
+  ~
+  ~ Licensed under the Apache License, version 2.0 ("the License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the license at:
+  ~
+  ~ https://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distribution under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  ~ Modified/adapted by Damyan Ivanov for MoLe
+  -->
+
+<vector android:autoMirrored="true" android:height="24dp"
+    android:tint="?colorAccent" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
index ca1889eef97e8370a0faca6d320e232ad2b3b851..ab1668cbffd19e9c8302c07ce663e64171c9ce5b 100644 (file)
                 android:hint="@string/zero_amount"
                 android:imeOptions="actionNext"
                 android:inputType="numberSigned|numberDecimal|number"
-                android:minWidth="80sp"
+                android:minEms="4"
                 android:selectAllOnFocus="true"
                 android:textAlignment="viewEnd"
                 app:layout_constraintBottom_toBottomOf="parent"