]> git.ktnx.net Git - mobile-ledger.git/commitdiff
rework transaction list with proper view holders and no background load
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Wed, 21 Apr 2021 19:22:20 +0000 (22:22 +0300)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Wed, 21 Apr 2021 19:22:20 +0000 (22:22 +0300)
all the data is already available in the transaction list loaded from
the database (asynchronously)

also, fix a glitch where a visible item's matching account row is not
coloured when the list is filtered by account name (because the
underlying item is the same)

12 files changed:
app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java
app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java
app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListLastUpdateRowHolder.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionLoaderStep.java [deleted file]
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolderBase.java [new file with mode: 0644]
app/src/main/res/layout/transaction_delimiter.xml [new file with mode: 0644]
app/src/main/res/layout/transaction_list_row.xml

index eb8376f0fc91a70176e5ec7e3f30fbc5fdcbfd4d..46b8426abbae7aaf1b16b91fd975ed87af52640b 100644 (file)
@@ -27,12 +27,16 @@ import java.util.ArrayList;
 public class TransactionAccumulator {
     private final ArrayList<TransactionListItem> list = new ArrayList<>();
     private final MainModel model;
+    private final String boldAccountName;
     private SimpleDate earliestDate, latestDate;
     private SimpleDate lastDate;
     private boolean done;
     public TransactionAccumulator(MainModel model) {
         this.model = model;
 
+        boldAccountName = model.getAccountFilter()
+                               .getValue();
+
         list.add(new TransactionListItem());    // head item
     }
     public void put(LedgerTransaction transaction) {
@@ -54,7 +58,7 @@ public class TransactionAccumulator {
             list.add(new TransactionListItem(date, showMonth));
         }
 
-        list.add(new TransactionListItem(transaction));
+        list.add(new TransactionListItem(transaction, boldAccountName));
 
         lastDate = date;
     }
index 1e62ff1320cdded3cae4e50ab2c627a68ad51f61..550a84c5ed25d020ea0d488db89d3e634dd81582 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2020 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
  * This file is part of MoLe.
  * MoLe is free software: you can distribute it and/or modify it
  * under the term of the GNU General Public License as published by
@@ -18,6 +18,7 @@
 package net.ktnx.mobileledger.model;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.utils.SimpleDate;
@@ -29,14 +30,17 @@ public class TransactionListItem {
     private SimpleDate date;
     private boolean monthShown;
     private LedgerTransaction transaction;
+    private String boldAccountName;
     public TransactionListItem(@NotNull SimpleDate date, boolean monthShown) {
         this.type = Type.DELIMITER;
         this.date = date;
         this.monthShown = monthShown;
     }
-    public TransactionListItem(@NotNull LedgerTransaction transaction) {
+    public TransactionListItem(@NotNull LedgerTransaction transaction,
+                               @Nullable String boldAccountName) {
         this.type = Type.TRANSACTION;
         this.transaction = transaction;
+        this.boldAccountName = boldAccountName;
     }
     public TransactionListItem() {
         this.type = Type.HEADER;
@@ -64,5 +68,21 @@ public class TransactionListItem {
                     String.format("Item type is not %s, but %s", Type.TRANSACTION, type));
         return transaction;
     }
-    public enum Type {TRANSACTION, DELIMITER, HEADER}
+    public @Nullable
+    String getBoldAccountName() {
+        return boldAccountName;
+    }
+    public enum Type {
+        TRANSACTION, DELIMITER, HEADER;
+        public static Type valueOf(int i) {
+            if (i == TRANSACTION.ordinal())
+                return TRANSACTION;
+            else if (i == DELIMITER.ordinal())
+                return DELIMITER;
+            else if (i == HEADER.ordinal())
+                return HEADER;
+            else
+                throw new IllegalStateException("Unexpected value: " + i);
+        }
+    }
 }
index 06f94ee39269babbcf20483a62b37951bdd4487b..039e24df6c45ed9495b09056efad4661debee203 100644 (file)
@@ -18,7 +18,6 @@
 package net.ktnx.mobileledger.ui;
 
 import android.os.AsyncTask;
-import android.text.TextUtils;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
@@ -83,21 +82,6 @@ public class MainModel extends ViewModel {
     public void setLastTransactionDate(SimpleDate latestDate) {
         this.lastTransactionDate = latestDate;
     }
-    private void applyTransactionFilter(List<LedgerTransaction> list) {
-        final String accFilter = accountFilter.getValue();
-        ArrayList<TransactionListItem> newList = new ArrayList<>();
-
-        TransactionAccumulator accumulator = new TransactionAccumulator(this);
-        if (TextUtils.isEmpty(accFilter))
-            for (LedgerTransaction tr : list)
-                newList.add(new TransactionListItem(tr));
-        else
-            for (LedgerTransaction tr : list)
-                if (tr.hasAccountNamedLike(accFilter))
-                    newList.add(new TransactionListItem(tr));
-
-        displayedTransactions.postValue(newList);
-    }
     public synchronized void scheduleTransactionListRetrieval() {
         if (retrieveTransactionsTask != null) {
             Logger.debug("db", "Ignoring request for transaction retrieval - already active");
index 67a4922b175be66ffac07f41fd0e8b81dfd651a2..599a9f8c8024e39e020a63485be749e1f5e0f27d 100644 (file)
 
 package net.ktnx.mobileledger.ui.transaction_list;
 
-import android.app.Activity;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Typeface;
-import android.os.AsyncTask;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.style.StyleSpan;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.AsyncListDiffer;
 import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.RecyclerView;
 
-import net.ktnx.mobileledger.App;
-import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.model.Data;
+import net.ktnx.mobileledger.databinding.LastUpdateLayoutBinding;
+import net.ktnx.mobileledger.databinding.TransactionDelimiterBinding;
+import net.ktnx.mobileledger.databinding.TransactionListRowBinding;
 import net.ktnx.mobileledger.model.LedgerTransaction;
-import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.model.TransactionListItem;
-import net.ktnx.mobileledger.ui.MainModel;
-import net.ktnx.mobileledger.utils.Colors;
-import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
-import net.ktnx.mobileledger.utils.SimpleDate;
 
-import java.text.DateFormat;
-import java.util.GregorianCalendar;
 import java.util.List;
 import java.util.Locale;
-import java.util.TimeZone;
 
-public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowHolder> {
-    private final MainModel model;
+public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowHolderBase> {
     private final AsyncListDiffer<TransactionListItem> listDiffer;
-    public TransactionListAdapter(MainModel model) {
+    public TransactionListAdapter() {
         super();
-        this.model = model;
 
         listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TransactionListItem>() {
             @Override
@@ -94,7 +72,9 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
                         return true;
                     case TRANSACTION:
                         return oldItem.getTransaction()
-                                      .equals(newItem.getTransaction());
+                                      .equals(newItem.getTransaction()) &&
+                               Misc.equalStrings(oldItem.getBoldAccountName(),
+                                       newItem.getBoldAccountName());
                     case HEADER:
                         // headers don't differ in their contents. they observe the last update
                         // date and react to its changes
@@ -108,7 +88,14 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
             }
         });
     }
-    public void onBindViewHolder(@NonNull TransactionRowHolder holder, int position) {
+    @Override
+    public int getItemViewType(int position) {
+        return listDiffer.getCurrentList()
+                         .get(position)
+                         .getType()
+                         .ordinal();
+    }
+    public void onBindViewHolder(@NonNull TransactionRowHolderBase holder, int position) {
         TransactionListItem item = listDiffer.getCurrentList()
                                              .get(position);
 
@@ -120,53 +107,21 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
             return;
 
         final TransactionListItem.Type newType = item.getType();
-        holder.setType(newType);
 
         switch (newType) {
             case TRANSACTION:
                 LedgerTransaction tr = item.getTransaction();
+                holder.asTransaction()
+                      .bind(tr, item.getBoldAccountName());
 
-                //        debug("transactions", String.format("Filling position %d with %d
-                //        accounts", position,
-                //                tr.getAccounts().size()));
-
-                TransactionLoader loader = new TransactionLoader();
-                loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
-                        new TransactionLoaderParams(tr, holder, position, model.getAccountFilter()
-                                                                               .getValue()));
-
-                // WORKAROUND what seems to be a bug in CardHolder somewhere
-                // when a view that was previously holding a delimiter is re-purposed
-                // occasionally it stays too short (not high enough)
-                holder.vTransaction.measure(
-                        View.MeasureSpec.makeMeasureSpec(holder.itemView.getWidth(),
-                                View.MeasureSpec.EXACTLY),
-                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                 break;
             case DELIMITER:
-                SimpleDate date = item.getDate();
-                holder.tvDelimiterDate.setText(DateFormat.getDateInstance()
-                                                         .format(date.toDate()));
-                if (item.isMonthShown()) {
-                    GregorianCalendar cal = new GregorianCalendar(TimeZone.getDefault());
-                    cal.setTime(date.toDate());
-                    App.prepareMonthNames();
-                    holder.tvDelimiterMonth.setText(
-                            Globals.monthNames[cal.get(GregorianCalendar.MONTH)]);
-                    holder.tvDelimiterMonth.setVisibility(View.VISIBLE);
-                    //                holder.vDelimiterLine.setBackgroundResource(R.drawable
-                    //                .dashed_border_8dp);
-                    holder.vDelimiterThick.setVisibility(View.VISIBLE);
-                }
-                else {
-                    holder.tvDelimiterMonth.setVisibility(View.GONE);
-                    //                holder.vDelimiterLine.setBackgroundResource(R.drawable
-                    //                .dashed_border_1dp);
-                    holder.vDelimiterThick.setVisibility(View.GONE);
-                }
+                holder.asDelimiter()
+                      .bind(item);
                 break;
             case HEADER:
-                holder.setLastUpdateText(Data.lastTransactionsUpdateText.getValue());
+                holder.asHeader()
+                      .bind();
 
                 break;
             default:
@@ -175,11 +130,23 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
     }
     @NonNull
     @Override
-    public TransactionRowHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+    public TransactionRowHolderBase onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 //        debug("perf", "onCreateViewHolder called");
-        View row = LayoutInflater.from(parent.getContext())
-                                 .inflate(R.layout.transaction_list_row, parent, false);
-        return new TransactionRowHolder(row);
+        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+
+        switch (TransactionListItem.Type.valueOf(viewType)) {
+            case TRANSACTION:
+                return new TransactionRowHolder(
+                        TransactionListRowBinding.inflate(inflater, parent, false));
+            case DELIMITER:
+                return new TransactionListDelimiterRowHolder(
+                        TransactionDelimiterBinding.inflate(inflater, parent, false));
+            case HEADER:
+                return new TransactionListLastUpdateRowHolder(
+                        LastUpdateLayoutBinding.inflate(inflater, parent, false));
+            default:
+                throw new IllegalStateException("Unexpected value: " + viewType);
+        }
     }
 
     @Override
@@ -192,132 +159,4 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
                 String.format(Locale.US, "Got new transaction list (%d items)", newList.size()));
         listDiffer.submitList(newList);
     }
-    enum LoaderStep {HEAD, ACCOUNTS, DONE}
-
-    private static class TransactionLoader
-            extends AsyncTask<TransactionLoaderParams, TransactionLoaderStep, Void> {
-        @Override
-        protected Void doInBackground(TransactionLoaderParams... p) {
-            LedgerTransaction tr = p[0].transaction;
-
-            SQLiteDatabase db = App.getDatabase();
-            tr.loadData(db);
-
-            publishProgress(new TransactionLoaderStep(p[0].holder, p[0].position, tr));
-
-            int rowIndex = 0;
-            // FIXME ConcurrentModificationException in ArrayList$ltr.next (ArrayList.java:831)
-            for (LedgerTransactionAccount acc : tr.getAccounts()) {
-//                debug(c.getAccountName(), acc.getAmount()));
-                publishProgress(new TransactionLoaderStep(p[0].holder, acc, rowIndex++,
-                        p[0].boldAccountName));
-            }
-
-            publishProgress(new TransactionLoaderStep(p[0].holder, p[0].position, rowIndex));
-
-            return null;
-        }
-        @Override
-        protected void onProgressUpdate(TransactionLoaderStep... values) {
-            super.onProgressUpdate(values);
-            TransactionLoaderStep step = values[0];
-            TransactionRowHolder holder = step.getHolder();
-
-            switch (step.getStep()) {
-                case HEAD:
-                    holder.tvDescription.setText(step.getTransaction()
-                                                     .getDescription());
-                    String trComment = Misc.emptyIsNull(step.getTransaction()
-                                                            .getComment());
-                    if (trComment == null)
-                        holder.tvComment.setVisibility(View.GONE);
-                    else {
-                        holder.tvComment.setText(trComment);
-                        holder.tvComment.setVisibility(View.VISIBLE);
-                    }
-
-//                    if (step.isOdd())
-//                        holder.row.setBackgroundColor(Colors.tableRowDarkBG);
-//                    else
-//                        holder.row.setBackgroundColor(Colors.tableRowLightBG);
-
-                    break;
-                case ACCOUNTS:
-                    int rowIndex = step.getAccountPosition();
-                    Context ctx = holder.row.getContext();
-                    LinearLayout row = (LinearLayout) holder.tableAccounts.getChildAt(rowIndex);
-                    if (row == null) {
-                        row = new LinearLayout(ctx);
-                        LayoutInflater inflater = ((Activity) ctx).getLayoutInflater();
-                        inflater.inflate(R.layout.transaction_list_row_accounts_table_row, row);
-                        holder.tableAccounts.addView(row);
-                    }
-                    TextView dummyText = row.findViewById(R.id.dummy_text);
-                    TextView accName = row.findViewById(R.id.transaction_list_acc_row_acc_name);
-                    TextView accComment =
-                            row.findViewById(R.id.transaction_list_acc_row_acc_comment);
-                    TextView accAmount = row.findViewById(R.id.transaction_list_acc_row_acc_amount);
-                    LedgerTransactionAccount acc = step.getAccount();
-
-
-//                    debug("tmp", String.format("showing acc row %d: %s %1.2f", rowIndex,
-//                            acc.getAccountName(), acc.getAmount()));
-
-                    String boldAccountName = step.getBoldAccountName();
-                    if ((boldAccountName != null) && acc.getAccountName()
-                                                        .startsWith(boldAccountName))
-                    {
-                        accName.setTextColor(Colors.secondary);
-                        accAmount.setTextColor(Colors.secondary);
-
-                        SpannableString ss = new SpannableString(acc.getAccountName());
-                        ss.setSpan(new StyleSpan(Typeface.BOLD), 0, boldAccountName.length(),
-                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                        accName.setText(ss);
-                    }
-                    else {
-                        @ColorInt int textColor = dummyText.getTextColors()
-                                                           .getDefaultColor();
-                        accName.setTextColor(textColor);
-                        accAmount.setTextColor(textColor);
-                        accName.setText(acc.getAccountName());
-                    }
-
-                    String comment = acc.getComment();
-                    if (comment != null && !comment.isEmpty()) {
-                        accComment.setText(comment);
-                        accComment.setVisibility(View.VISIBLE);
-                    }
-                    else {
-                        accComment.setVisibility(View.GONE);
-                    }
-                    accAmount.setText(acc.toString());
-
-                    break;
-                case DONE:
-                    int accCount = step.getAccountCount();
-                    if (holder.tableAccounts.getChildCount() > accCount) {
-                        holder.tableAccounts.removeViews(accCount,
-                                holder.tableAccounts.getChildCount() - accCount);
-                    }
-
-//                    debug("transactions",
-//                            String.format("Position %d fill done", step.getPosition()));
-            }
-        }
-    }
-
-    private static class TransactionLoaderParams {
-        final LedgerTransaction transaction;
-        final TransactionRowHolder holder;
-        final int position;
-        final String boldAccountName;
-        TransactionLoaderParams(LedgerTransaction transaction, TransactionRowHolder holder,
-                                int position, String boldAccountName) {
-            this.transaction = transaction;
-            this.holder = holder;
-            this.position = position;
-            this.boldAccountName = boldAccountName;
-        }
-    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java
new file mode 100644 (file)
index 0000000..390b494
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.transaction_list;
+
+import android.view.View;
+
+import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.databinding.TransactionDelimiterBinding;
+import net.ktnx.mobileledger.model.TransactionListItem;
+import net.ktnx.mobileledger.utils.Globals;
+import net.ktnx.mobileledger.utils.SimpleDate;
+
+import java.text.DateFormat;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+class TransactionListDelimiterRowHolder extends TransactionRowHolderBase {
+    private final TransactionDelimiterBinding b;
+    TransactionListDelimiterRowHolder(TransactionDelimiterBinding binding) {
+        super(binding.getRoot());
+        b = binding;
+    }
+    public void bind(TransactionListItem item) {
+        SimpleDate date = item.getDate();
+        b.transactionDelimiterDate.setText(DateFormat.getDateInstance()
+                                                     .format(date.toDate()));
+        if (item.isMonthShown()) {
+            GregorianCalendar cal = new GregorianCalendar(TimeZone.getDefault());
+            cal.setTime(date.toDate());
+            App.prepareMonthNames();
+            b.transactionDelimiterMonth.setText(
+                    Globals.monthNames[cal.get(GregorianCalendar.MONTH)]);
+            b.transactionDelimiterMonth.setVisibility(View.VISIBLE);
+            //                holder.vDelimiterLine.setBackgroundResource(R.drawable
+            //                .dashed_border_8dp);
+            b.transactionDelimiterThick.setVisibility(View.VISIBLE);
+        }
+        else {
+            b.transactionDelimiterMonth.setVisibility(View.GONE);
+            //                holder.vDelimiterLine.setBackgroundResource(R.drawable
+            //                .dashed_border_1dp);
+            b.transactionDelimiterThick.setVisibility(View.GONE);
+        }
+
+    }
+}
index a809a17eb2c243ee23fe482cdfcafba44076b343..56d5032f90a06d12bcc41e4b10196c50eafaecf9 100644 (file)
@@ -106,7 +106,7 @@ public class TransactionListFragment extends MobileLedgerListFragment
         root = view.findViewById(R.id.transaction_root);
         if (root == null)
             throw new RuntimeException("Can't get hold on the transaction value view");
-        modelAdapter = new TransactionListAdapter(model);
+        modelAdapter = new TransactionListAdapter();
         root.setAdapter(modelAdapter);
 
         mainActivity.fabShouldShow();
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListLastUpdateRowHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListLastUpdateRowHolder.java
new file mode 100644 (file)
index 0000000..0d0d3d3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.transaction_list;
+
+import net.ktnx.mobileledger.databinding.LastUpdateLayoutBinding;
+import net.ktnx.mobileledger.model.Data;
+
+class TransactionListLastUpdateRowHolder extends TransactionRowHolderBase {
+    private final LastUpdateLayoutBinding b;
+    TransactionListLastUpdateRowHolder(LastUpdateLayoutBinding binding) {
+        super(binding.getRoot());
+        b = binding;
+    }
+    void setLastUpdateText(String text) {
+        b.lastUpdateText.setText(text);
+    }
+    public void bind() {
+        b.lastUpdateText.setText(Data.lastTransactionsUpdateText.getValue());
+    }
+}
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionLoaderStep.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionLoaderStep.java
deleted file mode 100644 (file)
index 8fcb6de..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright © 2020 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.transaction_list;
-
-import net.ktnx.mobileledger.model.LedgerTransaction;
-import net.ktnx.mobileledger.model.LedgerTransactionAccount;
-
-class TransactionLoaderStep {
-    private final TransactionListAdapter.LoaderStep step;
-    private final TransactionRowHolder holder;
-    private int position;
-    private int accountCount;
-    private LedgerTransaction transaction;
-    private LedgerTransactionAccount account;
-    private int accountPosition;
-    private String boldAccountName;
-    public TransactionLoaderStep(TransactionRowHolder holder, int position,
-                                 LedgerTransaction transaction) {
-        this.step = TransactionListAdapter.LoaderStep.HEAD;
-        this.holder = holder;
-        this.transaction = transaction;
-        this.position = position;
-    }
-    public TransactionLoaderStep(TransactionRowHolder holder, LedgerTransactionAccount account,
-                                 int accountPosition, String boldAccountName) {
-        this.step = TransactionListAdapter.LoaderStep.ACCOUNTS;
-        this.holder = holder;
-        this.account = account;
-        this.accountPosition = accountPosition;
-        this.boldAccountName = boldAccountName;
-    }
-    public TransactionLoaderStep(TransactionRowHolder holder, int position, int accountCount) {
-        this.step = TransactionListAdapter.LoaderStep.DONE;
-        this.holder = holder;
-        this.position = position;
-        this.accountCount = accountCount;
-    }
-    public int getAccountCount() {
-        return accountCount;
-    }
-    public int getPosition() {
-        return position;
-    }
-    public String getBoldAccountName() {
-        return boldAccountName;
-    }
-    public int getAccountPosition() {
-        return accountPosition;
-    }
-    public TransactionRowHolder getHolder() {
-        return holder;
-    }
-    public TransactionListAdapter.LoaderStep getStep() {
-        return step;
-    }
-    public LedgerTransaction getTransaction() {
-        return transaction;
-    }
-    public LedgerTransactionAccount getAccount() {
-        return account;
-    }
-}
index 5ec19ba94649e41b89e3e8636ab69f3f32bcf250..85d36911a6e772421b0fff015039cc281493cd02 100644 (file)
 
 package net.ktnx.mobileledger.ui.transaction_list;
 
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
-import androidx.cardview.widget.CardView;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.recyclerview.widget.RecyclerView;
+import androidx.annotation.Nullable;
 
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.databinding.TransactionListRowBinding;
+import net.ktnx.mobileledger.model.LedgerTransaction;
+import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.model.TransactionListItem;
+import net.ktnx.mobileledger.utils.Colors;
+import net.ktnx.mobileledger.utils.Misc;
 
 import java.util.Observer;
 
-class TransactionRowHolder extends RecyclerView.ViewHolder {
-    final TextView tvDescription;
-    final TextView tvComment;
-    final LinearLayout tableAccounts;
-    final ConstraintLayout row;
-    final ConstraintLayout vDelimiter;
-    final CardView vTransaction;
-    final TextView tvDelimiterMonth, tvDelimiterDate;
-    final View vDelimiterThick;
-    final View vHeader;
-    final TextView tvLastUpdate;
+class TransactionRowHolder extends TransactionRowHolderBase {
+    private final TransactionListRowBinding b;
     TransactionListItem.Type lastType;
     private Observer lastUpdateObserver;
-    public TransactionRowHolder(@NonNull View itemView) {
-        super(itemView);
-        this.row = itemView.findViewById(R.id.transaction_row);
-        this.tvDescription = itemView.findViewById(R.id.transaction_row_description);
-        this.tvComment = itemView.findViewById(R.id.transaction_comment);
-        this.tableAccounts = itemView.findViewById(R.id.transaction_row_acc_amounts);
-        this.vDelimiter = itemView.findViewById(R.id.transaction_delimiter);
-        this.vTransaction = itemView.findViewById(R.id.transaction_card_view);
-        this.tvDelimiterDate = itemView.findViewById(R.id.transaction_delimiter_date);
-        this.tvDelimiterMonth = itemView.findViewById(R.id.transaction_delimiter_month);
-        this.vDelimiterThick = itemView.findViewById(R.id.transaction_delimiter_thick);
-        this.vHeader = itemView.findViewById(R.id.last_update_container);
-        this.tvLastUpdate = itemView.findViewById(R.id.last_update_text);
+    public TransactionRowHolder(@NonNull TransactionListRowBinding binding) {
+        super(binding.getRoot());
+        b = binding;
     }
-    void setLastUpdateText(String text) {
-        tvLastUpdate.setText(text);
-    }
-    void setType(TransactionListItem.Type newType) {
-        if (newType == lastType)
-            return;
+    public void bind(@NonNull LedgerTransaction tr, @Nullable String boldAccountName) {
+        b.transactionRowDescription.setText(tr.getDescription());
+        String trComment = Misc.emptyIsNull(tr.getComment());
+        if (trComment == null)
+            b.transactionComment.setVisibility(View.GONE);
+        else {
+            b.transactionComment.setText(trComment);
+            b.transactionComment.setVisibility(View.VISIBLE);
+        }
+
+        int rowIndex = 0;
+        Context ctx = b.getRoot()
+                       .getContext();
+        LayoutInflater inflater = ((Activity) ctx).getLayoutInflater();
+        for (LedgerTransactionAccount acc : tr.getAccounts()) {
+            LinearLayout row = (LinearLayout) b.transactionRowAccAmounts.getChildAt(rowIndex);
+            if (row == null) {
+                row = new LinearLayout(ctx);
+                inflater.inflate(R.layout.transaction_list_row_accounts_table_row, row);
+                b.transactionRowAccAmounts.addView(row);
+            }
+
+            TextView dummyText = row.findViewById(R.id.dummy_text);
+            TextView accName = row.findViewById(R.id.transaction_list_acc_row_acc_name);
+            TextView accComment = row.findViewById(R.id.transaction_list_acc_row_acc_comment);
+            TextView accAmount = row.findViewById(R.id.transaction_list_acc_row_acc_amount);
 
-        switch (newType) {
-            case TRANSACTION:
-                vHeader.setVisibility(View.GONE);
-                vTransaction.setVisibility(View.VISIBLE);
-                vDelimiter.setVisibility(View.GONE);
-                break;
-            case DELIMITER:
-                vHeader.setVisibility(View.GONE);
-                vTransaction.setVisibility(View.GONE);
-                vDelimiter.setVisibility(View.VISIBLE);
-                break;
-            case HEADER:
-                vHeader.setVisibility(View.VISIBLE);
-                vTransaction.setVisibility(View.GONE);
-                vDelimiter.setVisibility(View.GONE);
-                break;
-            default:
-                throw new IllegalStateException("Unexpected value: " + newType);
+            if ((boldAccountName != null) && acc.getAccountName()
+                                                .startsWith(boldAccountName))
+            {
+                accName.setTextColor(Colors.secondary);
+                accAmount.setTextColor(Colors.secondary);
+
+                SpannableString ss = new SpannableString(acc.getAccountName());
+                ss.setSpan(new StyleSpan(Typeface.BOLD), 0, boldAccountName.length(),
+                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                accName.setText(ss);
+            }
+            else {
+                @ColorInt int textColor = dummyText.getTextColors()
+                                                   .getDefaultColor();
+                accName.setTextColor(textColor);
+                accAmount.setTextColor(textColor);
+                accName.setText(acc.getAccountName());
+            }
+
+            String comment = acc.getComment();
+            if (comment != null && !comment.isEmpty()) {
+                accComment.setText(comment);
+                accComment.setVisibility(View.VISIBLE);
+            }
+            else {
+                accComment.setVisibility(View.GONE);
+            }
+            accAmount.setText(acc.toString());
+
+            rowIndex++;
         }
 
-        lastType = newType;
+        if (b.transactionRowAccAmounts.getChildCount() > rowIndex) {
+            b.transactionRowAccAmounts.removeViews(rowIndex,
+                    b.transactionRowAccAmounts.getChildCount() - rowIndex);
+        }
     }
 }
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolderBase.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolderBase.java
new file mode 100644 (file)
index 0000000..951cab6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.transaction_list;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class TransactionRowHolderBase extends RecyclerView.ViewHolder {
+    public TransactionRowHolderBase(@NonNull View itemView) {
+        super(itemView);
+    }
+    public TransactionListLastUpdateRowHolder asHeader() {
+        return (TransactionListLastUpdateRowHolder) this;
+    }
+    public TransactionRowHolder asTransaction() {
+        return (TransactionRowHolder) this;
+    }
+    public TransactionListDelimiterRowHolder asDelimiter() {
+        return (TransactionListDelimiterRowHolder) this;
+    }
+}
diff --git a/app/src/main/res/layout/transaction_delimiter.xml b/app/src/main/res/layout/transaction_delimiter.xml
new file mode 100644 (file)
index 0000000..0b283b3
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright © 2021 Damyan Ivanov.
+  ~ This file is part of MoLe.
+  ~ MoLe is free software: you can distribute it and/or modify it
+  ~ under the term of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ (at your opinion), any later version.
+  ~
+  ~ MoLe is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  ~ GNU General Public License terms for details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/transaction_delimiter"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="8dp"
+    android:layout_marginTop="16dp"
+    android:layout_marginEnd="8dp"
+    >
+
+    <TextView
+        android:id="@+id/transaction_delimiter_month"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginHorizontal="4dp"
+        android:text="---------"
+        android:textColor="?colorPrimary"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toEndOf="parent"
+        tools:ignore="HardcodedText"
+        />
+
+    <TextView
+        android:id="@+id/transaction_delimiter_date"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginHorizontal="4dp"
+        android:text="--.--.----"
+        android:textColor="?colorPrimary"
+        android:textStyle="bold"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:ignore="HardcodedText"
+        />
+
+    <View
+        android:id="@+id/transaction_delimiter_thick"
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_marginHorizontal="16dp"
+        android:background="?colorPrimary"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/transaction_delimiter_month"
+        app:layout_constraintStart_toEndOf="@id/transaction_delimiter_date"
+        app:layout_constraintTop_toTopOf="parent"
+        />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
index a8940553ee5b151447072283013a0a1c2007aff9..86b571692148942e553c106f0bc1e58a521b2c87 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  ~ Copyright © 2020 Damyan Ivanov.
+  ~ Copyright © 2021 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
@@ -22,7 +22,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:measureAllChildren="false"
     >
 
     <com.google.android.material.card.MaterialCardView
         </androidx.constraintlayout.widget.ConstraintLayout>
     </com.google.android.material.card.MaterialCardView>
 
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:id="@+id/transaction_delimiter"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
-        android:layout_marginTop="16dp"
-        android:layout_marginEnd="8dp"
-        >
-
-        <TextView
-            android:id="@+id/transaction_delimiter_month"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginHorizontal="4dp"
-            android:text="---------"
-            android:textColor="?colorPrimary"
-            android:textStyle="bold"
-            app:layout_constraintEnd_toEndOf="parent"
-            tools:ignore="HardcodedText"
-            />
-
-        <TextView
-            android:id="@+id/transaction_delimiter_date"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginHorizontal="4dp"
-            android:text="--.--.----"
-            android:textColor="?colorPrimary"
-            android:textStyle="bold"
-            app:layout_constraintStart_toStartOf="parent"
-            tools:ignore="HardcodedText"
-            />
-
-        <View
-            android:id="@+id/transaction_delimiter_thick"
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_marginHorizontal="16dp"
-            android:background="?colorPrimary"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/transaction_delimiter_month"
-            app:layout_constraintStart_toEndOf="@id/transaction_delimiter_date"
-            app:layout_constraintTop_toTopOf="parent"
-            />
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
-    <include layout="@layout/last_update_layout" />
 
 </FrameLayout>
\ No newline at end of file