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.
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.
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/>.
18 package net.ktnx.mobileledger.ui.templates;
20 import android.os.AsyncTask;
22 import androidx.lifecycle.LiveData;
23 import androidx.lifecycle.MutableLiveData;
24 import androidx.lifecycle.Observer;
25 import androidx.lifecycle.ViewModel;
27 import net.ktnx.mobileledger.dao.TemplateAccountDAO;
28 import net.ktnx.mobileledger.dao.TemplateHeaderDAO;
29 import net.ktnx.mobileledger.db.DB;
30 import net.ktnx.mobileledger.db.TemplateAccount;
31 import net.ktnx.mobileledger.db.TemplateHeader;
32 import net.ktnx.mobileledger.db.TemplateWithAccounts;
33 import net.ktnx.mobileledger.model.TemplateDetailsItem;
34 import net.ktnx.mobileledger.utils.Logger;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Objects;
41 import java.util.concurrent.atomic.AtomicInteger;
43 public class TemplateDetailsViewModel extends ViewModel {
44 private final MutableLiveData<List<TemplateDetailsItem>> items =
45 new MutableLiveData<>(Collections.emptyList());
46 private Long mPatternId;
47 private String mDefaultPatternName;
48 private boolean itemsLoaded = false;
49 private final AtomicInteger syntheticItemId = new AtomicInteger(0);
51 public String getDefaultPatternName() {
52 return mDefaultPatternName;
54 public void setDefaultPatternName(String name) {
55 mDefaultPatternName = name;
58 public void resetItems() {
59 applyList(new ArrayList<>());
61 public void applyList(List<TemplateDetailsItem> srcList) {
62 applyList(srcList, false);
64 public void applyList(List<TemplateDetailsItem> srcList, boolean async) {
66 if (srcList == null) {
67 srcList = new ArrayList<>(items.getValue());
73 srcList = Collections.unmodifiableList(srcList);
76 Logger.debug("tmpl", "Considering old list");
77 for (TemplateDetailsItem item : srcList)
78 Logger.debug("tmpl", String.format(Locale.US, " id %d pos %d", item.getId(),
82 ArrayList<TemplateDetailsItem> newList = new ArrayList<>();
84 boolean hasEmptyItem = false;
86 if (srcList.size() < 1) {
87 final TemplateDetailsItem.Header header = TemplateDetailsItem.createHeader();
88 header.setName(mDefaultPatternName);
94 newList.add(srcList.get(0));
97 for (int i = 1; i < srcList.size(); i++) {
98 final TemplateDetailsItem.AccountRow accRow = srcList.get(i)
100 if (accRow.isEmpty()) {
101 // it is normal to have two empty rows if they are at the
102 // top (position 1 and 2)
103 if (!hasEmptyItem || i < 3) {
104 accRow.setPosition(newList.size());
108 changes = true; // row skipped
113 accRow.setPosition(newList.size());
118 while (newList.size() < 3) {
119 final TemplateDetailsItem.AccountRow accountRow =
120 TemplateDetailsItem.createAccountRow();
121 accountRow.setId(genItemId());
122 accountRow.setPosition(newList.size());
123 newList.add(accountRow);
129 final TemplateDetailsItem.AccountRow accountRow =
130 TemplateDetailsItem.createAccountRow();
131 accountRow.setId(genItemId());
132 accountRow.setPosition(newList.size());
133 newList.add(accountRow);
138 Logger.debug("tmpl", "Changes detected, applying new list");
141 items.postValue(newList);
143 items.setValue(newList);
146 Logger.debug("tmpl", "No changes, ignoring new list");
148 public int genItemId() {
149 return syntheticItemId.decrementAndGet();
151 public LiveData<List<TemplateDetailsItem>> getItems(Long patternId) {
152 if (itemsLoaded && Objects.equals(patternId, this.mPatternId))
155 if (patternId != null && patternId <= 0)
156 throw new IllegalArgumentException("Pattern ID " + patternId + " is invalid");
158 mPatternId = patternId;
160 if (mPatternId == null) {
167 LiveData<TemplateWithAccounts> dbList = db.getTemplateDAO()
168 .getTemplateWithAccounts(mPatternId);
169 Observer<TemplateWithAccounts> observer = new Observer<TemplateWithAccounts>() {
171 public void onChanged(TemplateWithAccounts src) {
172 ArrayList<TemplateDetailsItem> l = new ArrayList<>();
174 TemplateDetailsItem header = TemplateDetailsItem.fromRoomObject(src.header);
176 Collections.sort(src.accounts,
177 (o1, o2) -> Long.compare(o1.getPosition(), o2.getPosition()));
178 for (TemplateAccount acc : src.accounts) {
179 l.add(TemplateDetailsItem.fromRoomObject(acc));
182 for (TemplateDetailsItem i : l) {
183 Logger.debug("patterns-db", "Loaded pattern item " + i);
188 dbList.removeObserver(this);
191 dbList.observeForever(observer);
195 public void setTestText(String text) {
196 List<TemplateDetailsItem> list = new ArrayList<>(items.getValue());
197 TemplateDetailsItem.Header header = new TemplateDetailsItem.Header(list.get(0)
199 header.setTestText(text);
202 items.setValue(list);
204 public void onSaveTemplate() {
205 Logger.debug("flow", "PatternDetailsViewModel.onSavePattern(); model=" + this);
206 final List<TemplateDetailsItem> list = Objects.requireNonNull(items.getValue());
208 AsyncTask.execute(() -> {
209 boolean newPattern = mPatternId == null || mPatternId <= 0;
211 TemplateDetailsItem.Header modelHeader = list.get(0)
213 TemplateHeaderDAO headerDAO = DB.get()
215 TemplateHeader dbHeader = modelHeader.toDBO();
217 dbHeader.setId(null);
218 dbHeader.setId(mPatternId = headerDAO.insertSync(dbHeader));
221 headerDAO.updateSync(dbHeader);
223 Logger.debug("pattern-db",
224 String.format(Locale.US, "Stored pattern header %d, item=%s", dbHeader.getId(),
228 TemplateAccountDAO taDAO = DB.get()
229 .getTemplateAccountDAO();
230 taDAO.prepareForSave(mPatternId);
231 for (int i = 1; i < list.size(); i++) {
232 final TemplateDetailsItem.AccountRow accRowItem = list.get(i)
234 TemplateAccount dbAccount = accRowItem.toDBO(dbHeader.getId());
235 dbAccount.setTemplateId(mPatternId);
236 dbAccount.setPosition(i);
237 if (dbAccount.getId() < 0) {
238 dbAccount.setId(null);
239 dbAccount.setId(taDAO.insertSync(dbAccount));
242 taDAO.updateSync(dbAccount);
244 Logger.debug("pattern-db", String.format(Locale.US,
245 "Stored pattern account %d, account=%s, comment=%s, neg=%s, item=%s",
246 dbAccount.getId(), dbAccount.getAccountName(),
247 dbAccount.getAccountComment(), dbAccount.getNegateAmount(), accRowItem));
249 taDAO.finishSave(mPatternId);
252 private ArrayList<TemplateDetailsItem> copyItems() {
253 List<TemplateDetailsItem> oldList = items.getValue();
254 ArrayList<TemplateDetailsItem> result = new ArrayList<>(oldList.size());
256 for (TemplateDetailsItem item : oldList) {
257 if (item instanceof TemplateDetailsItem.Header)
258 result.add(new TemplateDetailsItem.Header(item.asHeaderItem()));
259 else if (item instanceof TemplateDetailsItem.AccountRow)
260 result.add(new TemplateDetailsItem.AccountRow(item.asAccountRowItem()));
262 throw new RuntimeException("Unexpected item " + item);
267 public void moveItem(int sourcePos, int targetPos) {
268 ArrayList<TemplateDetailsItem> newList = new ArrayList<>(items.getValue());
269 TemplateDetailsItem item = newList.remove(sourcePos);
270 newList.add(targetPos, item);
271 items.setValue(newList);
273 public void removeItem(int position) {
274 ArrayList<TemplateDetailsItem> newList = new ArrayList<>(items.getValue());
275 newList.remove(position);