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.text.Editable;
21 import android.text.TextWatcher;
22 import android.view.LayoutInflater;
23 import android.view.MotionEvent;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.widget.TextView;
28 import androidx.annotation.NonNull;
29 import androidx.appcompat.app.AppCompatActivity;
30 import androidx.recyclerview.widget.AsyncListDiffer;
31 import androidx.recyclerview.widget.DiffUtil;
32 import androidx.recyclerview.widget.ItemTouchHelper;
33 import androidx.recyclerview.widget.RecyclerView;
35 import net.ktnx.mobileledger.R;
36 import net.ktnx.mobileledger.databinding.TemplateDetailsAccountBinding;
37 import net.ktnx.mobileledger.databinding.TemplateDetailsHeaderBinding;
38 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
39 import net.ktnx.mobileledger.db.TemplateBase;
40 import net.ktnx.mobileledger.model.Data;
41 import net.ktnx.mobileledger.model.TemplateDetailsItem;
42 import net.ktnx.mobileledger.ui.QRScanCapableFragment;
43 import net.ktnx.mobileledger.ui.TemplateDetailSourceSelectorFragment;
44 import net.ktnx.mobileledger.utils.Logger;
45 import net.ktnx.mobileledger.utils.Misc;
47 import org.jetbrains.annotations.NotNull;
49 import java.text.ParseException;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Locale;
53 import java.util.regex.Matcher;
54 import java.util.regex.Pattern;
56 class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter.ViewHolder> {
57 private static final String D_TEMPLATE_UI = "template-ui";
58 private final AsyncListDiffer<TemplateDetailsItem> differ;
59 private final TemplateDetailsViewModel mModel;
60 private final ItemTouchHelper itemTouchHelper;
61 public TemplateDetailsAdapter(TemplateDetailsViewModel model) {
64 setHasStableIds(true);
65 differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TemplateDetailsItem>() {
67 public boolean areItemsTheSame(@NonNull TemplateDetailsItem oldItem,
68 @NonNull TemplateDetailsItem newItem) {
69 if (oldItem.getType() != newItem.getType())
72 .equals(TemplateDetailsItem.Type.HEADER))
73 return true; // only one header item, ever
74 // the rest is comparing two account row items
75 return oldItem.asAccountRowItem()
76 .getId() == newItem.asAccountRowItem()
80 public boolean areContentsTheSame(@NonNull TemplateDetailsItem oldItem,
81 @NonNull TemplateDetailsItem newItem) {
83 .equals(TemplateDetailsItem.Type.HEADER))
85 TemplateDetailsItem.Header oldHeader = oldItem.asHeaderItem();
86 TemplateDetailsItem.Header newHeader = newItem.asHeaderItem();
88 return oldHeader.equalContents(newHeader);
91 TemplateDetailsItem.AccountRow oldAcc = oldItem.asAccountRowItem();
92 TemplateDetailsItem.AccountRow newAcc = newItem.asAccountRowItem();
94 return oldAcc.equalContents(newAcc);
98 itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
100 public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
104 public boolean isLongPressDragEnabled() {
108 public RecyclerView.ViewHolder chooseDropTarget(
109 @NonNull RecyclerView.ViewHolder selected,
110 @NonNull List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
111 RecyclerView.ViewHolder best = null;
112 int bestDistance = 0;
113 for (RecyclerView.ViewHolder v : dropTargets) {
117 final int viewTop = v.itemView.getTop();
118 int distance = Math.abs(viewTop - curY);
121 bestDistance = distance;
124 if (distance < bestDistance) {
125 bestDistance = distance;
131 Logger.debug("dnd", "Best target is " + best);
135 public boolean canDropOver(@NonNull RecyclerView recyclerView,
136 @NonNull RecyclerView.ViewHolder current,
137 @NonNull RecyclerView.ViewHolder target) {
138 final int adapterPosition = target.getAdapterPosition();
140 // first item is immovable
141 if (adapterPosition == 0)
144 return super.canDropOver(recyclerView, current, target);
147 public int getMovementFlags(@NonNull RecyclerView recyclerView,
148 @NonNull RecyclerView.ViewHolder viewHolder) {
150 // the top item (transaction params) is always there
151 final int adapterPosition = viewHolder.getAdapterPosition();
152 if (adapterPosition > 0)
153 flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
154 ItemTouchHelper.UP | ItemTouchHelper.DOWN) |
155 makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE,
156 ItemTouchHelper.START | ItemTouchHelper.END);
161 public boolean onMove(@NonNull RecyclerView recyclerView,
162 @NonNull RecyclerView.ViewHolder viewHolder,
163 @NonNull RecyclerView.ViewHolder target) {
165 final int fromPosition = viewHolder.getAdapterPosition();
166 final int toPosition = target.getAdapterPosition();
167 mModel.moveItem(fromPosition, toPosition);
172 public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
173 int pos = viewHolder.getAdapterPosition();
174 mModel.removeItem(pos);
179 public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
180 super.onAttachedToRecyclerView(recyclerView);
182 itemTouchHelper.attachToRecyclerView(recyclerView);
185 public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
186 super.onDetachedFromRecyclerView(recyclerView);
188 itemTouchHelper.attachToRecyclerView(null);
191 public long getItemId(int position) {
192 // header item is always first and IDs id may duplicate some of the account IDs
195 TemplateDetailsItem.AccountRow accRow = differ.getCurrentList()
198 return accRow.getId();
201 public int getItemViewType(int position) {
203 return differ.getCurrentList()
210 public TemplateDetailsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
212 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
214 case TemplateDetailsItem.TYPE.header:
215 return new Header(TemplateDetailsHeaderBinding.inflate(inflater, parent, false));
216 case TemplateDetailsItem.TYPE.accountItem:
217 return new AccountRow(
218 TemplateDetailsAccountBinding.inflate(inflater, parent, false));
220 throw new IllegalStateException("Unsupported view type " + viewType);
224 public void onBindViewHolder(@NonNull TemplateDetailsAdapter.ViewHolder holder, int position) {
225 TemplateDetailsItem item = differ.getCurrentList()
230 public int getItemCount() {
231 return differ.getCurrentList()
234 public void setTemplateItems(List<TemplateBase> items) {
235 ArrayList<TemplateDetailsItem> list = new ArrayList<>();
236 for (TemplateBase p : items) {
237 TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p);
242 public void setItems(List<TemplateDetailsItem> items) {
243 differ.submitList(items);
245 public String getMatchGroupText(int groupNumber) {
246 TemplateDetailsItem.Header header = getHeader();
247 Pattern p = header.getCompiledPattern();
251 final String testText = Misc.nullIsEmpty(header.getTestText());
252 Matcher m = p.matcher(testText);
253 if (m.matches() && m.groupCount() >= groupNumber)
254 return m.group(groupNumber);
258 protected TemplateDetailsItem.Header getHeader() {
259 return differ.getCurrentList()
264 private enum HeaderDetail {DESCRIPTION, COMMENT, DATE_YEAR, DATE_MONTH, DATE_DAY}
266 private enum AccDetail {ACCOUNT, COMMENT, AMOUNT}
268 public abstract static class ViewHolder extends RecyclerView.ViewHolder {
269 ViewHolder(@NonNull View itemView) {
272 abstract void bind(TemplateDetailsItem item);
275 public class Header extends ViewHolder {
276 private final TemplateDetailsHeaderBinding b;
277 public Header(@NonNull TemplateDetailsHeaderBinding binding) {
278 super(binding.getRoot());
281 TextWatcher templateNameWatcher = new TextWatcher() {
283 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
285 public void onTextChanged(CharSequence s, int start, int before, int count) {}
287 public void afterTextChanged(Editable s) {
288 final TemplateDetailsItem.Header header = getItem();
289 Logger.debug(D_TEMPLATE_UI,
290 "Storing changed template name " + s + "; header=" + header);
291 header.setName(String.valueOf(s));
294 b.templateName.addTextChangedListener(templateNameWatcher);
296 TextWatcher patternWatcher = new TextWatcher() {
298 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
300 public void onTextChanged(CharSequence s, int start, int before, int count) {}
302 public void afterTextChanged(Editable s) {
303 final TemplateDetailsItem.Header header = getItem();
304 Logger.debug(D_TEMPLATE_UI,
305 "Storing changed pattern " + s + "; header=" + header);
306 header.setPattern(String.valueOf(s));
308 checkPatternError(header);
311 b.pattern.addTextChangedListener(patternWatcher);
313 TextWatcher testTextWatcher = new TextWatcher() {
315 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
317 public void onTextChanged(CharSequence s, int start, int before, int count) {}
319 public void afterTextChanged(Editable s) {
320 final TemplateDetailsItem.Header header = getItem();
321 Logger.debug(D_TEMPLATE_UI,
322 "Storing changed test text " + s + "; header=" + header);
323 header.setTestText(String.valueOf(s));
325 checkPatternError(header);
328 b.testText.addTextChangedListener(testTextWatcher);
330 TextWatcher transactionDescriptionWatcher = new TextWatcher() {
332 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
335 public void onTextChanged(CharSequence s, int start, int before, int count) {
339 public void afterTextChanged(Editable s) {
340 final TemplateDetailsItem.Header header = getItem();
341 Logger.debug(D_TEMPLATE_UI,
342 "Storing changed transaction description " + s + "; header=" + header);
343 header.setTransactionDescription(String.valueOf(s));
346 b.transactionDescription.addTextChangedListener(transactionDescriptionWatcher);
347 TextWatcher transactionCommentWatcher = new TextWatcher() {
349 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
353 public void onTextChanged(CharSequence s, int start, int before, int count) {
357 public void afterTextChanged(Editable s) {
358 final TemplateDetailsItem.Header header = getItem();
359 Logger.debug(D_TEMPLATE_UI,
360 "Storing changed transaction description " + s + "; header=" + header);
361 header.setTransactionComment(String.valueOf(s));
364 b.transactionComment.addTextChangedListener(transactionCommentWatcher);
367 private TemplateDetailsItem.Header getItem() {
368 int pos = getAdapterPosition();
369 return differ.getCurrentList()
373 private void selectHeaderDetailSource(View v, HeaderDetail detail) {
374 TemplateDetailsItem.Header header = getItem();
375 Logger.debug(D_TEMPLATE_UI, "header is " + header);
376 TemplateDetailSourceSelectorFragment sel =
377 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
378 header.getTestText());
379 sel.setOnSourceSelectedListener((literal, group) -> {
383 header.switchToLiteralTransactionDescription();
386 header.switchToLiteralTransactionComment();
389 header.switchToLiteralDateYear();
392 header.switchToLiteralDateMonth();
395 header.switchToLiteralDateDay();
398 throw new IllegalStateException("Unexpected detail " + detail);
404 header.setTransactionDescriptionMatchGroup(group);
407 header.setTransactionCommentMatchGroup(group);
410 header.setDateYearMatchGroup(group);
413 header.setDateMonthMatchGroup(group);
416 header.setDateDayMatchGroup(group);
419 throw new IllegalStateException("Unexpected detail " + detail);
423 notifyItemChanged(getAdapterPosition());
425 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
426 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");
429 void bind(TemplateDetailsItem item) {
430 TemplateDetailsItem.Header header = item.asHeaderItem();
431 Logger.debug(D_TEMPLATE_UI, "Binding to header " + header);
433 String groupNoText = b.getRoot()
435 .getString(R.string.template_item_match_group_source);
437 b.templateName.setText(header.getName());
438 b.pattern.setText(header.getPattern());
439 b.testText.setText(header.getTestText());
441 if (header.hasLiteralDateYear()) {
442 b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
443 final Integer dateYear = header.getDateYear();
444 b.templateDetailsDateYear.setText(
445 (dateYear == null) ? null : String.valueOf(dateYear));
446 b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
449 b.templateDetailsDateYearLayout.setVisibility(View.GONE);
450 b.templateDetailsYearSource.setText(
451 String.format(Locale.US, groupNoText, header.getDateYearMatchGroup(),
452 getMatchGroupText(header.getDateYearMatchGroup())));
454 b.templateDetailsYearSourceLabel.setOnClickListener(
455 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
456 b.templateDetailsYearSource.setOnClickListener(
457 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
459 if (header.hasLiteralDateMonth()) {
460 b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
461 final Integer dateMonth = header.getDateMonth();
462 b.templateDetailsDateMonth.setText(
463 (dateMonth == null) ? null : String.valueOf(dateMonth));
464 b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
467 b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
468 b.templateDetailsMonthSource.setText(
469 String.format(Locale.US, groupNoText, header.getDateMonthMatchGroup(),
470 getMatchGroupText(header.getDateMonthMatchGroup())));
472 b.templateDetailsMonthSourceLabel.setOnClickListener(
473 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
474 b.templateDetailsMonthSource.setOnClickListener(
475 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
477 if (header.hasLiteralDateDay()) {
478 b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
479 final Integer dateDay = header.getDateDay();
480 b.templateDetailsDateDay.setText(
481 (dateDay == null) ? null : String.valueOf(dateDay));
482 b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
485 b.templateDetailsDateDayLayout.setVisibility(View.GONE);
486 b.templateDetailsDaySource.setText(
487 String.format(Locale.US, groupNoText, header.getDateDayMatchGroup(),
488 getMatchGroupText(header.getDateDayMatchGroup())));
490 b.templateDetailsDaySourceLabel.setOnClickListener(
491 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
492 b.templateDetailsDaySource.setOnClickListener(
493 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
495 if (header.hasLiteralTransactionDescription()) {
496 b.templateTransactionDescriptionSource.setText(
497 R.string.template_details_source_literal);
498 b.transactionDescription.setText(header.getTransactionDescription());
499 b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
502 b.transactionDescriptionLayout.setVisibility(View.GONE);
503 b.templateTransactionDescriptionSource.setText(String.format(Locale.US, groupNoText,
504 header.getTransactionDescriptionMatchGroup(),
505 getMatchGroupText(header.getTransactionDescriptionMatchGroup())));
508 b.templateTransactionDescriptionSourceLabel.setOnClickListener(
509 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
510 b.templateTransactionDescriptionSource.setOnClickListener(
511 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
513 if (header.hasLiteralTransactionComment()) {
514 b.templateTransactionCommentSource.setText(
515 R.string.template_details_source_literal);
516 b.transactionComment.setText(header.getTransactionComment());
517 b.transactionCommentLayout.setVisibility(View.VISIBLE);
520 b.transactionCommentLayout.setVisibility(View.GONE);
521 b.templateTransactionCommentSource.setText(String.format(Locale.US, groupNoText,
522 header.getTransactionCommentMatchGroup(),
523 getMatchGroupText(header.getTransactionCommentMatchGroup())));
526 b.templateTransactionCommentSourceLabel.setOnClickListener(
527 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
528 b.templateTransactionCommentSource.setOnClickListener(
529 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
531 b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
533 checkPatternError(header);
535 private void checkPatternError(TemplateDetailsItem.Header item) {
536 if (item.getPatternError() != null) {
537 b.patternLayout.setError(item.getPatternError());
538 b.patternHintTitle.setVisibility(View.GONE);
539 b.patternHintText.setVisibility(View.GONE);
542 b.patternLayout.setError(null);
543 if (item.testMatch() != null) {
544 b.patternHintText.setText(item.testMatch());
545 b.patternHintTitle.setVisibility(View.VISIBLE);
546 b.patternHintText.setVisibility(View.VISIBLE);
549 b.patternLayout.setError(null);
550 b.patternHintTitle.setVisibility(View.GONE);
551 b.patternHintText.setVisibility(View.GONE);
556 private void scanTestQR(View view) {
557 QRScanCapableFragment.triggerQRScan();
561 public class AccountRow extends ViewHolder {
562 private final TemplateDetailsAccountBinding b;
563 public AccountRow(@NonNull TemplateDetailsAccountBinding binding) {
564 super(binding.getRoot());
567 TextWatcher accountNameWatcher = new TextWatcher() {
569 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
571 public void onTextChanged(CharSequence s, int start, int before, int count) {}
573 public void afterTextChanged(Editable s) {
574 TemplateDetailsItem.AccountRow accRow = getItem();
575 Logger.debug(D_TEMPLATE_UI,
576 "Storing changed account name " + s + "; accRow=" + accRow);
577 accRow.setAccountName(String.valueOf(s));
580 b.templateDetailsAccountName.addTextChangedListener(accountNameWatcher);
581 b.templateDetailsAccountName.setAdapter(new AccountAutocompleteAdapter(b.getRoot()
583 b.templateDetailsAccountName.setOnItemClickListener(
584 (parent, view, position, id) -> b.templateDetailsAccountName.setText(
585 ((TextView) view).getText()));
586 TextWatcher accountCommentWatcher = new TextWatcher() {
588 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
590 public void onTextChanged(CharSequence s, int start, int before, int count) {}
592 public void afterTextChanged(Editable s) {
593 TemplateDetailsItem.AccountRow accRow = getItem();
594 Logger.debug(D_TEMPLATE_UI,
595 "Storing changed account comment " + s + "; accRow=" + accRow);
596 accRow.setAccountComment(String.valueOf(s));
599 b.templateDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
601 b.templateDetailsAccountAmount.addTextChangedListener(new TextWatcher() {
603 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
607 public void onTextChanged(CharSequence s, int start, int before, int count) {
611 public void afterTextChanged(Editable s) {
612 TemplateDetailsItem.AccountRow accRow = getItem();
614 String str = String.valueOf(s);
615 if (Misc.emptyIsNull(str) == null) {
616 accRow.setAmount(null);
620 final float amount = Data.parseNumber(str);
621 accRow.setAmount(amount);
622 b.templateDetailsAccountAmountLayout.setError(null);
624 Logger.debug(D_TEMPLATE_UI, String.format(Locale.US,
625 "Storing changed account amount %s [%4.2f]; accRow=%s", s,
628 catch (NumberFormatException | ParseException e) {
629 b.templateDetailsAccountAmountLayout.setError("!");
634 b.templateDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
638 TemplateDetailsItem.AccountRow accRow = getItem();
639 if (!accRow.hasLiteralAmount())
641 Float amt = accRow.getAmount();
645 b.templateDetailsAccountAmount.setText(Data.formatNumber(amt));
648 b.negateAmountSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
649 getItem().setNegateAmount(isChecked);
650 b.templateDetailsNegateAmountText.setText(
651 isChecked ? R.string.template_account_change_amount_sign
652 : R.string.template_account_keep_amount_sign);
654 final View.OnClickListener negLabelClickListener = (view) -> {
655 b.negateAmountSwitch.toggle();
657 b.templateDetailsNegateAmountLabel.setOnClickListener(negLabelClickListener);
658 b.templateDetailsNegateAmountText.setOnClickListener(negLabelClickListener);
659 b.patternAccountLabel.setOnTouchListener((v, event) -> {
660 if (event.getAction() == MotionEvent.ACTION_DOWN) {
661 itemTouchHelper.startDrag(this);
667 void bind(TemplateDetailsItem item) {
668 String groupNoText = b.getRoot()
670 .getString(R.string.template_item_match_group_source);
672 TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
673 if (accRow.hasLiteralAccountName()) {
674 b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
675 b.templateDetailsAccountName.setText(accRow.getAccountName());
676 b.templateDetailsAccountNameSource.setText(
677 R.string.template_details_source_literal);
680 b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
681 b.templateDetailsAccountNameSource.setText(
682 String.format(Locale.US, groupNoText, accRow.getAccountNameMatchGroup(),
683 getMatchGroupText(accRow.getAccountNameMatchGroup())));
686 if (accRow.hasLiteralAccountComment()) {
687 b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
688 b.templateDetailsAccountComment.setText(accRow.getAccountComment());
689 b.templateDetailsAccountCommentSource.setText(
690 R.string.template_details_source_literal);
693 b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
694 b.templateDetailsAccountCommentSource.setText(
695 String.format(Locale.US, groupNoText, accRow.getAccountCommentMatchGroup(),
696 getMatchGroupText(accRow.getAccountCommentMatchGroup())));
699 if (accRow.hasLiteralAmount()) {
700 b.templateDetailsAccountAmountSource.setText(
701 R.string.template_details_source_literal);
702 b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
703 Float amt = accRow.getAmount();
704 b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
705 Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
706 b.negateAmountSwitch.setVisibility(View.GONE);
707 b.templateDetailsNegateAmountLabel.setVisibility(View.GONE);
708 b.templateDetailsNegateAmountText.setVisibility(View.GONE);
711 b.templateDetailsAccountAmountSource.setText(
712 String.format(Locale.US, groupNoText, accRow.getAmountMatchGroup(),
713 getMatchGroupText(accRow.getAmountMatchGroup())));
714 b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
715 b.negateAmountSwitch.setVisibility(View.VISIBLE);
716 b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
717 b.templateDetailsNegateAmountText.setText(
718 accRow.isNegateAmount() ? R.string.template_account_change_amount_sign
719 : R.string.template_account_keep_amount_sign);
720 b.templateDetailsNegateAmountLabel.setVisibility(View.VISIBLE);
721 b.templateDetailsNegateAmountText.setVisibility(View.VISIBLE);
724 b.templateAccountNameSourceLabel.setOnClickListener(
725 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
726 b.templateDetailsAccountNameSource.setOnClickListener(
727 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
728 b.templateAccountCommentSourceLabel.setOnClickListener(
729 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
730 b.templateDetailsAccountCommentSource.setOnClickListener(
731 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
732 b.templateAccountAmountSourceLabel.setOnClickListener(
733 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
734 b.templateDetailsAccountAmountSource.setOnClickListener(
735 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
737 private @NotNull TemplateDetailsItem.AccountRow getItem() {
738 return differ.getCurrentList()
739 .get(getAdapterPosition())
742 private void selectAccountRowDetailSource(View v, AccDetail detail) {
743 TemplateDetailsItem.AccountRow accRow = getItem();
744 final TemplateDetailsItem.Header header = getHeader();
745 Logger.debug(D_TEMPLATE_UI, "header is " + header);
746 TemplateDetailSourceSelectorFragment sel =
747 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
748 header.getTestText());
749 sel.setOnSourceSelectedListener((literal, group) -> {
753 accRow.switchToLiteralAccountName();
756 accRow.switchToLiteralAccountComment();
759 accRow.switchToLiteralAmount();
762 throw new IllegalStateException("Unexpected detail " + detail);
768 accRow.setAccountNameMatchGroup(group);
771 accRow.setAccountCommentMatchGroup(group);
774 accRow.setAmountMatchGroup(group);
777 throw new IllegalStateException("Unexpected detail " + detail);
781 notifyItemChanged(getAdapterPosition());
783 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
784 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");