]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemsAdapter.java
more pronounced day/month delimiters in the transaction list
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / new_transaction / NewTransactionItemsAdapter.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.new_transaction;
19
20 import android.view.LayoutInflater;
21 import android.view.ViewGroup;
22
23 import androidx.annotation.NonNull;
24 import androidx.recyclerview.widget.AsyncListDiffer;
25 import androidx.recyclerview.widget.DiffUtil;
26 import androidx.recyclerview.widget.ItemTouchHelper;
27 import androidx.recyclerview.widget.RecyclerView;
28
29 import net.ktnx.mobileledger.databinding.NewTransactionAccountRowBinding;
30 import net.ktnx.mobileledger.databinding.NewTransactionHeaderRowBinding;
31 import net.ktnx.mobileledger.db.Profile;
32 import net.ktnx.mobileledger.utils.Logger;
33
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Objects;
37
38 class NewTransactionItemsAdapter extends RecyclerView.Adapter<NewTransactionItemViewHolder> {
39     private static final int ITEM_VIEW_TYPE_HEADER = 1;
40     private static final int ITEM_VIEW_TYPE_ACCOUNT = 2;
41     final NewTransactionModel model;
42     private final ItemTouchHelper touchHelper;
43     private final AsyncListDiffer<NewTransactionModel.Item> differ =
44             new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<NewTransactionModel.Item>() {
45                 @Override
46                 public boolean areItemsTheSame(@NonNull NewTransactionModel.Item oldItem,
47                                                @NonNull NewTransactionModel.Item newItem) {
48 //                    Logger.debug("new-trans",
49 //                            String.format("comparing ids of {%s} and {%s}", oldItem.toString(),
50 //                                    newItem.toString()));
51                     return oldItem.getId() == newItem.getId();
52                 }
53                 @Override
54                 public boolean areContentsTheSame(@NonNull NewTransactionModel.Item oldItem,
55                                                   @NonNull NewTransactionModel.Item newItem) {
56
57 //                    Logger.debug("new-trans",
58 //                            String.format("comparing contents of {%s} and {%s}", oldItem
59 //                            .toString(),
60 //                                    newItem.toString()));
61                     return oldItem.equalContents(newItem);
62                 }
63             });
64     private Profile mProfile;
65     private int checkHoldCounter = 0;
66     NewTransactionItemsAdapter(NewTransactionModel viewModel, Profile profile) {
67         super();
68         setHasStableIds(true);
69         model = viewModel;
70         mProfile = profile;
71
72
73         NewTransactionItemsAdapter adapter = this;
74
75         touchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
76             @Override
77             public boolean isLongPressDragEnabled() {
78                 return true;
79             }
80             @Override
81             public boolean canDropOver(@NonNull RecyclerView recyclerView,
82                                        @NonNull RecyclerView.ViewHolder current,
83                                        @NonNull RecyclerView.ViewHolder target) {
84                 final int adapterPosition = target.getBindingAdapterPosition();
85
86                 // first item is immovable
87                 if (adapterPosition == 0)
88                     return false;
89
90                 return super.canDropOver(recyclerView, current, target);
91             }
92             @Override
93             public int getMovementFlags(@NonNull RecyclerView recyclerView,
94                                         @NonNull RecyclerView.ViewHolder viewHolder) {
95                 int flags = makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END);
96                 // the top (date and description) and the bottom (padding) items are always there
97                 final int adapterPosition = viewHolder.getBindingAdapterPosition();
98                 if (adapterPosition > 0) {
99                     flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
100                             ItemTouchHelper.UP | ItemTouchHelper.DOWN) |
101                              makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE,
102                                      ItemTouchHelper.START | ItemTouchHelper.END);
103                 }
104
105                 return flags;
106             }
107             @Override
108             public boolean onMove(@NonNull RecyclerView recyclerView,
109                                   @NonNull RecyclerView.ViewHolder viewHolder,
110                                   @NonNull RecyclerView.ViewHolder target) {
111
112                 model.moveItem(viewHolder.getBindingAdapterPosition(),
113                         target.getBindingAdapterPosition());
114                 return true;
115             }
116             @Override
117             public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
118                 int pos = viewHolder.getBindingAdapterPosition();
119                 viewModel.removeItem(pos);
120             }
121         });
122     }
123     @Override
124     public int getItemViewType(int position) {
125         final ItemType type = differ.getCurrentList()
126                                     .get(position)
127                                     .getType();
128         switch (type) {
129             case generalData:
130                 return ITEM_VIEW_TYPE_HEADER;
131             case transactionRow:
132                 return ITEM_VIEW_TYPE_ACCOUNT;
133             default:
134                 throw new RuntimeException("Can't handle " + type);
135         }
136     }
137     @Override
138     public long getItemId(int position) {
139         return differ.getCurrentList()
140                      .get(position)
141                      .getId();
142     }
143     public void setProfile(Profile profile) {
144         mProfile = profile;
145     }
146     @NonNull
147     @Override
148     public NewTransactionItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
149                                                            int viewType) {
150         switch (viewType) {
151             case ITEM_VIEW_TYPE_HEADER:
152                 NewTransactionHeaderRowBinding headerBinding =
153                         NewTransactionHeaderRowBinding.inflate(
154                                 LayoutInflater.from(parent.getContext()), parent, false);
155                 final NewTransactionHeaderItemHolder headerHolder =
156                         new NewTransactionHeaderItemHolder(headerBinding, this);
157                 Logger.debug("new-trans", "Creating new Header ViewHolder " +
158                                           Integer.toHexString(headerHolder.hashCode()));
159                 return headerHolder;
160             case ITEM_VIEW_TYPE_ACCOUNT:
161                 NewTransactionAccountRowBinding accBinding =
162                         NewTransactionAccountRowBinding.inflate(
163                                 LayoutInflater.from(parent.getContext()), parent, false);
164                 final NewTransactionAccountRowItemHolder accHolder =
165                         new NewTransactionAccountRowItemHolder(accBinding, this);
166                 Logger.debug("new-trans", "Creating new AccountRow ViewHolder " +
167                                           Integer.toHexString(accHolder.hashCode()));
168                 return accHolder;
169             default:
170                 throw new RuntimeException("Cant handle view type " + viewType);
171         }
172     }
173     @Override
174     public void onBindViewHolder(@NonNull NewTransactionItemViewHolder holder, int position) {
175         Logger.debug("bind",
176                 String.format(Locale.US, "Binding item at position %d, holder %s", position,
177                         Integer.toHexString(holder.hashCode())));
178         NewTransactionModel.Item item = Objects.requireNonNull(differ.getCurrentList()
179                                                                      .get(position));
180         holder.bind(item);
181         Logger.debug("bind", String.format(Locale.US, "Bound %s item at position %d", item.getType()
182                                                                                           .toString(),
183                 position));
184     }
185     @Override
186     public int getItemCount() {
187         return differ.getCurrentList()
188                      .size();
189     }
190     @Override
191     public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
192         super.onAttachedToRecyclerView(recyclerView);
193         touchHelper.attachToRecyclerView(recyclerView);
194     }
195     @Override
196     public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
197         touchHelper.attachToRecyclerView(null);
198         super.onDetachedFromRecyclerView(recyclerView);
199     }
200     void noteFocusIsOnAccount(int position) {
201         model.noteFocusChanged(position, FocusedElement.Account);
202     }
203     void noteFocusIsOnAmount(int position) {
204         model.noteFocusChanged(position, FocusedElement.Amount);
205     }
206     void noteFocusIsOnComment(int position) {
207         model.noteFocusChanged(position, FocusedElement.Comment);
208     }
209     void noteFocusIsOnTransactionComment(int position) {
210         model.noteFocusChanged(position, FocusedElement.TransactionComment);
211     }
212     public void noteFocusIsOnDescription(int pos) {
213         model.noteFocusChanged(pos, FocusedElement.Description);
214     }
215     private void holdSubmittableChecks() {
216         checkHoldCounter++;
217     }
218     private void releaseSubmittableChecks() {
219         if (checkHoldCounter == 0)
220             throw new RuntimeException("Asymmetrical call to releaseSubmittableChecks");
221         checkHoldCounter--;
222     }
223     void setItemCurrency(int position, String newCurrency) {
224         model.setItemCurrency(position, newCurrency);
225     }
226
227     public void setItems(List<NewTransactionModel.Item> newList) {
228         Logger.debug("new-trans", "adapter: submitting new item list");
229         differ.submitList(newList);
230     }
231     public NewTransactionModel.Item getItem(int position) {
232         return differ.getCurrentList()
233                      .get(position);
234     }
235 }