]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java
whitespace
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / templates / TemplateDetailsAdapter.java
index e0406446485cfbbf7896ddd9dae079dced9f7b54..9152fdfd691a858f4bdc6cb63123a82f70d8c736 100644 (file)
@@ -18,6 +18,7 @@
 package net.ktnx.mobileledger.ui.templates;
 
 import android.annotation.SuppressLint;
+import android.content.res.Resources;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
@@ -33,11 +34,11 @@ import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.RecyclerView;
 
+import net.ktnx.mobileledger.BuildConfig;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.databinding.TemplateDetailsAccountBinding;
 import net.ktnx.mobileledger.databinding.TemplateDetailsHeaderBinding;
 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
-import net.ktnx.mobileledger.db.TemplateBase;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.TemplateDetailsItem;
 import net.ktnx.mobileledger.ui.QRScanCapableFragment;
@@ -48,7 +49,6 @@ import net.ktnx.mobileledger.utils.Misc;
 import org.jetbrains.annotations.NotNull;
 
 import java.text.ParseException;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
@@ -99,7 +99,7 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
         itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
             @Override
             public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
-                return 0.1f;
+                return 0.5f;
             }
             @Override
             public boolean isLongPressDragEnabled() {
@@ -165,6 +165,16 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
 
                 final int fromPosition = viewHolder.getAdapterPosition();
                 final int toPosition = target.getAdapterPosition();
+                if (fromPosition == toPosition) {
+                    Logger.debug("drag", String.format(Locale.US,
+                            "Ignoring request to move an account from position %d to %d",
+                            fromPosition, toPosition));
+                    return false;
+                }
+
+                Logger.debug("drag",
+                        String.format(Locale.US, "Moving account from %d to %d", fromPosition,
+                                toPosition));
                 mModel.moveItem(fromPosition, toPosition);
 
                 return true;
@@ -192,7 +202,7 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
     public long getItemId(int position) {
         // header item is always first and IDs id may duplicate some of the account IDs
         if (position == 0)
-            return -1;
+            return 0;
         TemplateDetailsItem.AccountRow accRow = differ.getCurrentList()
                                                       .get(position)
                                                       .asAccountRowItem();
@@ -232,15 +242,16 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
         return differ.getCurrentList()
                      .size();
     }
-    public void setTemplateItems(List<TemplateBase> items) {
-        ArrayList<TemplateDetailsItem> list = new ArrayList<>();
-        for (TemplateBase p : items) {
-            TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p);
-            list.add(item);
-        }
-        setItems(list);
-    }
     public void setItems(List<TemplateDetailsItem> items) {
+        if (BuildConfig.DEBUG) {
+            Logger.debug("tmpl", "Got new list");
+            for (int i = 1; i < items.size(); i++) {
+                final TemplateDetailsItem item = items.get(i);
+                Logger.debug("tmpl",
+                        String.format(Locale.US, "  %d: id %d, pos %d", i, item.getId(),
+                                item.getPosition()));
+            }
+        }
         differ.submitList(items);
     }
     public String getMatchGroupText(int groupNumber) {
@@ -273,8 +284,22 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
         abstract void bind(TemplateDetailsItem item);
     }
 
-    public class Header extends ViewHolder {
+    private abstract static class BaseItem extends ViewHolder {
+        boolean updatePropagationDisabled = false;
+        BaseItem(@NonNull View itemView) {
+            super(itemView);
+        }
+        void disableUpdatePropagation() {
+            updatePropagationDisabled = true;
+        }
+        void enableUpdatePropagation() {
+            updatePropagationDisabled = false;
+        }
+    }
+
+    public class Header extends BaseItem {
         private final TemplateDetailsHeaderBinding b;
+        boolean updatePropagationDisabled = false;
         public Header(@NonNull TemplateDetailsHeaderBinding binding) {
             super(binding.getRoot());
             b = binding;
@@ -286,6 +311,9 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 public void onTextChanged(CharSequence s, int start, int before, int count) {}
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     final TemplateDetailsItem.Header header = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed template name " + s + "; header=" + header);
@@ -301,6 +329,9 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 public void onTextChanged(CharSequence s, int start, int before, int count) {}
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     final TemplateDetailsItem.Header header = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed pattern " + s + "; header=" + header);
@@ -318,6 +349,9 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 public void onTextChanged(CharSequence s, int start, int before, int count) {}
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     final TemplateDetailsItem.Header header = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed test text " + s + "; header=" + header);
@@ -334,10 +368,12 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 }
                 @Override
                 public void onTextChanged(CharSequence s, int start, int before, int count) {
-
                 }
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     final TemplateDetailsItem.Header header = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed transaction description " + s + "; header=" + header);
@@ -348,14 +384,15 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
             TextWatcher transactionCommentWatcher = 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) {
-
                 }
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     final TemplateDetailsItem.Header header = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed transaction description " + s + "; header=" + header);
@@ -363,6 +400,19 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 }
             };
             b.transactionComment.addTextChangedListener(transactionCommentWatcher);
