X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fnet%2Fktnx%2Fmobileledger%2Fui%2Ftemplates%2FTemplateDetailsAdapter.java;h=9152fdfd691a858f4bdc6cb63123a82f70d8c736;hp=e472049b1e5762f0a5f1ab75968d16b5b282842e;hb=5fbe0ff31da3cd8585928fafeb77b7127bef780c;hpb=b953cc391328af937917ed94de6aeafb83cc715c diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java index e472049b..9152fdfd 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java @@ -17,9 +17,12 @@ 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; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -28,13 +31,14 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.AsyncListDiffer; 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; @@ -45,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; @@ -54,8 +57,11 @@ import java.util.regex.Pattern; class TemplateDetailsAdapter extends RecyclerView.Adapter { private static final String D_TEMPLATE_UI = "template-ui"; private final AsyncListDiffer differ; - public TemplateDetailsAdapter() { + private final TemplateDetailsViewModel mModel; + private final ItemTouchHelper itemTouchHelper; + public TemplateDetailsAdapter(TemplateDetailsViewModel model) { super(); + mModel = model; setHasStableIds(true); differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback() { @Override @@ -90,12 +96,113 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter dropTargets, int curX, int curY) { + RecyclerView.ViewHolder best = null; + int bestDistance = 0; + for (RecyclerView.ViewHolder v : dropTargets) { + if (v == selected) + continue; + + final int viewTop = v.itemView.getTop(); + int distance = Math.abs(viewTop - curY); + if (best == null) { + best = v; + bestDistance = distance; + } + else { + if (distance < bestDistance) { + bestDistance = distance; + best = v; + } + } + } + + Logger.debug("dnd", "Best target is " + best); + return best; + } + @Override + public boolean canDropOver(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder current, + @NonNull RecyclerView.ViewHolder target) { + final int adapterPosition = target.getAdapterPosition(); + + // first item is immovable + if (adapterPosition == 0) + return false; + + return super.canDropOver(recyclerView, current, target); + } + @Override + public int getMovementFlags(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder) { + int flags = 0; + // the top item (transaction params) is always there + final int adapterPosition = viewHolder.getAdapterPosition(); + if (adapterPosition > 0) + flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, + ItemTouchHelper.UP | ItemTouchHelper.DOWN) | + makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, + ItemTouchHelper.START | ItemTouchHelper.END); + + return flags; + } + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, + @NonNull RecyclerView.ViewHolder target) { + + 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; + } + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + int pos = viewHolder.getAdapterPosition(); + mModel.removeItem(pos); + } + }); + } + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + + itemTouchHelper.attachToRecyclerView(recyclerView); + } + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + super.onDetachedFromRecyclerView(recyclerView); + + itemTouchHelper.attachToRecyclerView(null); } @Override 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(); @@ -135,15 +242,16 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter items) { - ArrayList list = new ArrayList<>(); - for (TemplateBase p : items) { - TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p); - list.add(item); - } - setItems(list); - } public void setItems(List 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) { @@ -176,8 +284,22 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter 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, "Group %d (%s)", 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, "Group %d (%s)", 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, "Group %d (%s)", header.getDateDayMatchGroup(), - getMatchGroupText(header.getDateDayMatchGroup()))); - } - b.templateDetailsDaySourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); - b.templateDetailsDaySource.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); + } + 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()))); - 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, "Group %d (%s)", - header.getTransactionDescriptionMatchGroup(), getMatchGroupText(header.getTransactionDescriptionMatchGroup()))); + } + b.templateTransactionCommentSourceLabel.setOnClickListener( + v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT)); + b.templateTransactionCommentSource.setOnClickListener( + v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT)); - } - b.templateTransactionDescriptionSourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); - b.templateTransactionDescriptionSource.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); + b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR); - 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, "Group %d (%s)", - header.getTransactionCommentMatchGroup(), - getMatchGroupText(header.getTransactionCommentMatchGroup()))); + b.templateIsFallbackSwitch.setChecked(header.isFallback()); + b.templateIsFallbackText.setText( + header.isFallback() ? R.string.template_is_fallback_yes + : R.string.template_is_fallback_no); + checkPatternError(header); + } + finally { + enableUpdatePropagation(); } - b.templateTransactionCommentSourceLabel.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT)); - b.templateTransactionCommentSource.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT)); - - b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR); - - checkPatternError(header); } private void checkPatternError(TemplateDetailsItem.Header item) { if (item.getPatternError() != null) { @@ -450,7 +621,7 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter { @@ -537,69 +721,112 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter getItem().setNegateAmount(isChecked)); + b.negateAmountSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (updatePropagationDisabled) + return; + + getItem().setNegateAmount(isChecked); + b.templateDetailsNegateAmountText.setText( + isChecked ? R.string.template_account_change_amount_sign + : R.string.template_account_keep_amount_sign); + }); + final View.OnClickListener negLabelClickListener = + (view) -> b.negateAmountSwitch.toggle(); + b.templateDetailsNegateAmountLabel.setOnClickListener(negLabelClickListener); + b.templateDetailsNegateAmountText.setOnClickListener(negLabelClickListener); + manageAccountLabelDrag(); + } + @SuppressLint("ClickableViewAccessibility") + public void manageAccountLabelDrag() { + b.patternAccountLabel.setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + itemTouchHelper.startDrag(this); + } + return false; + }); } @Override void bind(TemplateDetailsItem item) { - 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, "Group %d (%s)", 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, "Group %d (%s)", - 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); + 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, "Group %d (%s)", accRow.getAmountMatchGroup(), - getMatchGroupText(accRow.getAmountMatchGroup()))); - b.templateDetailsAccountAmountLayout.setVisibility(View.GONE); - b.negateAmountSwitch.setVisibility(View.VISIBLE); - b.negateAmountSwitch.setChecked(accRow.isNegateAmount()); + 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()