]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java
more Room adoption - accounts@100%, some profiles/transactions
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / MainModel.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;
19
20 import android.os.AsyncTask;
21 import android.text.TextUtils;
22
23 import androidx.lifecycle.LiveData;
24 import androidx.lifecycle.MutableLiveData;
25 import androidx.lifecycle.ViewModel;
26
27 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
28 import net.ktnx.mobileledger.async.TransactionAccumulator;
29 import net.ktnx.mobileledger.async.UpdateTransactionsTask;
30 import net.ktnx.mobileledger.model.AccountListItem;
31 import net.ktnx.mobileledger.model.Data;
32 import net.ktnx.mobileledger.model.LedgerAccount;
33 import net.ktnx.mobileledger.model.LedgerTransaction;
34 import net.ktnx.mobileledger.model.MobileLedgerProfile;
35 import net.ktnx.mobileledger.model.TransactionListItem;
36 import net.ktnx.mobileledger.utils.Locker;
37 import net.ktnx.mobileledger.utils.Logger;
38 import net.ktnx.mobileledger.utils.MLDB;
39 import net.ktnx.mobileledger.utils.SimpleDate;
40
41 import java.util.ArrayList;
42 import java.util.Date;
43 import java.util.List;
44 import java.util.Locale;
45
46 import static net.ktnx.mobileledger.utils.Logger.debug;
47
48 public class MainModel extends ViewModel {
49     public final MutableLiveData<Integer> foundTransactionItemIndex = new MutableLiveData<>(null);
50     private final MutableLiveData<Boolean> updatingFlag = new MutableLiveData<>(false);
51     private final MutableLiveData<String> accountFilter = new MutableLiveData<>();
52     private final MutableLiveData<List<TransactionListItem>> displayedTransactions =
53             new MutableLiveData<>(new ArrayList<>());
54     private final MutableLiveData<List<AccountListItem>> displayedAccounts =
55             new MutableLiveData<>();
56     private final Locker accountsLocker = new Locker();
57     private final MutableLiveData<String> updateError = new MutableLiveData<>();
58     private MobileLedgerProfile profile;
59     private final List<LedgerAccount> allAccounts = new ArrayList<>();
60     private SimpleDate firstTransactionDate;
61     private SimpleDate lastTransactionDate;
62     transient private RetrieveTransactionsTask retrieveTransactionsTask;
63     transient private Thread displayedAccountsUpdater;
64     private TransactionsDisplayedFilter displayedTransactionsUpdater;
65     private void setLastUpdateStamp(long transactionCount) {
66         debug("db", "Updating transaction value stamp");
67         Date now = new Date();
68         profile.setLongOption(MLDB.OPT_LAST_SCRAPE, now.getTime());
69         Data.lastUpdateDate.postValue(now);
70     }
71     public void scheduleTransactionListReload() {
72         UpdateTransactionsTask task = new UpdateTransactionsTask();
73         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
74     }
75     public LiveData<Boolean> getUpdatingFlag() {
76         return updatingFlag;
77     }
78     public LiveData<String> getUpdateError() {
79         return updateError;
80     }
81     public void setProfile(MobileLedgerProfile profile) {
82         stopTransactionsRetrieval();
83         this.profile = profile;
84     }
85     public LiveData<List<TransactionListItem>> getDisplayedTransactions() {
86         return displayedTransactions;
87     }
88     public void setDisplayedTransactions(List<TransactionListItem> list, int transactionCount) {
89         displayedTransactions.postValue(list);
90         Data.lastUpdateTransactionCount.postValue(transactionCount);
91     }
92     public SimpleDate getFirstTransactionDate() {
93         return firstTransactionDate;
94     }
95     public void setFirstTransactionDate(SimpleDate earliestDate) {
96         this.firstTransactionDate = earliestDate;
97     }
98     public MutableLiveData<String> getAccountFilter() {
99         return accountFilter;
100     }
101     public SimpleDate getLastTransactionDate() {
102         return lastTransactionDate;
103     }
104     public void setLastTransactionDate(SimpleDate latestDate) {
105         this.lastTransactionDate = latestDate;
106     }
107     private void applyTransactionFilter(List<LedgerTransaction> list) {
108         final String accFilter = accountFilter.getValue();
109         ArrayList<TransactionListItem> newList = new ArrayList<>();
110
111         TransactionAccumulator accumulator = new TransactionAccumulator(this);
112         if (TextUtils.isEmpty(accFilter))
113             for (LedgerTransaction tr : list)
114                 newList.add(new TransactionListItem(tr));
115         else
116             for (LedgerTransaction tr : list)
117                 if (tr.hasAccountNamedLike(accFilter))
118                     newList.add(new TransactionListItem(tr));
119
120         displayedTransactions.postValue(newList);
121     }
122     public synchronized void scheduleTransactionListRetrieval() {
123         if (retrieveTransactionsTask != null) {
124             Logger.debug("db", "Ignoring request for transaction retrieval - already active");
125             return;
126         }
127         MobileLedgerProfile profile = Data.getProfile();
128
129         retrieveTransactionsTask = new RetrieveTransactionsTask(this, profile, allAccounts);
130         Logger.debug("db", "Created a background transaction retrieval task");
131
132         retrieveTransactionsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
133     }
134     public synchronized void stopTransactionsRetrieval() {
135         if (retrieveTransactionsTask != null)
136             retrieveTransactionsTask.cancel(true);
137     }
138     public void transactionRetrievalDone() {
139         retrieveTransactionsTask = null;
140     }
141     public synchronized Locker lockAccountsForWriting() {
142         accountsLocker.lockForWriting();
143         return accountsLocker;
144     }
145     public LiveData<List<AccountListItem>> getDisplayedAccounts() {
146         return displayedAccounts;
147     }
148     public synchronized void setAndStoreAccountAndTransactionListFromWeb(
149             List<LedgerAccount> accounts, List<LedgerTransaction> transactions) {
150         profile.storeAccountAndTransactionListAsync(accounts, transactions);
151
152         setLastUpdateStamp(transactions.size());
153
154         updateDisplayedTransactionsFromWeb(transactions);
155     }
156     synchronized public void updateDisplayedTransactionsFromWeb(List<LedgerTransaction> list) {
157         if (displayedTransactionsUpdater != null) {
158             displayedTransactionsUpdater.interrupt();
159         }
160         displayedTransactionsUpdater = new TransactionsDisplayedFilter(this, list);
161         displayedTransactionsUpdater.start();
162     }
163     public void clearUpdateError() {
164         updateError.postValue(null);
165     }
166     public void clearAccounts() { displayedAccounts.postValue(new ArrayList<>()); }
167     public void clearTransactions() {
168         displayedTransactions.setValue(new ArrayList<>());
169     }
170
171     static class TransactionsDisplayedFilter extends Thread {
172         private final MainModel model;
173         private final List<LedgerTransaction> list;
174         TransactionsDisplayedFilter(MainModel model, List<LedgerTransaction> list) {
175             this.model = model;
176             this.list = list;
177         }
178         @Override
179         public void run() {
180             List<LedgerAccount> newDisplayed = new ArrayList<>();
181             Logger.debug("dFilter", "waiting for synchronized block");
182             Logger.debug("dFilter", String.format(Locale.US,
183                     "entered synchronized block (about to examine %d transactions)", list.size()));
184             String accNameFilter = model.getAccountFilter()
185                                         .getValue();
186
187             TransactionAccumulator acc = new TransactionAccumulator(model);
188             for (LedgerTransaction tr : list) {
189                 if (isInterrupted()) {
190                     return;
191                 }
192
193                 if (accNameFilter == null || tr.hasAccountNamedLike(accNameFilter)) {
194                     acc.put(tr, tr.getDate());
195                 }
196             }
197             if (!isInterrupted()) {
198                 acc.done();
199             }
200             Logger.debug("dFilter", "left synchronized block");
201         }
202     }
203 }