]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemsAdapter.java
Room-based profile management
[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.getAdapterPosition();
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.getAdapterPosition();
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.getAdapterPosition(), target.getAdapterPosition());
113                 return true;
114             }
115             @Override
116             public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
117                 int pos = viewHolder.getAdapterPosition();
118                 viewModel.removeItem(pos);
119             }
120         });
121     }
122     @Override
123     public int getItemViewType(int position) {
124         final ItemType type = differ.getCurrentList()
125                                     .get(position)
126                                     .getType();
127         switch (type) {
128             case generalData:
129                 return ITEM_VIEW_TYPE_HEADER;
130             case transactionRow:
131                 return ITEM_VIEW_TYPE_ACCOUNT;
132             default:
133                 throw new RuntimeException("Can't handle " + type);
134         }
135     }
136     @Override
137     public long getItemId(int position) {
138         return differ.getCurrentList()
139                      .get(position)
140                      .getId();
141     }
142     public void setProfile(Profile profile) {
143         mProfile = profile;
144     }
145     @NonNull
146     @Override
147     public NewTransactionItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
148                                                            int viewType) {
149         switch (viewType) {
150             case ITEM_VIEW_TYPE_HEADER:
151                 NewTransactionHeaderRowBinding headerBinding =
152                         NewTransactionHeaderRowBinding.inflate(
153                                 LayoutInflater.from(parent.getContext()), parent, false);
154                 final NewTransactionHeaderItemHolder headerHolder =
155                         new NewTransactionHeaderItemHolder(headerBinding, this);
156                 Logger.debug("new-trans", "Creating new Header ViewHolder " +
157                                           Integer.toHexString(headerHolder.hashCode()));
158                 return headerHolder;
159             case ITEM_VIEW_TYPE_ACCOUNT:
160                 NewTransactionAccountRowBinding accBinding =
161                         NewTransactionAccountRowBinding.inflate(
162                                 LayoutInflater.from(parent.getContext()), parent, false);
163                 final NewTransactionAccountRowItemHolder accHolder =
164                         new NewTransactionAccountRowItemHolder(accBinding, this);
165                 Logger.debug("new-trans", "Creating new AccountRow ViewHolder " +
166                                           Integer.toHexString(accHolder.hashCode()));
167                 return accHolder;
168             default:
169                 throw new RuntimeException("Cant handle view type " + viewType);
170         }
171     }
172     @Override
173     public void onBindViewHolder(@NonNull NewTransactionItemViewHolder holder, int position) {
174         Logger.debug("bind",
175                 String.format(Locale.US, "Binding item at position %d, holder %s", position,
176                         Integer.toHexString(holder.hashCode())));
177         NewTransactionModel.Item item = Objects.requireNonNull(differ.getCurrentList()
178                                                                      .get(position));
179         holder.bind(item);
180         Logger.debug("bind", String.format(Locale.US, "Bound %s item at position %d", item.getType()
181                                                                                           .toString(),
182                 position));
183     }
184     @Override
185     public int getItemCount() {
186         return differ.getCurrentList()
187                      .size();
188     }
189     @Override
190     public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
191         super.onAttachedToRecyclerView(recyclerView);
192         touchHelper.attachToRecyclerView(recyclerView);
193     }
194     @Override
195     public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
196         touchHelper.attachToRecyclerView(null);
197         super.onDetachedFromRecyclerView(recyclerView);
198     }
199     void noteFocusIsOnAccount(int position) {
200         model.noteFocusChanged(position, FocusedElement.Account);
201     }
202     void noteFocusIsOnAmount(int position) {
203         model.noteFocusChanged(position, FocusedElement.Amount);
204     }
205     void noteFocusIsOnComment(int position) {
206         model.noteFocusChanged(position, FocusedElement.Comment);
207     }
208     void noteFocusIsOnTransactionComment(int position) {
209         model.noteFocusChanged(position, FocusedElement.TransactionComment);
210     }
211     public void noteFocusIsOnDescription(int pos) {
212         model.noteFocusChanged(pos, FocusedElement.Description);
213     }
214     private void holdSubmittableChecks() {
215         checkHoldCounter++;
216     }
217     private void releaseSubmittableChecks() {
218         if (checkHoldCounter == 0)
219             throw new RuntimeException("Asymmetrical call to releaseSubmittableChecks");
220         checkHoldCounter--;
221     }
222     void setItemCurrency(int position, String newCurrency) {
223         model.setItemCurrency(position, newCurrency);
224     }
225
226     public void setItems(List<NewTransactionModel.Item> newList) {
227         Logger.debug("new-trans", "adapter: submitting new item list");
228         differ.submitList(newList);
229     }
230     public NewTransactionModel.Item getItem(int position) {
231         return differ.getCurrentList()
232                      .get(position);
233     }
234 }