]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemHolder.java
NT: keep cursor position while setting account name text
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / new_transaction / NewTransactionItemHolder.java
index a69a65af40a50cdd95e5a8f0f7da3b5f605c1d59..c6232d167f7e303af4d93be2222354918727cc36 100644 (file)
@@ -26,6 +26,7 @@ import android.view.Gravity;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
+import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
@@ -55,7 +56,7 @@ import java.util.Objects;
 
 class NewTransactionItemHolder extends RecyclerView.ViewHolder
         implements DatePickerFragment.DatePickedListener, DescriptionSelectedCallback {
-    private final String decimalDot;
+    private final String decimalDot = ".";
     private final MobileLedgerProfile mProfile;
     private final NewTransactionRowBinding b;
     private final NewTransactionItemsAdapter mAdapter;
@@ -89,7 +90,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
         mProfile = Data.getProfile();
 
-        View.OnFocusChangeListener focusMonitor = (v, hasFocus) -> {
+        @SuppressLint("DefaultLocale") View.OnFocusChangeListener focusMonitor = (v, hasFocus) -> {
             final int id = v.getId();
             if (hasFocus) {
                 boolean wasSyncing = syncingData;
@@ -118,6 +119,19 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                     syncingData = wasSyncing;
                 }
             }
+            else {  // lost focus
+                if (id == R.id.account_row_acc_amounts) {
+                    try {
+                        String input = String.valueOf(b.accountRowAccAmounts.getText());
+                        input = input.replace(decimalSeparator, decimalDot);
+                        b.accountRowAccAmounts.setText(
+                                String.format("%4.2f", Float.parseFloat(input)));
+                    }
+                    catch (NumberFormatException ex) {
+                        // ignored
+                    }
+                }
+            }
 
             if (id == R.id.comment) {
                 commentFocusChanged(b.comment, hasFocus);
@@ -146,8 +160,6 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                 DecimalFormatSymbols.getInstance(locale)
                                     .getMonetaryDecimalSeparator()));
 
-        decimalDot = ".";
-
         final TextWatcher tw = new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -164,10 +176,11 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                     return;
 
                 Logger.debug("textWatcher", "calling syncData()");
-                syncData();
-                Logger.debug("textWatcher",
-                        "syncData() returned, checking if transaction is submittable");
-                adapter.model.checkTransactionSubmittable(null);
+                if (syncData()) {
+                    Logger.debug("textWatcher",
+                            "syncData() returned, checking if transaction is submittable");
+                    adapter.model.checkTransactionSubmittable(null);
+                }
                 Logger.debug("textWatcher", "done");
             }
         };
@@ -202,77 +215,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
         commentFocusChanged(b.comment, false);
 
         adapter.model.getFocusInfo()
-                     .observe(activity, focusInfo -> {
-                         if (ignoreFocusChanges) {
-                             Logger.debug("new-trans", "Ignoring focus change");
-                             return;
-                         }
-                         ignoreFocusChanges = true;
-                         try {
-                             if (((focusInfo == null) ||
-                                  focusInfo.position != getAdapterPosition()) ||
-                                 itemView.hasFocus())
-                                 return;
-
-                             NewTransactionModel.Item item = getItem();
-                             if (item instanceof NewTransactionModel.TransactionHead) {
-                                 NewTransactionModel.TransactionHead head =
-                                         item.toTransactionHead();
-                                 // bad idea - double pop-up, and not really necessary.
-                                 // the user can tap the input to get the calendar
-                                 //if (!tvDate.hasFocus()) tvDate.requestFocus();
-                                 switch (focusInfo.element) {
-                                     case TransactionComment:
-                                         b.transactionComment.setVisibility(View.VISIBLE);
-                                         b.transactionComment.requestFocus();
-                                         break;
-                                     case Description:
-                                         boolean focused =
-                                                 b.newTransactionDescription.requestFocus();
-//                            tvDescription.dismissDropDown();
-                                         if (focused)
-                                             Misc.showSoftKeyboard(
-                                                     (NewTransactionActivity) b.getRoot()
-                                                                               .getContext());
-                                         break;
-                                 }
-                             }
-                             else if (item instanceof NewTransactionModel.TransactionAccount) {
-                                 NewTransactionModel.TransactionAccount acc =
-                                         item.toTransactionAccount();
-                                 switch (focusInfo.element) {
-                                     case Amount:
-                                         b.accountRowAccAmounts.requestFocus();
-                                         break;
-                                     case Comment:
-                                         b.comment.setVisibility(View.VISIBLE);
-                                         b.comment.requestFocus();
-                                         break;
-                                     case Account:
-                                         boolean focused = b.accountRowAccName.requestFocus();
-//                                         b.accountRowAccName.dismissDropDown();
-                                         if (focused)
-                                             Misc.showSoftKeyboard(
-                                                     (NewTransactionActivity) b.getRoot()
-                                                                               .getContext());
-                                         break;
-                                 }
-                             }
-                         }
-                         finally {
-                             ignoreFocusChanges = false;
-                         }
-                     });
-        adapter.model.getAccountCount()
-                     .observe(activity, count -> {
-                         final int adapterPosition = getAdapterPosition();
-                         final int layoutPosition = getLayoutPosition();
-
-                         if (adapterPosition == count)
-                             b.accountRowAccAmounts.setImeOptions(EditorInfo.IME_ACTION_DONE);
-                         else
-                             b.accountRowAccAmounts.setImeOptions(EditorInfo.IME_ACTION_NEXT);
-                     });
+                     .observe(activity, this::applyFocus);
 
         Data.currencyGap.observe(activity,
                 hasGap -> updateCurrencyPositionAndPadding(Data.currencySymbolPosition.getValue(),
@@ -328,6 +271,61 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                          b.transactionCommentLayout.setVisibility(show ? View.VISIBLE : View.GONE);
                      });
     }
