2 * Copyright © 2021 Damyan Ivanov.
3 * This file is part of MoLe.
4 * MoLe is free software: you can distribute it and/or modify it
5 * under the term of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your opinion), any later version.
9 * MoLe is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License terms for details.
14 * You should have received a copy of the GNU General Public License
15 * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
18 package net.ktnx.mobileledger.ui.templates;
20 import android.annotation.SuppressLint;
21 import android.content.res.Resources;
22 import android.text.Editable;
23 import android.text.TextWatcher;
24 import android.view.LayoutInflater;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.TextView;
30 import androidx.annotation.NonNull;
31 import androidx.appcompat.app.AppCompatActivity;
32 import androidx.recyclerview.widget.AsyncListDiffer;
33 import androidx.recyclerview.widget.DiffUtil;
34 import androidx.recyclerview.widget.ItemTouchHelper;
35 import androidx.recyclerview.widget.RecyclerView;
37 import net.ktnx.mobileledger.R;
38 import net.ktnx.mobileledger.databinding.TemplateDetailsAccountBinding;
39 import net.ktnx.mobileledger.databinding.TemplateDetailsHeaderBinding;
40 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
41 import net.ktnx.mobileledger.db.TemplateBase;
42 import net.ktnx.mobileledger.model.Data;
43 import net.ktnx.mobileledger.model.TemplateDetailsItem;
44 import net.ktnx.mobileledger.ui.QRScanCapableFragment;
45 import net.ktnx.mobileledger.ui.TemplateDetailSourceSelectorFragment;
46 import net.ktnx.mobileledger.utils.Logger;
47 import net.ktnx.mobileledger.utils.Misc;
49 import org.jetbrains.annotations.NotNull;
51 import java.text.ParseException;
52 import java.util.ArrayList;
53 import java.util.List;
54 import java.util.Locale;
55 import java.util.regex.Matcher;
56 import java.util.regex.Pattern;
58 class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter.ViewHolder> {
59 private static final String D_TEMPLATE_UI = "template-ui";
60 private final AsyncListDiffer<TemplateDetailsItem> differ;
61 private final TemplateDetailsViewModel mModel;
62 private final ItemTouchHelper itemTouchHelper;
63 public TemplateDetailsAdapter(TemplateDetailsViewModel model) {
66 setHasStableIds(true);
67 differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TemplateDetailsItem>() {
69 public boolean areItemsTheSame(@NonNull TemplateDetailsItem oldItem,
70 @NonNull TemplateDetailsItem newItem) {
71 if (oldItem.getType() != newItem.getType())
74 .equals(TemplateDetailsItem.Type.HEADER))
75 return true; // only one header item, ever
76 // the rest is comparing two account row items
77 return oldItem.asAccountRowItem()
78 .getId() == newItem.asAccountRowItem()
82 public boolean areContentsTheSame(@NonNull TemplateDetailsItem oldItem,
83 @NonNull TemplateDetailsItem newItem) {
85 .equals(TemplateDetailsItem.Type.HEADER))
87 TemplateDetailsItem.Header oldHeader = oldItem.asHeaderItem();
88 TemplateDetailsItem.Header newHeader = newItem.asHeaderItem();
90 return oldHeader.equalContents(newHeader);
93 TemplateDetailsItem.AccountRow oldAcc = oldItem.asAccountRowItem();
94 TemplateDetailsItem.AccountRow newAcc = newItem.asAccountRowItem();
96 return oldAcc.equalContents(newAcc);
100 itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
102 public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
106 public boolean isLongPressDragEnabled() {
110 public RecyclerView.ViewHolder chooseDropTarget(
111 @NonNull RecyclerView.ViewHolder selected,
112 @NonNull List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
113 RecyclerView.ViewHolder best = null;
114 int bestDistance = 0;
115 for (RecyclerView.ViewHolder v : dropTargets) {
119 final int viewTop = v.itemView.getTop();
120 int distance = Math.abs(viewTop - curY);
123 bestDistance = distance;
126 if (distance < bestDistance) {
127 bestDistance = distance;
133 Logger.debug("dnd", "Best target is " + best);
137 public boolean canDropOver(@NonNull RecyclerView recyclerView,
138 @NonNull RecyclerView.ViewHolder current,
139 @NonNull RecyclerView.ViewHolder target) {
140 final int adapterPosition = target.getAdapterPosition();
142 // first item is immovable
143 if (adapterPosition == 0)
146 return super.canDropOver(recyclerView, current, target);
149 public int getMovementFlags(@NonNull RecyclerView recyclerView,
150 @NonNull RecyclerView.ViewHolder viewHolder) {
152 // the top item (transaction params) is always there
153 final int adapterPosition = viewHolder.getAdapterPosition();
154 if (adapterPosition > 0)
155 flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
156 ItemTouchHelper.UP | ItemTouchHelper.DOWN) |
157 makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE,
158 ItemTouchHelper.START | ItemTouchHelper.END);
163 public boolean onMove(@NonNull RecyclerView recyclerView,
164 @NonNull RecyclerView.ViewHolder viewHolder,
165 @NonNull RecyclerView.ViewHolder target) {
167 final int fromPosition = viewHolder.getAdapterPosition();
168 final int toPosition = target.getAdapterPosition();
169 mModel.moveItem(fromPosition, toPosition);
174 public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
175 int pos = viewHolder.getAdapterPosition();
176 mModel.removeItem(pos);
181 public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
182 super.onAttachedToRecyclerView(recyclerView);
184 itemTouchHelper.attachToRecyclerView(recyclerView);
187 public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
188 super.onDetachedFromRecyclerView(recyclerView);
190 itemTouchHelper.attachToRecyclerView(null);
193 public long getItemId(int position) {
194 // header item is always first and IDs id may duplicate some of the account IDs
197 TemplateDetailsItem.AccountRow accRow = differ.getCurrentList()
200 return accRow.getId();
203 public int getItemViewType(int position) {
205 return differ.getCurrentList()
212 public TemplateDetailsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
214 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
216 case TemplateDetailsItem.TYPE.header:
217 return new Header(TemplateDetailsHeaderBinding.inflate(inflater, parent, false));
218 case TemplateDetailsItem.TYPE.accountItem:
219 return new AccountRow(
220 TemplateDetailsAccountBinding.inflate(inflater, parent, false));
222 throw new IllegalStateException("Unsupported view type " + viewType);
226 public void onBindViewHolder(@NonNull TemplateDetailsAdapter.ViewHolder holder, int position) {
227 TemplateDetailsItem item = differ.getCurrentList()
232 public int getItemCount() {
233 return differ.getCurrentList()
236 public void setTemplateItems(List<TemplateBase> items) {
237 ArrayList<TemplateDetailsItem> list = new ArrayList<>();
238 for (TemplateBase p : items) {
239 TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p);
244 public void setItems(List<TemplateDetailsItem> items) {
245 differ.submitList(items);
247 public String getMatchGroupText(int groupNumber) {
248 TemplateDetailsItem.Header header = getHeader();
249 Pattern p = header.getCompiledPattern();
253 final String testText = Misc.nullIsEmpty(header.getTestText());
254 Matcher m = p.matcher(testText);
255 if (m.matches() && m.groupCount() >= groupNumber)
256 return m.group(groupNumber);
260 protected TemplateDetailsItem.Header getHeader() {
261 return differ.getCurrentList()
266 private enum HeaderDetail {DESCRIPTION, COMMENT, DATE_YEAR, DATE_MONTH, DATE_DAY}
268 private enum AccDetail {ACCOUNT, COMMENT, AMOUNT}
270 public abstract static class ViewHolder extends RecyclerView.ViewHolder {
271 ViewHolder(@NonNull View itemView) {
274 abstract void bind(TemplateDetailsItem item);
277 private abstract static class BaseItem extends ViewHolder {
278 boolean updatePropagationDisabled = false;
279 BaseItem(@NonNull View itemView) {
282 void disableUpdatePropagation() {
283 updatePropagationDisabled = true;
285 void enableUpdatePropagation() {
286 updatePropagationDisabled = false;
290 public class Header extends BaseItem {
291 private final TemplateDetailsHeaderBinding b;
292 boolean updatePropagationDisabled = false;
293 public Header(@NonNull TemplateDetailsHeaderBinding binding) {
294 super(binding.getRoot());
297 TextWatcher templateNameWatcher = new TextWatcher() {
299 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
301 public void onTextChanged(CharSequence s, int start, int before, int count) {}
303 public void afterTextChanged(Editable s) {
304 if (updatePropagationDisabled)
307 final TemplateDetailsItem.Header header = getItem();
308 Logger.debug(D_TEMPLATE_UI,
309 "Storing changed template name " + s + "; header=" + header);
310 header.setName(String.valueOf(s));
313 b.templateName.addTextChangedListener(templateNameWatcher);
315 TextWatcher patternWatcher = new TextWatcher() {
317 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
319 public void onTextChanged(CharSequence s, int start, int before, int count) {}
321 public void afterTextChanged(Editable s) {
322 if (updatePropagationDisabled)
325 final TemplateDetailsItem.Header header = getItem();
326 Logger.debug(D_TEMPLATE_UI,
327 "Storing changed pattern " + s + "; header=" + header);
328 header.setPattern(String.valueOf(s));
330 checkPatternError(header);
333 b.pattern.addTextChangedListener(patternWatcher);
335 TextWatcher testTextWatcher = new TextWatcher() {
337 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
339 public void onTextChanged(CharSequence s, int start, int before, int count) {}
341 public void afterTextChanged(Editable s) {
342 if (updatePropagationDisabled)
345 final TemplateDetailsItem.Header header = getItem();
346 Logger.debug(D_TEMPLATE_UI,
347 "Storing changed test text " + s + "; header=" + header);
348 header.setTestText(String.valueOf(s));
350 checkPatternError(header);
353 b.testText.addTextChangedListener(testTextWatcher);
355 TextWatcher transactionDescriptionWatcher = new TextWatcher() {
357 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
360 public void onTextChanged(CharSequence s, int start, int before, int count) {
363 public void afterTextChanged(Editable s) {
364 if (updatePropagationDisabled)
367 final TemplateDetailsItem.Header header = getItem();
368 Logger.debug(D_TEMPLATE_UI,
369 "Storing changed transaction description " + s + "; header=" + header);
370 header.setTransactionDescription(String.valueOf(s));
373 b.transactionDescription.addTextChangedListener(transactionDescriptionWatcher);
374 TextWatcher transactionCommentWatcher = new TextWatcher() {
376 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
379 public void onTextChanged(CharSequence s, int start, int before, int count) {
382 public void afterTextChanged(Editable s) {
383 if (updatePropagationDisabled)
386 final TemplateDetailsItem.Header header = getItem();
387 Logger.debug(D_TEMPLATE_UI,
388 "Storing changed transaction description " + s + "; header=" + header);
389 header.setTransactionComment(String.valueOf(s));
392 b.transactionComment.addTextChangedListener(transactionCommentWatcher);
395 private TemplateDetailsItem.Header getItem() {
396 int pos = getAdapterPosition();
397 return differ.getCurrentList()
401 private void selectHeaderDetailSource(View v, HeaderDetail detail) {
402 TemplateDetailsItem.Header header = getItem();
403 Logger.debug(D_TEMPLATE_UI, "header is " + header);
404 TemplateDetailSourceSelectorFragment sel =
405 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
406 header.getTestText());
407 sel.setOnSourceSelectedListener((literal, group) -> {
411 header.switchToLiteralTransactionDescription();
414 header.switchToLiteralTransactionComment();
417 header.switchToLiteralDateYear();
420 header.switchToLiteralDateMonth();
423 header.switchToLiteralDateDay();
426 throw new IllegalStateException("Unexpected detail " + detail);
432 header.setTransactionDescriptionMatchGroup(group);
435 header.setTransactionCommentMatchGroup(group);
438 header.setDateYearMatchGroup(group);
441 header.setDateMonthMatchGroup(group);
444 header.setDateDayMatchGroup(group);
447 throw new IllegalStateException("Unexpected detail " + detail);
451 notifyItemChanged(getAdapterPosition());
453 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
454 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");
457 void bind(TemplateDetailsItem item) {
458 TemplateDetailsItem.Header header = item.asHeaderItem();
459 Logger.debug(D_TEMPLATE_UI, "Binding to header " + header);
461 disableUpdatePropagation();
463 String groupNoText = b.getRoot()
465 .getString(R.string.template_item_match_group_source);
467 b.templateName.setText(header.getName());
468 b.pattern.setText(header.getPattern());
469 b.testText.setText(header.getTestText());
471 if (header.hasLiteralDateYear()) {
472 b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
473 final Integer dateYear = header.getDateYear();
474 b.templateDetailsDateYear.setText(
475 (dateYear == null) ? null : String.valueOf(dateYear));
476 b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
479 b.templateDetailsDateYearLayout.setVisibility(View.GONE);
480 b.templateDetailsYearSource.setText(
481 String.format(Locale.US, groupNoText, header.getDateYearMatchGroup(),
482 getMatchGroupText(header.getDateYearMatchGroup())));
484 b.templateDetailsYearSourceLabel.setOnClickListener(
485 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
486 b.templateDetailsYearSource.setOnClickListener(
487 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
489 if (header.hasLiteralDateMonth()) {
490 b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
491 final Integer dateMonth = header.getDateMonth();
492 b.templateDetailsDateMonth.setText(
493 (dateMonth == null) ? null : String.valueOf(dateMonth));
494 b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
497 b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
498 b.templateDetailsMonthSource.setText(
499 String.format(Locale.US, groupNoText, header.getDateMonthMatchGroup(),
500 getMatchGroupText(header.getDateMonthMatchGroup())));
502 b.templateDetailsMonthSourceLabel.setOnClickListener(
503 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
504 b.templateDetailsMonthSource.setOnClickListener(
505 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
507 if (header.hasLiteralDateDay()) {
508 b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
509 final Integer dateDay = header.getDateDay();
510 b.templateDetailsDateDay.setText(
511 (dateDay == null) ? null : String.valueOf(dateDay));
512 b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
515 b.templateDetailsDateDayLayout.setVisibility(View.GONE);
516 b.templateDetailsDaySource.setText(
517 String.format(Locale.US, groupNoText, header.getDateDayMatchGroup(),
518 getMatchGroupText(header.getDateDayMatchGroup())));
520 b.templateDetailsDaySourceLabel.setOnClickListener(
521 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
522 b.templateDetailsDaySource.setOnClickListener(
523 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
525 if (header.hasLiteralTransactionDescription()) {
526 b.templateTransactionDescriptionSource.setText(
527 R.string.template_details_source_literal);
528 b.transactionDescription.setText(header.getTransactionDescription());
529 b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
532 b.transactionDescriptionLayout.setVisibility(View.GONE);
533 b.templateTransactionDescriptionSource.setText(
534 String.format(Locale.US, groupNoText,
535 header.getTransactionDescriptionMatchGroup(), getMatchGroupText(
536 header.getTransactionDescriptionMatchGroup())));
539 b.templateTransactionDescriptionSourceLabel.setOnClickListener(
540 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
541 b.templateTransactionDescriptionSource.setOnClickListener(
542 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
544 if (header.hasLiteralTransactionComment()) {
545 b.templateTransactionCommentSource.setText(
546 R.string.template_details_source_literal);
547 b.transactionComment.setText(header.getTransactionComment());
548 b.transactionCommentLayout.setVisibility(View.VISIBLE);
551 b.transactionCommentLayout.setVisibility(View.GONE);
552 b.templateTransactionCommentSource.setText(String.format(Locale.US, groupNoText,
553 header.getTransactionCommentMatchGroup(),
554 getMatchGroupText(header.getTransactionCommentMatchGroup())));
557 b.templateTransactionCommentSourceLabel.setOnClickListener(
558 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
559 b.templateTransactionCommentSource.setOnClickListener(
560 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
562 b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
564 checkPatternError(header);
567 enableUpdatePropagation();
570 private void checkPatternError(TemplateDetailsItem.Header item) {
571 if (item.getPatternError() != null) {
572 b.patternLayout.setError(item.getPatternError());
573 b.patternHintTitle.setVisibility(View.GONE);
574 b.patternHintText.setVisibility(View.GONE);
577 b.patternLayout.setError(null);
578 if (item.testMatch() != null) {
579 b.patternHintText.setText(item.testMatch());
580 b.patternHintTitle.setVisibility(View.VISIBLE);
581 b.patternHintText.setVisibility(View.VISIBLE);
584 b.patternLayout.setError(null);
585 b.patternHintTitle.setVisibility(View.GONE);
586 b.patternHintText.setVisibility(View.GONE);
591 private void scanTestQR(View view) {
592 QRScanCapableFragment.triggerQRScan();
596 public class AccountRow extends BaseItem {
597 private final TemplateDetailsAccountBinding b;
598 public AccountRow(@NonNull TemplateDetailsAccountBinding binding) {
599 super(binding.getRoot());
602 TextWatcher accountNameWatcher = new TextWatcher() {
604 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
606 public void onTextChanged(CharSequence s, int start, int before, int count) {}
608 public void afterTextChanged(Editable s) {
609 if (updatePropagationDisabled)
612 TemplateDetailsItem.AccountRow accRow = getItem();
613 Logger.debug(D_TEMPLATE_UI,
614 "Storing changed account name " + s + "; accRow=" + accRow);
615 accRow.setAccountName(String.valueOf(s));
618 b.templateDetailsAccountName.addTextChangedListener(accountNameWatcher);
619 b.templateDetailsAccountName.setAdapter(new AccountAutocompleteAdapter(b.getRoot()
621 b.templateDetailsAccountName.setOnItemClickListener(
622 (parent, view, position, id) -> b.templateDetailsAccountName.setText(
623 ((TextView) view).getText()));
624 TextWatcher accountCommentWatcher = new TextWatcher() {
626 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
628 public void onTextChanged(CharSequence s, int start, int before, int count) {}
630 public void afterTextChanged(Editable s) {
631 if (updatePropagationDisabled)
634 TemplateDetailsItem.AccountRow accRow = getItem();
635 Logger.debug(D_TEMPLATE_UI,
636 "Storing changed account comment " + s + "; accRow=" + accRow);
637 accRow.setAccountComment(String.valueOf(s));
640 b.templateDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
642 b.templateDetailsAccountAmount.addTextChangedListener(new TextWatcher() {
644 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
648 public void onTextChanged(CharSequence s, int start, int before, int count) {
652 public void afterTextChanged(Editable s) {
653 if (updatePropagationDisabled)
656 TemplateDetailsItem.AccountRow accRow = getItem();
658 String str = String.valueOf(s);
659 if (Misc.emptyIsNull(str) == null) {
660 accRow.setAmount(null);
664 final float amount = Data.parseNumber(str);
665 accRow.setAmount(amount);
666 b.templateDetailsAccountAmountLayout.setError(null);
668 Logger.debug(D_TEMPLATE_UI, String.format(Locale.US,
669 "Storing changed account amount %s [%4.2f]; accRow=%s", s,
672 catch (NumberFormatException | ParseException e) {
673 b.templateDetailsAccountAmountLayout.setError("!");
678 b.templateDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
682 TemplateDetailsItem.AccountRow accRow = getItem();
683 if (!accRow.hasLiteralAmount())
685 Float amt = accRow.getAmount();
689 b.templateDetailsAccountAmount.setText(Data.formatNumber(amt));
692 b.negateAmountSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
693 if (updatePropagationDisabled)
696 getItem().setNegateAmount(isChecked);
697 b.templateDetailsNegateAmountText.setText(
698 isChecked ? R.string.template_account_change_amount_sign
699 : R.string.template_account_keep_amount_sign);
701 final View.OnClickListener negLabelClickListener =
702 (view) -> b.negateAmountSwitch.toggle();
703 b.templateDetailsNegateAmountLabel.setOnClickListener(negLabelClickListener);
704 b.templateDetailsNegateAmountText.setOnClickListener(negLabelClickListener);
705 manageAccountLabelDrag();
707 @SuppressLint("ClickableViewAccessibility")
708 public void manageAccountLabelDrag() {
709 b.patternAccountLabel.setOnTouchListener((v, event) -> {
710 if (event.getAction() == MotionEvent.ACTION_DOWN) {
711 itemTouchHelper.startDrag(this);
717 void bind(TemplateDetailsItem item) {
718 disableUpdatePropagation();
720 final Resources resources = b.getRoot()
722 String groupNoText = resources.getString(R.string.template_item_match_group_source);
724 TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
725 b.patternAccountLabel.setText(String.format(Locale.US,
726 resources.getString(R.string.template_details_account_row_label),
727 accRow.getPosition()));
728 if (accRow.hasLiteralAccountName()) {
729 b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
730 b.templateDetailsAccountName.setText(accRow.getAccountName());
731 b.templateDetailsAccountNameSource.setText(
732 R.string.template_details_source_literal);
735 b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
736 b.templateDetailsAccountNameSource.setText(
737 String.format(Locale.US, groupNoText, accRow.getAccountNameMatchGroup(),
738 getMatchGroupText(accRow.getAccountNameMatchGroup())));
741 if (accRow.hasLiteralAccountComment()) {
742 b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
743 b.templateDetailsAccountComment.setText(accRow.getAccountComment());
744 b.templateDetailsAccountCommentSource.setText(
745 R.string.template_details_source_literal);
748 b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
749 b.templateDetailsAccountCommentSource.setText(
750 String.format(Locale.US, groupNoText,
751 accRow.getAccountCommentMatchGroup(),
752 getMatchGroupText(accRow.getAccountCommentMatchGroup())));
755 if (accRow.hasLiteralAmount()) {
756 b.templateDetailsAccountAmountSource.setText(
757 R.string.template_details_source_literal);
758 b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
759 Float amt = accRow.getAmount();
760 b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
761 Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
762 b.negateAmountSwitch.setVisibility(View.GONE);
763 b.templateDetailsNegateAmountLabel.setVisibility(View.GONE);
764 b.templateDetailsNegateAmountText.setVisibility(View.GONE);
767 b.templateDetailsAccountAmountSource.setText(
768 String.format(Locale.US, groupNoText, accRow.getAmountMatchGroup(),
769 getMatchGroupText(accRow.getAmountMatchGroup())));
770 b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
771 b.negateAmountSwitch.setVisibility(View.VISIBLE);
772 b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
773 b.templateDetailsNegateAmountText.setText(
774 accRow.isNegateAmount() ? R.string.template_account_change_amount_sign
775 : R.string.template_account_keep_amount_sign);
776 b.templateDetailsNegateAmountLabel.setVisibility(View.VISIBLE);
777 b.templateDetailsNegateAmountText.setVisibility(View.VISIBLE);
780 b.templateAccountNameSourceLabel.setOnClickListener(
781 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
782 b.templateDetailsAccountNameSource.setOnClickListener(
783 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
784 b.templateAccountCommentSourceLabel.setOnClickListener(
785 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
786 b.templateDetailsAccountCommentSource.setOnClickListener(
787 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
788 b.templateAccountAmountSourceLabel.setOnClickListener(
789 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
790 b.templateDetailsAccountAmountSource.setOnClickListener(
791 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
794 enableUpdatePropagation();
797 private @NotNull TemplateDetailsItem.AccountRow getItem() {
798 return differ.getCurrentList()
799 .get(getAdapterPosition())
802 private void selectAccountRowDetailSource(View v, AccDetail detail) {
803 TemplateDetailsItem.AccountRow accRow = getItem();
804 final TemplateDetailsItem.Header header = getHeader();
805 Logger.debug(D_TEMPLATE_UI, "header is " + header);
806 TemplateDetailSourceSelectorFragment sel =
807 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
808 header.getTestText());
809 sel.setOnSourceSelectedListener((literal, group) -> {
813 accRow.switchToLiteralAccountName();
816 accRow.switchToLiteralAccountComment();
819 accRow.switchToLiteralAmount();
822 throw new IllegalStateException("Unexpected detail " + detail);
828 accRow.setAccountNameMatchGroup(group);
831 accRow.setAccountCommentMatchGroup(group);
834 accRow.setAmountMatchGroup(group);
837 throw new IllegalStateException("Unexpected detail " + detail);
841 notifyItemChanged(getAdapterPosition());
843 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
844 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");