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.text.Editable;
22 import android.text.TextWatcher;
23 import android.view.LayoutInflater;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.TextView;
29 import androidx.annotation.NonNull;
30 import androidx.appcompat.app.AppCompatActivity;
31 import androidx.recyclerview.widget.AsyncListDiffer;
32 import androidx.recyclerview.widget.DiffUtil;
33 import androidx.recyclerview.widget.ItemTouchHelper;
34 import androidx.recyclerview.widget.RecyclerView;
36 import net.ktnx.mobileledger.R;
37 import net.ktnx.mobileledger.databinding.TemplateDetailsAccountBinding;
38 import net.ktnx.mobileledger.databinding.TemplateDetailsHeaderBinding;
39 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
40 import net.ktnx.mobileledger.db.TemplateBase;
41 import net.ktnx.mobileledger.model.Data;
42 import net.ktnx.mobileledger.model.TemplateDetailsItem;
43 import net.ktnx.mobileledger.ui.QRScanCapableFragment;
44 import net.ktnx.mobileledger.ui.TemplateDetailSourceSelectorFragment;
45 import net.ktnx.mobileledger.utils.Logger;
46 import net.ktnx.mobileledger.utils.Misc;
48 import org.jetbrains.annotations.NotNull;
50 import java.text.ParseException;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Locale;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
57 class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter.ViewHolder> {
58 private static final String D_TEMPLATE_UI = "template-ui";
59 private final AsyncListDiffer<TemplateDetailsItem> differ;
60 private final TemplateDetailsViewModel mModel;
61 private final ItemTouchHelper itemTouchHelper;
62 public TemplateDetailsAdapter(TemplateDetailsViewModel model) {
65 setHasStableIds(true);
66 differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TemplateDetailsItem>() {
68 public boolean areItemsTheSame(@NonNull TemplateDetailsItem oldItem,
69 @NonNull TemplateDetailsItem newItem) {
70 if (oldItem.getType() != newItem.getType())
73 .equals(TemplateDetailsItem.Type.HEADER))
74 return true; // only one header item, ever
75 // the rest is comparing two account row items
76 return oldItem.asAccountRowItem()
77 .getId() == newItem.asAccountRowItem()
81 public boolean areContentsTheSame(@NonNull TemplateDetailsItem oldItem,
82 @NonNull TemplateDetailsItem newItem) {
84 .equals(TemplateDetailsItem.Type.HEADER))
86 TemplateDetailsItem.Header oldHeader = oldItem.asHeaderItem();
87 TemplateDetailsItem.Header newHeader = newItem.asHeaderItem();
89 return oldHeader.equalContents(newHeader);
92 TemplateDetailsItem.AccountRow oldAcc = oldItem.asAccountRowItem();
93 TemplateDetailsItem.AccountRow newAcc = newItem.asAccountRowItem();
95 return oldAcc.equalContents(newAcc);
99 itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
101 public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
105 public boolean isLongPressDragEnabled() {
109 public RecyclerView.ViewHolder chooseDropTarget(
110 @NonNull RecyclerView.ViewHolder selected,
111 @NonNull List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
112 RecyclerView.ViewHolder best = null;
113 int bestDistance = 0;
114 for (RecyclerView.ViewHolder v : dropTargets) {
118 final int viewTop = v.itemView.getTop();
119 int distance = Math.abs(viewTop - curY);
122 bestDistance = distance;
125 if (distance < bestDistance) {
126 bestDistance = distance;
132 Logger.debug("dnd", "Best target is " + best);
136 public boolean canDropOver(@NonNull RecyclerView recyclerView,
137 @NonNull RecyclerView.ViewHolder current,
138 @NonNull RecyclerView.ViewHolder target) {
139 final int adapterPosition = target.getAdapterPosition();
141 // first item is immovable
142 if (adapterPosition == 0)
145 return super.canDropOver(recyclerView, current, target);
148 public int getMovementFlags(@NonNull RecyclerView recyclerView,
149 @NonNull RecyclerView.ViewHolder viewHolder) {
151 // the top item (transaction params) is always there
152 final int adapterPosition = viewHolder.getAdapterPosition();
153 if (adapterPosition > 0)
154 flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
155 ItemTouchHelper.UP | ItemTouchHelper.DOWN) |
156 makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE,
157 ItemTouchHelper.START | ItemTouchHelper.END);
162 public boolean onMove(@NonNull RecyclerView recyclerView,
163 @NonNull RecyclerView.ViewHolder viewHolder,
164 @NonNull RecyclerView.ViewHolder target) {
166 final int fromPosition = viewHolder.getAdapterPosition();
167 final int toPosition = target.getAdapterPosition();
168 mModel.moveItem(fromPosition, toPosition);
173 public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
174 int pos = viewHolder.getAdapterPosition();
175 mModel.removeItem(pos);
180 public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
181 super.onAttachedToRecyclerView(recyclerView);
183 itemTouchHelper.attachToRecyclerView(recyclerView);
186 public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
187 super.onDetachedFromRecyclerView(recyclerView);
189 itemTouchHelper.attachToRecyclerView(null);
192 public long getItemId(int position) {
193 // header item is always first and IDs id may duplicate some of the account IDs
196 TemplateDetailsItem.AccountRow accRow = differ.getCurrentList()
199 return accRow.getId();
202 public int getItemViewType(int position) {
204 return differ.getCurrentList()
211 public TemplateDetailsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
213 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
215 case TemplateDetailsItem.TYPE.header:
216 return new Header(TemplateDetailsHeaderBinding.inflate(inflater, parent, false));
217 case TemplateDetailsItem.TYPE.accountItem:
218 return new AccountRow(
219 TemplateDetailsAccountBinding.inflate(inflater, parent, false));
221 throw new IllegalStateException("Unsupported view type " + viewType);
225 public void onBindViewHolder(@NonNull TemplateDetailsAdapter.ViewHolder holder, int position) {
226 TemplateDetailsItem item = differ.getCurrentList()
231 public int getItemCount() {
232 return differ.getCurrentList()
235 public void setTemplateItems(List<TemplateBase> items) {
236 ArrayList<TemplateDetailsItem> list = new ArrayList<>();
237 for (TemplateBase p : items) {
238 TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p);
243 public void setItems(List<TemplateDetailsItem> items) {
244 differ.submitList(items);
246 public String getMatchGroupText(int groupNumber) {
247 TemplateDetailsItem.Header header = getHeader();
248 Pattern p = header.getCompiledPattern();
252 final String testText = Misc.nullIsEmpty(header.getTestText());
253 Matcher m = p.matcher(testText);
254 if (m.matches() && m.groupCount() >= groupNumber)
255 return m.group(groupNumber);
259 protected TemplateDetailsItem.Header getHeader() {
260 return differ.getCurrentList()
265 private enum HeaderDetail {DESCRIPTION, COMMENT, DATE_YEAR, DATE_MONTH, DATE_DAY}
267 private enum AccDetail {ACCOUNT, COMMENT, AMOUNT}
269 public abstract static class ViewHolder extends RecyclerView.ViewHolder {
270 ViewHolder(@NonNull View itemView) {
273 abstract void bind(TemplateDetailsItem item);
276 public class Header extends ViewHolder {
277 private final TemplateDetailsHeaderBinding b;
278 public Header(@NonNull TemplateDetailsHeaderBinding binding) {
279 super(binding.getRoot());
282 TextWatcher templateNameWatcher = new TextWatcher() {
284 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
286 public void onTextChanged(CharSequence s, int start, int before, int count) {}
288 public void afterTextChanged(Editable s) {
289 final TemplateDetailsItem.Header header = getItem();
290 Logger.debug(D_TEMPLATE_UI,
291 "Storing changed template name " + s + "; header=" + header);
292 header.setName(String.valueOf(s));
295 b.templateName.addTextChangedListener(templateNameWatcher);
297 TextWatcher patternWatcher = 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 final TemplateDetailsItem.Header header = getItem();
305 Logger.debug(D_TEMPLATE_UI,
306 "Storing changed pattern " + s + "; header=" + header);
307 header.setPattern(String.valueOf(s));
309 checkPatternError(header);
312 b.pattern.addTextChangedListener(patternWatcher);
314 TextWatcher testTextWatcher = new TextWatcher() {
316 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
318 public void onTextChanged(CharSequence s, int start, int before, int count) {}
320 public void afterTextChanged(Editable s) {
321 final TemplateDetailsItem.Header header = getItem();
322 Logger.debug(D_TEMPLATE_UI,
323 "Storing changed test text " + s + "; header=" + header);
324 header.setTestText(String.valueOf(s));
326 checkPatternError(header);
329 b.testText.addTextChangedListener(testTextWatcher);
331 TextWatcher transactionDescriptionWatcher = new TextWatcher() {
333 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
336 public void onTextChanged(CharSequence s, int start, int before, int count) {
340 public void afterTextChanged(Editable s) {
341 final TemplateDetailsItem.Header header = getItem();
342 Logger.debug(D_TEMPLATE_UI,
343 "Storing changed transaction description " + s + "; header=" + header);
344 header.setTransactionDescription(String.valueOf(s));
347 b.transactionDescription.addTextChangedListener(transactionDescriptionWatcher);
348 TextWatcher transactionCommentWatcher = new TextWatcher() {
350 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
354 public void onTextChanged(CharSequence s, int start, int before, int count) {
358 public void afterTextChanged(Editable s) {
359 final TemplateDetailsItem.Header header = getItem();
360 Logger.debug(D_TEMPLATE_UI,
361 "Storing changed transaction description " + s + "; header=" + header);
362 header.setTransactionComment(String.valueOf(s));
365 b.transactionComment.addTextChangedListener(transactionCommentWatcher);
368 private TemplateDetailsItem.Header getItem() {
369 int pos = getAdapterPosition();
370 return differ.getCurrentList()
374 private void selectHeaderDetailSource(View v, HeaderDetail detail) {
375 TemplateDetailsItem.Header header = getItem();
376 Logger.debug(D_TEMPLATE_UI, "header is " + header);
377 TemplateDetailSourceSelectorFragment sel =
378 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
379 header.getTestText());
380 sel.setOnSourceSelectedListener((literal, group) -> {
384 header.switchToLiteralTransactionDescription();
387 header.switchToLiteralTransactionComment();
390 header.switchToLiteralDateYear();
393 header.switchToLiteralDateMonth();
396 header.switchToLiteralDateDay();
399 throw new IllegalStateException("Unexpected detail " + detail);
405 header.setTransactionDescriptionMatchGroup(group);
408 header.setTransactionCommentMatchGroup(group);
411 header.setDateYearMatchGroup(group);
414 header.setDateMonthMatchGroup(group);
417 header.setDateDayMatchGroup(group);
420 throw new IllegalStateException("Unexpected detail " + detail);
424 notifyItemChanged(getAdapterPosition());
426 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
427 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");
430 void bind(TemplateDetailsItem item) {
431 TemplateDetailsItem.Header header = item.asHeaderItem();
432 Logger.debug(D_TEMPLATE_UI, "Binding to header " + header);
434 String groupNoText = b.getRoot()
436 .getString(R.string.template_item_match_group_source);
438 b.templateName.setText(header.getName());
439 b.pattern.setText(header.getPattern());
440 b.testText.setText(header.getTestText());
442 if (header.hasLiteralDateYear()) {
443 b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
444 final Integer dateYear = header.getDateYear();
445 b.templateDetailsDateYear.setText(
446 (dateYear == null) ? null : String.valueOf(dateYear));
447 b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
450 b.templateDetailsDateYearLayout.setVisibility(View.GONE);
451 b.templateDetailsYearSource.setText(
452 String.format(Locale.US, groupNoText, header.getDateYearMatchGroup(),
453 getMatchGroupText(header.getDateYearMatchGroup())));
455 b.templateDetailsYearSourceLabel.setOnClickListener(
456 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
457 b.templateDetailsYearSource.setOnClickListener(
458 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
460 if (header.hasLiteralDateMonth()) {
461 b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
462 final Integer dateMonth = header.getDateMonth();
463 b.templateDetailsDateMonth.setText(
464 (dateMonth == null) ? null : String.valueOf(dateMonth));
465 b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
468 b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
469 b.templateDetailsMonthSource.setText(
470 String.format(Locale.US, groupNoText, header.getDateMonthMatchGroup(),
471 getMatchGroupText(header.getDateMonthMatchGroup())));
473 b.templateDetailsMonthSourceLabel.setOnClickListener(
474 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
475 b.templateDetailsMonthSource.setOnClickListener(
476 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
478 if (header.hasLiteralDateDay()) {
479 b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
480 final Integer dateDay = header.getDateDay();
481 b.templateDetailsDateDay.setText(
482 (dateDay == null) ? null : String.valueOf(dateDay));
483 b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
486 b.templateDetailsDateDayLayout.setVisibility(View.GONE);
487 b.templateDetailsDaySource.setText(
488 String.format(Locale.US, groupNoText, header.getDateDayMatchGroup(),
489 getMatchGroupText(header.getDateDayMatchGroup())));
491 b.templateDetailsDaySourceLabel.setOnClickListener(
492 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
493 b.templateDetailsDaySource.setOnClickListener(
494 v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
496 if (header.hasLiteralTransactionDescription()) {
497 b.templateTransactionDescriptionSource.setText(
498 R.string.template_details_source_literal);
499 b.transactionDescription.setText(header.getTransactionDescription());
500 b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
503 b.transactionDescriptionLayout.setVisibility(View.GONE);
504 b.templateTransactionDescriptionSource.setText(String.format(Locale.US, groupNoText,
505 header.getTransactionDescriptionMatchGroup(),
506 getMatchGroupText(header.getTransactionDescriptionMatchGroup())));
509 b.templateTransactionDescriptionSourceLabel.setOnClickListener(
510 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
511 b.templateTransactionDescriptionSource.setOnClickListener(
512 v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
514 if (header.hasLiteralTransactionComment()) {
515 b.templateTransactionCommentSource.setText(
516 R.string.template_details_source_literal);
517 b.transactionComment.setText(header.getTransactionComment());
518 b.transactionCommentLayout.setVisibility(View.VISIBLE);
521 b.transactionCommentLayout.setVisibility(View.GONE);
522 b.templateTransactionCommentSource.setText(String.format(Locale.US, groupNoText,
523 header.getTransactionCommentMatchGroup(),
524 getMatchGroupText(header.getTransactionCommentMatchGroup())));
527 b.templateTransactionCommentSourceLabel.setOnClickListener(
528 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
529 b.templateTransactionCommentSource.setOnClickListener(
530 v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
532 b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
534 checkPatternError(header);
536 private void checkPatternError(TemplateDetailsItem.Header item) {
537 if (item.getPatternError() != null) {
538 b.patternLayout.setError(item.getPatternError());
539 b.patternHintTitle.setVisibility(View.GONE);
540 b.patternHintText.setVisibility(View.GONE);
543 b.patternLayout.setError(null);
544 if (item.testMatch() != null) {
545 b.patternHintText.setText(item.testMatch());
546 b.patternHintTitle.setVisibility(View.VISIBLE);
547 b.patternHintText.setVisibility(View.VISIBLE);
550 b.patternLayout.setError(null);
551 b.patternHintTitle.setVisibility(View.GONE);
552 b.patternHintText.setVisibility(View.GONE);
557 private void scanTestQR(View view) {
558 QRScanCapableFragment.triggerQRScan();
562 public class AccountRow extends ViewHolder {
563 private final TemplateDetailsAccountBinding b;
564 public AccountRow(@NonNull TemplateDetailsAccountBinding binding) {
565 super(binding.getRoot());
568 TextWatcher accountNameWatcher = new TextWatcher() {
570 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
572 public void onTextChanged(CharSequence s, int start, int before, int count) {}
574 public void afterTextChanged(Editable s) {
575 TemplateDetailsItem.AccountRow accRow = getItem();
576 Logger.debug(D_TEMPLATE_UI,
577 "Storing changed account name " + s + "; accRow=" + accRow);
578 accRow.setAccountName(String.valueOf(s));
581 b.templateDetailsAccountName.addTextChangedListener(accountNameWatcher);
582 b.templateDetailsAccountName.setAdapter(new AccountAutocompleteAdapter(b.getRoot()
584 b.templateDetailsAccountName.setOnItemClickListener(
585 (parent, view, position, id) -> b.templateDetailsAccountName.setText(
586 ((TextView) view).getText()));
587 TextWatcher accountCommentWatcher = new TextWatcher() {
589 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
591 public void onTextChanged(CharSequence s, int start, int before, int count) {}
593 public void afterTextChanged(Editable s) {
594 TemplateDetailsItem.AccountRow accRow = getItem();
595 Logger.debug(D_TEMPLATE_UI,
596 "Storing changed account comment " + s + "; accRow=" + accRow);
597 accRow.setAccountComment(String.valueOf(s));
600 b.templateDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
602 b.templateDetailsAccountAmount.addTextChangedListener(new TextWatcher() {
604 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
608 public void onTextChanged(CharSequence s, int start, int before, int count) {
612 public void afterTextChanged(Editable s) {
613 TemplateDetailsItem.AccountRow accRow = getItem();
615 String str = String.valueOf(s);
616 if (Misc.emptyIsNull(str) == null) {
617 accRow.setAmount(null);
621 final float amount = Data.parseNumber(str);
622 accRow.setAmount(amount);
623 b.templateDetailsAccountAmountLayout.setError(null);
625 Logger.debug(D_TEMPLATE_UI, String.format(Locale.US,
626 "Storing changed account amount %s [%4.2f]; accRow=%s", s,
629 catch (NumberFormatException | ParseException e) {
630 b.templateDetailsAccountAmountLayout.setError("!");
635 b.templateDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
639 TemplateDetailsItem.AccountRow accRow = getItem();
640 if (!accRow.hasLiteralAmount())
642 Float amt = accRow.getAmount();
646 b.templateDetailsAccountAmount.setText(Data.formatNumber(amt));
649 b.negateAmountSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
650 getItem().setNegateAmount(isChecked);
651 b.templateDetailsNegateAmountText.setText(
652 isChecked ? R.string.template_account_change_amount_sign
653 : R.string.template_account_keep_amount_sign);
655 final View.OnClickListener negLabelClickListener =
656 (view) -> b.negateAmountSwitch.toggle();
657 b.templateDetailsNegateAmountLabel.setOnClickListener(negLabelClickListener);
658 b.templateDetailsNegateAmountText.setOnClickListener(negLabelClickListener);
659 manageAccountLabelDrag();
661 @SuppressLint("ClickableViewAccessibility")
662 public void manageAccountLabelDrag() {
663 b.patternAccountLabel.setOnTouchListener((v, event) -> {
664 if (event.getAction() == MotionEvent.ACTION_DOWN) {
665 itemTouchHelper.startDrag(this);
671 void bind(TemplateDetailsItem item) {
672 String groupNoText = b.getRoot()
674 .getString(R.string.template_item_match_group_source);
676 TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
677 if (accRow.hasLiteralAccountName()) {
678 b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
679 b.templateDetailsAccountName.setText(accRow.getAccountName());
680 b.templateDetailsAccountNameSource.setText(
681 R.string.template_details_source_literal);
684 b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
685 b.templateDetailsAccountNameSource.setText(
686 String.format(Locale.US, groupNoText, accRow.getAccountNameMatchGroup(),
687 getMatchGroupText(accRow.getAccountNameMatchGroup())));
690 if (accRow.hasLiteralAccountComment()) {
691 b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
692 b.templateDetailsAccountComment.setText(accRow.getAccountComment());
693 b.templateDetailsAccountCommentSource.setText(
694 R.string.template_details_source_literal);
697 b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
698 b.templateDetailsAccountCommentSource.setText(
699 String.format(Locale.US, groupNoText, accRow.getAccountCommentMatchGroup(),
700 getMatchGroupText(accRow.getAccountCommentMatchGroup())));
703 if (accRow.hasLiteralAmount()) {
704 b.templateDetailsAccountAmountSource.setText(
705 R.string.template_details_source_literal);
706 b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
707 Float amt = accRow.getAmount();
708 b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
709 Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
710 b.negateAmountSwitch.setVisibility(View.GONE);
711 b.templateDetailsNegateAmountLabel.setVisibility(View.GONE);
712 b.templateDetailsNegateAmountText.setVisibility(View.GONE);
715 b.templateDetailsAccountAmountSource.setText(
716 String.format(Locale.US, groupNoText, accRow.getAmountMatchGroup(),
717 getMatchGroupText(accRow.getAmountMatchGroup())));
718 b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
719 b.negateAmountSwitch.setVisibility(View.VISIBLE);
720 b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
721 b.templateDetailsNegateAmountText.setText(
722 accRow.isNegateAmount() ? R.string.template_account_change_amount_sign
723 : R.string.template_account_keep_amount_sign);
724 b.templateDetailsNegateAmountLabel.setVisibility(View.VISIBLE);
725 b.templateDetailsNegateAmountText.setVisibility(View.VISIBLE);
728 b.templateAccountNameSourceLabel.setOnClickListener(
729 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
730 b.templateDetailsAccountNameSource.setOnClickListener(
731 v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
732 b.templateAccountCommentSourceLabel.setOnClickListener(
733 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
734 b.templateDetailsAccountCommentSource.setOnClickListener(
735 v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
736 b.templateAccountAmountSourceLabel.setOnClickListener(
737 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
738 b.templateDetailsAccountAmountSource.setOnClickListener(
739 v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
741 private @NotNull TemplateDetailsItem.AccountRow getItem() {
742 return differ.getCurrentList()
743 .get(getAdapterPosition())
746 private void selectAccountRowDetailSource(View v, AccDetail detail) {
747 TemplateDetailsItem.AccountRow accRow = getItem();
748 final TemplateDetailsItem.Header header = getHeader();
749 Logger.debug(D_TEMPLATE_UI, "header is " + header);
750 TemplateDetailSourceSelectorFragment sel =
751 TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
752 header.getTestText());
753 sel.setOnSourceSelectedListener((literal, group) -> {
757 accRow.switchToLiteralAccountName();
760 accRow.switchToLiteralAccountComment();
763 accRow.switchToLiteralAmount();
766 throw new IllegalStateException("Unexpected detail " + detail);
772 accRow.setAccountNameMatchGroup(group);
775 accRow.setAccountCommentMatchGroup(group);
778 accRow.setAmountMatchGroup(group);
781 throw new IllegalStateException("Unexpected detail " + detail);
785 notifyItemChanged(getAdapterPosition());
787 final AppCompatActivity activity = (AppCompatActivity) v.getContext();
788 sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");