+
+            b.templateIsFallbackSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                if (updatePropagationDisabled)
+                    return;
+
+                getItem().setFallback(isChecked);
+                b.templateIsFallbackText.setText(isChecked ? R.string.template_is_fallback_yes
+                                                           : R.string.template_is_fallback_no);
+            });
+            final View.OnClickListener fallbackLabelClickListener =
+                    (view) -> b.templateIsFallbackSwitch.toggle();
+            b.templateIsFallbackLabel.setOnClickListener(fallbackLabelClickListener);
+            b.templateIsFallbackText.setOnClickListener(fallbackLabelClickListener);
         }
         @NotNull
         private TemplateDetailsItem.Header getItem() {
@@ -431,107 +481,119 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
             TemplateDetailsItem.Header header = item.asHeaderItem();
             Logger.debug(D_TEMPLATE_UI, "Binding to header " + header);
 
-            String groupNoText = b.getRoot()
-                                  .getResources()
-                                  .getString(R.string.template_item_match_group_source);
-
-            b.templateName.setText(header.getName());
-            b.pattern.setText(header.getPattern());
-            b.testText.setText(header.getTestText());
+            disableUpdatePropagation();
+            try {
+                String groupNoText = b.getRoot()
+                                      .getResources()
+                                      .getString(R.string.template_item_match_group_source);
+
+                b.templateName.setText(header.getName());
+                b.pattern.setText(header.getPattern());
+                b.testText.setText(header.getTestText());
+
+                if (header.hasLiteralDateYear()) {
+                    b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
+                    final Integer dateYear = header.getDateYear();
+                    b.templateDetailsDateYear.setText(
+                            (dateYear == null) ? null : String.valueOf(dateYear));
+                    b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
+                }
+                else {
+                    b.templateDetailsDateYearLayout.setVisibility(View.GONE);
+                    b.templateDetailsYearSource.setText(
+                            String.format(Locale.US, groupNoText, header.getDateYearMatchGroup(),
+                                    getMatchGroupText(header.getDateYearMatchGroup())));
+                }
+                b.templateDetailsYearSourceLabel.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
+                b.templateDetailsYearSource.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
+
+                if (header.hasLiteralDateMonth()) {
+                    b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
+                    final Integer dateMonth = header.getDateMonth();
+                    b.templateDetailsDateMonth.setText(
+                            (dateMonth == null) ? null : String.valueOf(dateMonth));
+                    b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
+                }
+                else {
+                    b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
+                    b.templateDetailsMonthSource.setText(
+                            String.format(Locale.US, groupNoText, header.getDateMonthMatchGroup(),
+                                    getMatchGroupText(header.getDateMonthMatchGroup())));
+                }
+                b.templateDetailsMonthSourceLabel.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
+                b.templateDetailsMonthSource.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
+
+                if (header.hasLiteralDateDay()) {
+                    b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
+                    final Integer dateDay = header.getDateDay();
+                    b.templateDetailsDateDay.setText(
+                            (dateDay == null) ? null : String.valueOf(dateDay));
+                    b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
+                }
+                else {
+                    b.templateDetailsDateDayLayout.setVisibility(View.GONE);
+                    b.templateDetailsDaySource.setText(
+                            String.format(Locale.US, groupNoText, header.getDateDayMatchGroup(),
+                                    getMatchGroupText(header.getDateDayMatchGroup())));
+                }
+                b.templateDetailsDaySourceLabel.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
+                b.templateDetailsDaySource.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
+
+                if (header.hasLiteralTransactionDescription()) {
+                    b.templateTransactionDescriptionSource.setText(
+                            R.string.template_details_source_literal);
+                    b.transactionDescription.setText(header.getTransactionDescription());
+                    b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
+                }
+                else {
+                    b.transactionDescriptionLayout.setVisibility(View.GONE);
+                    b.templateTransactionDescriptionSource.setText(
+                            String.format(Locale.US, groupNoText,
+                                    header.getTransactionDescriptionMatchGroup(), getMatchGroupText(
+                                            header.getTransactionDescriptionMatchGroup())));
 
-            if (header.hasLiteralDateYear()) {
-                b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
-                final Integer dateYear = header.getDateYear();
-                b.templateDetailsDateYear.setText(
-                        (dateYear == null) ? null : String.valueOf(dateYear));
-                b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
-            }
-            else {
-                b.templateDetailsDateYearLayout.setVisibility(View.GONE);
-                b.templateDetailsYearSource.setText(
-                        String.format(Locale.US, groupNoText, header.getDateYearMatchGroup(),
-                                getMatchGroupText(header.getDateYearMatchGroup())));
-            }
-            b.templateDetailsYearSourceLabel.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
-            b.templateDetailsYearSource.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
-
-            if (header.hasLiteralDateMonth()) {
-                b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
-                final Integer dateMonth = header.getDateMonth();
-                b.templateDetailsDateMonth.setText(
-                        (dateMonth == null) ? null : String.valueOf(dateMonth));
-                b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
-            }
-            else {
-                b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
-                b.templateDetailsMonthSource.setText(
-                        String.format(Locale.US, groupNoText, header.getDateMonthMatchGroup(),
-                                getMatchGroupText(header.getDateMonthMatchGroup())));
-            }
-            b.templateDetailsMonthSourceLabel.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
-            b.templateDetailsMonthSource.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
-
-            if (header.hasLiteralDateDay()) {
-                b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
-                final Integer dateDay = header.getDateDay();
-                b.templateDetailsDateDay.setText(
-                        (dateDay == null) ? null : String.valueOf(dateDay));
-                b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
-            }
-            else {
-                b.templateDetailsDateDayLayout.setVisibility(View.GONE);
-                b.templateDetailsDaySource.setText(
-                        String.format(Locale.US, groupNoText, header.getDateDayMatchGroup(),
-                                getMatchGroupText(header.getDateDayMatchGroup())));
-            }
-            b.templateDetailsDaySourceLabel.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
-            b.templateDetailsDaySource.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
-
-            if (header.hasLiteralTransactionDescription()) {
-                b.templateTransactionDescriptionSource.setText(
-                        R.string.template_details_source_literal);
-                b.transactionDescription.setText(header.getTransactionDescription());
-                b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
-            }
-            else {
-                b.transactionDescriptionLayout.setVisibility(View.GONE);
-                b.templateTransactionDescriptionSource.setText(String.format(Locale.US, groupNoText,
-                        header.getTransactionDescriptionMatchGroup(),
-                        getMatchGroupText(header.getTransactionDescriptionMatchGroup())));
+                }
+                b.templateTransactionDescriptionSourceLabel.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
+                b.templateTransactionDescriptionSource.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
+
+                if (header.hasLiteralTransactionComment()) {
+                    b.templateTransactionCommentSource.setText(
+                            R.string.template_details_source_literal);
+                    b.transactionComment.setText(header.getTransactionComment());
+                    b.transactionCommentLayout.setVisibility(View.VISIBLE);
+                }
+                else {
+                    b.transactionCommentLayout.setVisibility(View.GONE);
+                    b.templateTransactionCommentSource.setText(String.format(Locale.US, groupNoText,
+                            header.getTransactionCommentMatchGroup(),
+                            getMatchGroupText(header.getTransactionCommentMatchGroup())));
 
-            }
-            b.templateTransactionDescriptionSourceLabel.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
-            b.templateTransactionDescriptionSource.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
-
-            if (header.hasLiteralTransactionComment()) {
-                b.templateTransactionCommentSource.setText(
-                        R.string.template_details_source_literal);
-                b.transactionComment.setText(header.getTransactionComment());
-                b.transactionCommentLayout.setVisibility(View.VISIBLE);
-            }
-            else {
-                b.transactionCommentLayout.setVisibility(View.GONE);
-                b.templateTransactionCommentSource.setText(String.format(Locale.US, groupNoText,
-                        header.getTransactionCommentMatchGroup(),
-                        getMatchGroupText(header.getTransactionCommentMatchGroup())));
+                }
+                b.templateTransactionCommentSourceLabel.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
+                b.templateTransactionCommentSource.setOnClickListener(
+                        v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
 
-            }
-            b.templateTransactionCommentSourceLabel.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
-            b.templateTransactionCommentSource.setOnClickListener(
-                    v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
+                b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
 
-            b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
+                b.templateIsFallbackSwitch.setChecked(header.isFallback());
+                b.templateIsFallbackText.setText(
+                        header.isFallback() ? R.string.template_is_fallback_yes
+                                            : R.string.template_is_fallback_no);
 
-            checkPatternError(header);
+                checkPatternError(header);
+            }
+            finally {
+                enableUpdatePropagation();
+            }
         }
         private void checkPatternError(TemplateDetailsItem.Header item) {
             if (item.getPatternError() != null) {
@@ -559,7 +621,7 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
         }
     }
 
-    public class AccountRow extends ViewHolder {
+    public class AccountRow extends BaseItem {
         private final TemplateDetailsAccountBinding b;
         public AccountRow(@NonNull TemplateDetailsAccountBinding binding) {
             super(binding.getRoot());
@@ -572,10 +634,15 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 public void onTextChanged(CharSequence s, int start, int before, int count) {}
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     TemplateDetailsItem.AccountRow accRow = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed account name " + s + "; accRow=" + accRow);
                     accRow.setAccountName(String.valueOf(s));
+
+                    mModel.applyList(null);
                 }
             };
             b.templateDetailsAccountName.addTextChangedListener(accountNameWatcher);
@@ -591,10 +658,15 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                 public void onTextChanged(CharSequence s, int start, int before, int count) {}
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     TemplateDetailsItem.AccountRow accRow = getItem();
                     Logger.debug(D_TEMPLATE_UI,
                             "Storing changed account comment " + s + "; accRow=" + accRow);
                     accRow.setAccountComment(String.valueOf(s));
+
+                    mModel.applyList(null);
                 }
             };
             b.templateDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
@@ -602,14 +674,15 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
             b.templateDetailsAccountAmount.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) {
-
                 }
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (updatePropagationDisabled)
+                        return;
+
                     TemplateDetailsItem.AccountRow accRow = getItem();
 
                     String str = String.valueOf(s);