+    private void applyFocus(NewTransactionModel.FocusInfo focusInfo) {
+        if (ignoreFocusChanges) {
+            Logger.debug("new-trans", "Ignoring focus change");
+            return;
+        }
+        ignoreFocusChanges = true;
+        try {
+            if (((focusInfo == null) || (focusInfo.element == null) ||
+                 focusInfo.position != getAdapterPosition()))
+                return;
+
+            NewTransactionModel.Item item = getItem();
+            if (item instanceof NewTransactionModel.TransactionHead) {
+                NewTransactionModel.TransactionHead head = item.toTransactionHead();
+                // bad idea - double pop-up, and not really necessary.
+                // the user can tap the input to get the calendar
+                //if (!tvDate.hasFocus()) tvDate.requestFocus();
+                switch (focusInfo.element) {
+                    case TransactionComment:
+                        b.transactionComment.setVisibility(View.VISIBLE);
+                        b.transactionComment.requestFocus();
+                        break;
+                    case Description:
+                        boolean focused = b.newTransactionDescription.requestFocus();
+//                            tvDescription.dismissDropDown();
+                        if (focused)
+                            Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
+                                                                            .getContext());
+                        break;
+                }
+            }
+            else if (item instanceof NewTransactionModel.TransactionAccount) {
+                NewTransactionModel.TransactionAccount acc = item.toTransactionAccount();
+                switch (focusInfo.element) {
+                    case Amount:
+                        b.accountRowAccAmounts.requestFocus();
+                        break;
+                    case Comment:
+                        b.comment.setVisibility(View.VISIBLE);
+                        b.comment.requestFocus();
+                        break;
+                    case Account:
+                        boolean focused = b.accountRowAccName.requestFocus();
+//                                         b.accountRowAccName.dismissDropDown();
+                        if (focused)
+                            Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
+                                                                            .getContext());
+                        break;
+                }
+            }
+        }
+        finally {
+            ignoreFocusChanges = false;
+        }
+    }
     public void checkAmountValid(String s) {
         boolean valid = true;
         try {
@@ -485,17 +483,35 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
         syncingData = true;
 
+        boolean significantChange = false;
+
         try {
             if (item instanceof NewTransactionModel.TransactionHead) {
                 NewTransactionModel.TransactionHead head = item.toTransactionHead();
 
                 head.setDate(String.valueOf(b.newTransactionDate.getText()));
+
+                // transaction description is required
+                if (TextUtils.isEmpty(head.getDescription()) !=
+                    TextUtils.isEmpty(b.newTransactionDescription.getText()))
+                    significantChange = true;
+
                 head.setDescription(String.valueOf(b.newTransactionDescription.getText()));
                 head.setComment(String.valueOf(b.transactionComment.getText()));
             }
             else if (item instanceof NewTransactionModel.TransactionAccount) {
                 NewTransactionModel.TransactionAccount acc = item.toTransactionAccount();
-                acc.setAccountName(String.valueOf(b.accountRowAccName.getText()));
+
+                // having account name is important
+                final Editable incomingAccountName = b.accountRowAccName.getText();
+                if (TextUtils.isEmpty(acc.getAccountName()) !=
+                    TextUtils.isEmpty(incomingAccountName))
+                    significantChange = true;
+
+                acc.setAccountName(String.valueOf(incomingAccountName));
+                final int accNameSelEnd = b.accountRowAccName.getSelectionEnd();
+                final int accNameSelStart = b.accountRowAccName.getSelectionStart();
+                acc.setAccountNameCursorPosition(accNameSelEnd);
 
                 acc.setComment(String.valueOf(b.comment.getText()));
 
@@ -503,36 +519,48 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                 amount = amount.trim();
 
                 if (amount.isEmpty()) {
+                    if (acc.isAmountSet())
+                        significantChange = true;
                     acc.resetAmount();
                     acc.setAmountValid(true);
                 }
                 else {
                     try {
                         amount = amount.replace(decimalSeparator, decimalDot);
-                        acc.setAmount(Float.parseFloat(amount));
+                        final float parsedAmount = Float.parseFloat(amount);
+                        if (!acc.isAmountSet() || !Misc.equalFloats(parsedAmount, acc.getAmount()))
+                            significantChange = true;
+                        acc.setAmount(parsedAmount);
                         acc.setAmountValid(true);
                     }
                     catch (NumberFormatException e) {
                         Logger.debug("new-trans", String.format(
                                 "assuming amount is not set due to number format exception. " +
                                 "input was '%s'", amount));
+                        if (acc.isAmountValid())
+                            significantChange = true;
                         acc.setAmountValid(false);
                     }
                     final String curr = String.valueOf(b.currency.getText());
+                    final String currValue;
                     if (curr.equals(b.currency.getContext()
                                               .getResources()
                                               .getString(R.string.currency_symbol)) ||
                         curr.isEmpty())
-                        acc.setCurrency(null);
+                        currValue = null;
                     else
-                        acc.setCurrency(curr);
+                        currValue = curr;
+
+                    if (!significantChange && !TextUtils.equals(acc.getCurrency(), currValue))
+                        significantChange = true;
+                    acc.setCurrency(currValue);
                 }
             }
             else {
                 throw new RuntimeException("Should not happen");
             }
 
-            return true;
+            return significantChange;
         }
         catch (ParseException e) {
             throw new RuntimeException("Should not happen", e);
@@ -563,20 +591,49 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                 if (item instanceof NewTransactionModel.TransactionHead) {
                     NewTransactionModel.TransactionHead head = item.toTransactionHead();
                     b.newTransactionDate.setText(head.getFormattedDate());
-                    b.newTransactionDescription.setText(head.getDescription());
+
+                    // avoid triggering completion pop-up
+                    SimpleCursorAdapter a =
+                            (SimpleCursorAdapter) b.newTransactionDescription.getAdapter();
+                    try {
+                        b.newTransactionDescription.setAdapter(null);
+                        b.newTransactionDescription.setText(head.getDescription());
+                    }
+                    finally {
+                        b.newTransactionDescription.setAdapter(a);
+                    }
 
                     b.transactionComment.setText(head.getComment());
                     //styleComment(b.transactionComment, head.getComment());
 
                     b.ntrData.setVisibility(View.VISIBLE);
                     b.ntrAccount.setVisibility(View.GONE);
-                    b.ntrPadding.setVisibility(View.GONE);
                     setEditable(true);
                 }
                 else if (item instanceof NewTransactionModel.TransactionAccount) {
                     NewTransactionModel.TransactionAccount acc = item.toTransactionAccount();
 
-                    b.accountRowAccName.setText(acc.getAccountName());
+                    final String incomingAccountName = acc.getAccountName();
+                    final String presentAccountName = String.valueOf(b.accountRowAccName.getText());
+                    if (!TextUtils.equals(incomingAccountName, presentAccountName)) {
+                        Logger.debug("bind",
+                                String.format("Setting account name from '%s' to '%s' (| @ %d)",
+                                        presentAccountName, incomingAccountName,
+                                        acc.getAccountNameCursorPosition()));
+                        // avoid triggering completion pop-up
+                        AccountAutocompleteAdapter a =
+                                (AccountAutocompleteAdapter) b.accountRowAccName.getAdapter();
+                        try {
+                            b.accountRowAccName.setAdapter(null);
+                            b.accountRowAccName.setText(incomingAccountName);
+                            if (b.accountRowAccName.hasFocus())
+                                b.accountRowAccName.setSelection(
+                                        acc.getAccountNameCursorPosition());
+                        }
+                        finally {
+                            b.accountRowAccName.setAdapter(a);
+                        }
+                    }
 
                     final String amountHint = acc.getAmountHint();
                     if (amountHint == null) {
@@ -586,6 +643,9 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                         b.accountRowAccAmounts.setHint(amountHint);
                     }
 
+                    b.accountRowAccAmounts.setImeOptions(
+                            acc.isLast() ? EditorInfo.IME_ACTION_DONE : EditorInfo.IME_ACTION_NEXT);
+
                     setCurrencyString(acc.getCurrency());
                     b.accountRowAccAmounts.setText(
                             acc.isAmountSet() ? String.format("%4.2f", acc.getAmount()) : null);
@@ -595,18 +655,15 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
                     b.ntrData.setVisibility(View.GONE);
                     b.ntrAccount.setVisibility(View.VISIBLE);
-                    b.ntrPadding.setVisibility(View.GONE);
+
                     setEditable(true);
                 }
-                else if (item instanceof NewTransactionModel.BottomFiller) {
-                    b.ntrData.setVisibility(View.GONE);
-                    b.ntrAccount.setVisibility(View.GONE);
-                    b.ntrPadding.setVisibility(View.VISIBLE);
-                    setEditable(false);
-                }
                 else {
                     throw new RuntimeException("Don't know how to handle " + item);
                 }
+
+                applyFocus(mAdapter.model.getFocusInfo()
+                                         .getValue());
             }
             finally {
                 syncingData = false;