]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java
dc78f11fd637568ba0ab1f17024b97323a6e110f
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / account_summary / AccountSummaryAdapter.java
1 /*
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.ktnx.mobileledger.ui.account_summary;
19
20 import android.content.res.Resources;
21 import android.os.AsyncTask;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27
28 import androidx.annotation.NonNull;
29 import androidx.appcompat.app.AlertDialog;
30 import androidx.constraintlayout.widget.ConstraintLayout;
31 import androidx.lifecycle.LifecycleOwner;
32 import androidx.recyclerview.widget.AsyncListDiffer;
33 import androidx.recyclerview.widget.DiffUtil;
34 import androidx.recyclerview.widget.RecyclerView;
35
36 import net.ktnx.mobileledger.R;
37 import net.ktnx.mobileledger.databinding.AccountListRowBinding;
38 import net.ktnx.mobileledger.databinding.AccountListSummaryRowBinding;
39 import net.ktnx.mobileledger.db.Account;
40 import net.ktnx.mobileledger.db.DB;
41 import net.ktnx.mobileledger.model.AccountListItem;
42 import net.ktnx.mobileledger.model.LedgerAccount;
43 import net.ktnx.mobileledger.ui.activity.MainActivity;
44 import net.ktnx.mobileledger.utils.Logger;
45 import net.ktnx.mobileledger.utils.Misc;
46
47 import org.jetbrains.annotations.NotNull;
48
49 import java.util.List;
50 import java.util.Locale;
51
52 import static net.ktnx.mobileledger.utils.Logger.debug;
53
54 public class AccountSummaryAdapter extends RecyclerView.Adapter<AccountSummaryAdapter.RowHolder> {
55     public static final int AMOUNT_LIMIT = 3;
56     private static final int ITEM_TYPE_HEADER = 1;
57     private static final int ITEM_TYPE_ACCOUNT = 2;
58     private final AsyncListDiffer<AccountListItem> listDiffer;
59     AccountSummaryAdapter() {
60         setHasStableIds(true);
61         
62         listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<AccountListItem>() {
63             @Override
64             public boolean areItemsTheSame(@NotNull AccountListItem oldItem,
65                                            @NotNull AccountListItem newItem) {
66                 final AccountListItem.Type oldType = oldItem.getType();
67                 final AccountListItem.Type newType = newItem.getType();
68                 if (oldType != newType)
69                     return false;
70                 if (oldType == AccountListItem.Type.HEADER)
71                     return true;
72
73                 return Misc.equalStrings(oldItem.getAccount()
74                                                 .getName(), newItem.getAccount()
75                                                                    .getName());
76             }
77             @Override
78             public boolean areContentsTheSame(@NotNull AccountListItem oldItem,
79                                               @NotNull AccountListItem newItem) {
80                 return oldItem.sameContent(newItem);
81             }
82         });
83     }
84     @Override
85     public long getItemId(int position) {
86         if (position == 0)
87             return 0;
88         return listDiffer.getCurrentList()
89                          .get(position)
90                          .getAccount()
91                          .getId();
92     }
93     public void onBindViewHolder(@NonNull RowHolder holder, int position) {
94         holder.bind(listDiffer.getCurrentList()
95                               .get(position));
96     }
97
98     @NonNull
99     @Override
100     public RowHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
101         final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
102
103         final RowHolder result;
104         switch (viewType) {
105             case ITEM_TYPE_HEADER:
106                 result = new HeaderRowHolder(
107                         AccountListSummaryRowBinding.inflate(inflater, parent, false));
108                 break;
109             case ITEM_TYPE_ACCOUNT:
110                 result = new AccountRowHolder(
111                         AccountListRowBinding.inflate(inflater, parent, false));
112                 break;
113             default:
114                 throw new IllegalStateException("Unexpected value: " + viewType);
115         }
116
117         Logger.debug("acc-ui", "Creating " + result);
118         return result;
119     }
120
121     @Override
122     public int getItemCount() {
123         return listDiffer.getCurrentList()
124                          .size();
125     }
126     @Override
127     public int getItemViewType(int position) {
128         return (position == 0) ? ITEM_TYPE_HEADER : ITEM_TYPE_ACCOUNT;
129     }
130     public void setAccounts(List<AccountListItem> newList) {
131         new Handler(Looper.getMainLooper()).post(() -> listDiffer.submitList(newList));
132     }
133     static abstract class RowHolder extends RecyclerView.ViewHolder {
134         public RowHolder(@NonNull View itemView) {
135             super(itemView);
136         }
137         public abstract void bind(AccountListItem accountListItem);
138     }
139
140     static class HeaderRowHolder extends RowHolder {
141         private final AccountListSummaryRowBinding b;
142         public HeaderRowHolder(@NonNull AccountListSummaryRowBinding binding) {
143             super(binding.getRoot());
144             b = binding;
145         }
146         @Override
147         public void bind(AccountListItem item) {
148             Resources r = itemView.getResources();
149             Logger.debug("acc", itemView.getContext()
150                                         .toString());
151             ((AccountListItem.Header) item).getText()
152                                            .observe((LifecycleOwner) itemView.getContext(),
153                                                    b.lastUpdateText::setText);
154         }
155     }
156
157     class AccountRowHolder extends AccountSummaryAdapter.RowHolder {
158         private final AccountListRowBinding b;
159         public AccountRowHolder(@NonNull AccountListRowBinding binding) {
160             super(binding.getRoot());
161             b = binding;
162
163             itemView.setOnLongClickListener(this::onItemLongClick);
164             b.accountRowAccName.setOnLongClickListener(this::onItemLongClick);
165             b.accountRowAccAmounts.setOnLongClickListener(this::onItemLongClick);
166             b.accountExpanderContainer.setOnLongClickListener(this::onItemLongClick);
167             b.accountExpander.setOnLongClickListener(this::onItemLongClick);
168
169             b.accountRowAccName.setOnClickListener(v -> toggleAccountExpanded());
170             b.accountExpanderContainer.setOnClickListener(v -> toggleAccountExpanded());
171             b.accountExpander.setOnClickListener(v -> toggleAccountExpanded());
172             b.accountRowAccAmounts.setOnClickListener(v -> toggleAmountsExpanded());
173         }
174         private void toggleAccountExpanded() {
175             LedgerAccount account = getAccount();
176             if (!account.hasSubAccounts())
177                 return;
178             debug("accounts", "Account expander clicked");
179
180             AsyncTask.execute(() -> {
181                 Account dbo = account.toDBO();
182                 dbo.setExpanded(!dbo.isExpanded());
183                 Logger.debug("accounts",
184                         String.format(Locale.ROOT, "%s (%d) → %s", account.getName(), dbo.getId(),
185                                 dbo.isExpanded() ? "expanded" : "collapsed"));
186                 DB.get()
187                   .getAccountDAO()
188                   .updateSync(dbo);
189             });
190         }
191         @NotNull
192         private LedgerAccount getAccount() {
193             return listDiffer.getCurrentList()
194                              .get(getAdapterPosition())
195                              .getAccount();
196         }
197         private void toggleAmountsExpanded() {
198             LedgerAccount account = getAccount();
199             if (account.getAmountCount() <= AMOUNT_LIMIT)
200                 return;
201
202             account.toggleAmountsExpanded();
203             if (account.amountsExpanded()) {
204                 b.accountRowAccAmounts.setText(account.getAmountsString());
205                 b.accountRowAmountsExpanderContainer.setVisibility(View.GONE);
206             }
207             else {
208                 b.accountRowAccAmounts.setText(account.getAmountsString(AMOUNT_LIMIT));
209                 b.accountRowAmountsExpanderContainer.setVisibility(View.VISIBLE);
210             }
211
212             AsyncTask.execute(() -> {
213                 Account dbo = account.toDBO();
214                 DB.get()
215                   .getAccountDAO()
216                   .updateSync(dbo);
217             });
218         }
219         private boolean onItemLongClick(View v) {
220             MainActivity activity = (MainActivity) v.getContext();
221             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
222             final String accountName = getAccount().getName();
223             builder.setTitle(accountName);
224             builder.setItems(R.array.acc_ctx_menu, (dialog, which) -> {
225                 if (which == 0) {// show transactions
226                     activity.showAccountTransactions(accountName);
227                 }
228                 else {
229                     throw new RuntimeException(String.format("Unknown menu item id (%d)", which));
230                 }
231                 dialog.dismiss();
232             });
233             builder.show();
234             return true;
235         }
236         @Override
237         public void bind(AccountListItem item) {
238             LedgerAccount acc = item.getAccount();
239
240             debug("accounts",
241                     String.format(Locale.US, "Binding to '%s' to %s", acc.getName(), this));
242             Resources rm = b.getRoot()
243                             .getContext()
244                             .getResources();
245
246             b.accountRowAccName.setText(acc.getShortName());
247
248             ConstraintLayout.LayoutParams lp =
249                     (ConstraintLayout.LayoutParams) b.accountNameLayout.getLayoutParams();
250             lp.setMarginStart(
251                     acc.getLevel() * rm.getDimensionPixelSize(R.dimen.thumb_row_height) / 3);
252
253             if (acc.hasSubAccounts()) {
254                 b.accountExpanderContainer.setVisibility(View.VISIBLE);
255                 int wantedRotation = acc.isExpanded() ? 0 : 180;
256                 if (b.accountExpanderContainer.getRotation() != wantedRotation) {
257                     Logger.debug("acc-ui",
258                             String.format(Locale.ROOT, "Rotating %s to %d", acc.getName(),
259                                     wantedRotation));
260                     b.accountExpanderContainer.animate()
261                                               .rotation(wantedRotation);
262                 }
263             }
264             else {
265                 b.accountExpanderContainer.setVisibility(View.GONE);
266             }
267
268             int amounts = acc.getAmountCount();
269             if ((amounts > AMOUNT_LIMIT) && !acc.amountsExpanded()) {
270                 b.accountRowAccAmounts.setText(acc.getAmountsString(AMOUNT_LIMIT));
271                 b.accountRowAmountsExpanderContainer.setVisibility(View.VISIBLE);
272             }
273             else {
274                 b.accountRowAccAmounts.setText(acc.getAmountsString());
275                 b.accountRowAmountsExpanderContainer.setVisibility(View.GONE);
276             }
277         }
278     }
279 }