@@ -630,6 +703,8 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
                             b.templateDetailsAccountAmountLayout.setError("!");
                         }
                     }
+
+                    mModel.applyList(null);
                 }
             });
             b.templateDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
@@ -647,6 +722,9 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
             });
 
             b.negateAmountSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                if (updatePropagationDisabled)
+                    return;
+
                 getItem().setNegateAmount(isChecked);
                 b.templateDetailsNegateAmountText.setText(
                         isChecked ? R.string.template_account_change_amount_sign
@@ -669,74 +747,86 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter
         }
         @Override
         void bind(TemplateDetailsItem item) {
-            String groupNoText = b.getRoot()
-                                  .getResources()
-                                  .getString(R.string.template_item_match_group_source);
-
-            TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
-            if (accRow.hasLiteralAccountName()) {
-                b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
-                b.templateDetailsAccountName.setText(accRow.getAccountName());
-                b.templateDetailsAccountNameSource.setText(
-                        R.string.template_details_source_literal);
-            }
-            else {
-                b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
-                b.templateDetailsAccountNameSource.setText(
-                        String.format(Locale.US, groupNoText, accRow.getAccountNameMatchGroup(),
-                                getMatchGroupText(accRow.getAccountNameMatchGroup())));
-            }
+            disableUpdatePropagation();
+            try {
+                final Resources resources = b.getRoot()
+                                             .getResources();
+                String groupNoText = resources.getString(R.string.template_item_match_group_source);
+
+                Logger.debug("drag", String.format(Locale.US, "Binding account id %d, pos %d at %d",
+                        item.getId(), item.getPosition(), getAdapterPosition()));
+                TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
+                b.patternAccountLabel.setText(String.format(Locale.US,
+                        resources.getString(R.string.template_details_account_row_label),
+                        accRow.getPosition()));
+                if (accRow.hasLiteralAccountName()) {
+                    b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
+                    b.templateDetailsAccountName.setText(accRow.getAccountName());
+                    b.templateDetailsAccountNameSource.setText(
+                            R.string.template_details_source_literal);
+                }
+                else {
+                    b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
+                    b.templateDetailsAccountNameSource.setText(
+                            String.format(Locale.US, groupNoText, accRow.getAccountNameMatchGroup(),
+                                    getMatchGroupText(accRow.getAccountNameMatchGroup())));
+                }
 
-            if (accRow.hasLiteralAccountComment()) {
-                b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
-                b.templateDetailsAccountComment.setText(accRow.getAccountComment());
-                b.templateDetailsAccountCommentSource.setText(
-                        R.string.template_details_source_literal);
-            }
-            else {
-                b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
-                b.templateDetailsAccountCommentSource.setText(
-                        String.format(Locale.US, groupNoText, accRow.getAccountCommentMatchGroup(),
-                                getMatchGroupText(accRow.getAccountCommentMatchGroup())));
-            }
+                if (accRow.hasLiteralAccountComment()) {
+                    b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
+                    b.templateDetailsAccountComment.setText(accRow.getAccountComment());
+                    b.templateDetailsAccountCommentSource.setText(
+                            R.string.template_details_source_literal);
+                }
+                else {
+                    b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
+                    b.templateDetailsAccountCommentSource.setText(
+                            String.format(Locale.US, groupNoText,
+                                    accRow.getAccountCommentMatchGroup(),
+                                    getMatchGroupText(accRow.getAccountCommentMatchGroup())));
+                }
 
-            if (accRow.hasLiteralAmount()) {
-                b.templateDetailsAccountAmountSource.setText(
-                        R.string.template_details_source_literal);
-                b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
-                Float amt = accRow.getAmount();
-                b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
-                        Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
-                b.negateAmountSwitch.setVisibility(View.GONE);
-                b.templateDetailsNegateAmountLabel.setVisibility(View.GONE);
-                b.templateDetailsNegateAmountText.setVisibility(View.GONE);
+                if (accRow.hasLiteralAmount()) {
+                    b.templateDetailsAccountAmountSource.setText(
+                            R.string.template_details_source_literal);
+                    b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
+                    Float amt = accRow.getAmount();
+                    b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
+                            Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
+                    b.negateAmountSwitch.setVisibility(View.GONE);
+                    b.templateDetailsNegateAmountLabel.setVisibility(View.GONE);
+                    b.templateDetailsNegateAmountText.setVisibility(View.GONE);
+                }
+                else {
+                    b.templateDetailsAccountAmountSource.setText(
+                            String.format(Locale.US, groupNoText, accRow.getAmountMatchGroup(),
+                                    getMatchGroupText(accRow.getAmountMatchGroup())));
+                    b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
+                    b.negateAmountSwitch.setVisibility(View.VISIBLE);
+                    b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
+                    b.templateDetailsNegateAmountText.setText(
+                            accRow.isNegateAmount() ? R.string.template_account_change_amount_sign
+                                                    : R.string.template_account_keep_amount_sign);
+                    b.templateDetailsNegateAmountLabel.setVisibility(View.VISIBLE);
+                    b.templateDetailsNegateAmountText.setVisibility(View.VISIBLE);
+                }
+
+                b.templateAccountNameSourceLabel.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
+                b.templateDetailsAccountNameSource.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
+                b.templateAccountCommentSourceLabel.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
+                b.templateDetailsAccountCommentSource.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
+                b.templateAccountAmountSourceLabel.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
+                b.templateDetailsAccountAmountSource.setOnClickListener(
+                        v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
             }
-            else {
-                b.templateDetailsAccountAmountSource.setText(
-                        String.format(Locale.US, groupNoText, accRow.getAmountMatchGroup(),
-                                getMatchGroupText(accRow.getAmountMatchGroup())));
-                b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
-                b.negateAmountSwitch.setVisibility(View.VISIBLE);
-                b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
-                b.templateDetailsNegateAmountText.setText(
-                        accRow.isNegateAmount() ? R.string.template_account_change_amount_sign
-                                                : R.string.template_account_keep_amount_sign);
-                b.templateDetailsNegateAmountLabel.setVisibility(View.VISIBLE);
-                b.templateDetailsNegateAmountText.setVisibility(View.VISIBLE);
+            finally {
+                enableUpdatePropagation();
             }
-
-            b.templateAccountNameSourceLabel.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
-            b.templateDetailsAccountNameSource.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
-            b.templateAccountCommentSourceLabel.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
-            b.templateDetailsAccountCommentSource.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
-            b.templateAccountAmountSourceLabel.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
-            b.templateDetailsAccountAmountSource.setOnClickListener(
-                    v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
         }
         private @NotNull TemplateDetailsItem.AccountRow getItem() {
             return differ.getCurrentList()