From: Damyan Ivanov Date: Sat, 10 Apr 2021 09:35:15 +0000 (+0300) Subject: more Room adoption - accounts@100%, some profiles/transactions X-Git-Tag: v0.18.0~90 X-Git-Url: https://git.ktnx.net/?a=commitdiff_plain;h=25cd3a8bb9b8196cddd1f66b7757cadc8248a37f;p=mobile-ledger.git more Room adoption - accounts@100%, some profiles/transactions --- diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java index 3b401b66..7bca44ce 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java @@ -23,16 +23,20 @@ import androidx.room.ColumnInfo; import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; +import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Update; import net.ktnx.mobileledger.db.Account; +import net.ktnx.mobileledger.db.AccountValue; import net.ktnx.mobileledger.db.AccountWithAmounts; +import net.ktnx.mobileledger.db.DB; import java.util.ArrayList; import java.util.List; + @Dao public abstract class AccountDAO extends BaseDAO { static public List unbox(List list) { @@ -43,9 +47,34 @@ public abstract class AccountDAO extends BaseDAO { return result; } - @Insert + + @Insert(onConflict = OnConflictStrategy.REPLACE) public abstract long insertSync(Account item); + @Insert(onConflict = OnConflictStrategy.REPLACE) + public abstract void insertSync(List items); + + @Transaction + public void insertSync(@NonNull AccountWithAmounts accountWithAmounts) { + final AccountValueDAO valueDAO = DB.get() + .getAccountValueDAO(); + Account account = accountWithAmounts.account; + Account existingAccount = getByNameSync(account.getProfileId(), account.getName()); + if (existingAccount != null) { + existingAccount.setGeneration(account.getGeneration()); + account = existingAccount; + updateSync(account); + } + else { + long accountId = insertSync(account); + account.setId(accountId); + } + for (AccountValue value : accountWithAmounts.amounts) { + value.setAccountId(account.getId()); + value.setGeneration(account.getGeneration()); + value.setId(valueDAO.insertSync(value)); + } + } @Update public abstract void updateSync(Account item); @@ -72,6 +101,9 @@ public abstract class AccountDAO extends BaseDAO { @Query("SELECT * FROM accounts WHERE profile_id = :profileId AND name = :accountName") public abstract LiveData getByName(long profileId, @NonNull String accountName); + @Query("SELECT * FROM accounts WHERE profile_id = :profileId AND name = :accountName") + public abstract Account getByNameSync(long profileId, @NonNull String accountName); + @Transaction @Query("SELECT * FROM accounts WHERE profile_id = :profileId AND name = :accountName") public abstract LiveData getByNameWithAmounts(long profileId, @@ -113,10 +145,48 @@ public abstract class AccountDAO extends BaseDAO { @Query("SELECT * FROM accounts WHERE profile_id = :profileId") public abstract List allForProfileSync(long profileId); + @Query("SELECT generation FROM accounts WHERE profile_id = :profileId LIMIT 1") + protected abstract AccountGenerationContainer getGenerationPOJOSync(long profileId); + public long getGenerationSync(long profileId) { + AccountGenerationContainer result = getGenerationPOJOSync(profileId); + + if (result == null) + return 0; + return result.generation; + } + @Query("DELETE FROM accounts WHERE profile_id = :profileId AND generation <> " + + ":currentGeneration") + public abstract void purgeOldAccountsSync(long profileId, long currentGeneration); + + @Query("DELETE FROM account_values WHERE EXISTS (SELECT 1 FROM accounts a WHERE a" + + ".id=account_values.account_id AND a.profile_id=:profileId) AND generation <> " + + ":currentGeneration") + public abstract void purgeOldAccountValuesSync(long profileId, long currentGeneration); + @Transaction + public void storeAccountsSync(List accounts, long profileId) { + long generation = getGenerationSync(profileId) + 1; + + for (AccountWithAmounts rec : accounts) { + rec.account.setGeneration(generation); + rec.account.setProfileId(profileId); + insertSync(rec); + } + purgeOldAccountsSync(profileId, generation); + purgeOldAccountValuesSync(profileId, generation); + } + static public class AccountNameContainer { @ColumnInfo public String name; @ColumnInfo public int ordering; } + + static class AccountGenerationContainer { + @ColumnInfo + long generation; + public AccountGenerationContainer(long generation) { + this.generation = generation; + } + } } diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/AccountValueDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/AccountValueDAO.java index 5ac559d6..54a42c12 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/AccountValueDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/AccountValueDAO.java @@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; +import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; @@ -31,7 +32,7 @@ import java.util.List; @Dao public abstract class AccountValueDAO extends BaseDAO { - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) public abstract long insertSync(AccountValue item); @Update diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/DescriptionHistoryDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/DescriptionHistoryDAO.java new file mode 100644 index 00000000..26d0cf40 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/dao/DescriptionHistoryDAO.java @@ -0,0 +1,77 @@ +/* + * Copyright © 2021 Damyan Ivanov. + * This file is part of MoLe. + * MoLe is free software: you can distribute it and/or modify it + * under the term of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your opinion), any later version. + * + * MoLe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License terms for details. + * + * You should have received a copy of the GNU General Public License + * along with MoLe. If not, see . + */ + +package net.ktnx.mobileledger.dao; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.room.ColumnInfo; +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import net.ktnx.mobileledger.db.DescriptionHistory; + +import java.util.ArrayList; +import java.util.List; + +@Dao +public abstract class DescriptionHistoryDAO extends BaseDAO { + static public List unbox(List list) { + ArrayList result = new ArrayList<>(list.size()); + for (DescriptionContainer item : list) { + result.add(item.description); + } + + return result; + } + @Insert + public abstract long insertSync(DescriptionHistory item); + + @Update + public abstract void updateSync(DescriptionHistory item); + + @Delete + public abstract void deleteSync(DescriptionHistory item); + + @Delete + public abstract void deleteSync(List items); + + @Query("DELETE FROM description_history where not exists (select 1 from transactions tr where" + + " upper(tr.description)=description_history.description_upper)") + public abstract void sweepSync(); + + @Query("SELECT * FROM description_history") + public abstract LiveData> getAll(); + + @Query("SELECT DISTINCT description, CASE WHEN description_upper LIKE :term||'%%' THEN 1 " + + " WHEN description_upper LIKE '%%:'||:term||'%%' THEN 2 " + + " WHEN description_upper LIKE '%% '||:term||'%%' THEN 3 " + + " ELSE 9 END AS ordering " + "FROM description_history " + + "WHERE description_upper LIKE '%%'||:term||'%%' " + + "ORDER BY ordering, description_upper, rowid ") + public abstract List lookupSync(@NonNull String term); + + static public class DescriptionContainer { + @ColumnInfo + public String description; + @ColumnInfo + public int ordering; + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java index ac51ecc2..b4eaf935 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java @@ -18,17 +18,24 @@ package net.ktnx.mobileledger.dao; import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; import net.ktnx.mobileledger.db.Profile; @Dao -abstract class ProfileDAO extends BaseDAO { - @Override +public abstract class ProfileDAO extends BaseDAO { + @Insert abstract long insertSync(Profile item); - @Override + @Update abstract void updateSync(Profile item); - @Override - abstract void deleteSync(Profile item); + @Delete + public abstract void deleteSync(Profile item); + + @Query("select * from profiles where id = :profileId") + public abstract Profile getByIdSync(long profileId); } diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java index 185caa18..485f65f1 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java @@ -27,6 +27,7 @@ import androidx.room.Query; import androidx.room.Update; import net.ktnx.mobileledger.db.Transaction; +import net.ktnx.mobileledger.db.TransactionWithAccounts; import java.util.ArrayList; import java.util.List; @@ -63,6 +64,10 @@ public abstract class TransactionDAO extends BaseDAO { @Query("SELECT * FROM transactions WHERE id = :id") public abstract LiveData getById(long id); + @androidx.room.Transaction + @Query("SELECT * FROM transactions WHERE id = :transactionId") + public abstract LiveData getByIdWithAccounts(long transactionId); + @Query("SELECT DISTINCT description, CASE WHEN description_upper LIKE :term||'%%' THEN 1 " + " WHEN description_upper LIKE '%%:'||:term||'%%' THEN 2 " + " WHEN description_upper LIKE '%% '||:term||'%%' THEN 3 " + diff --git a/app/src/main/java/net/ktnx/mobileledger/db/AccountValue.java b/app/src/main/java/net/ktnx/mobileledger/db/AccountValue.java index cc807564..2ebc3ec4 100644 --- a/app/src/main/java/net/ktnx/mobileledger/db/AccountValue.java +++ b/app/src/main/java/net/ktnx/mobileledger/db/AccountValue.java @@ -44,7 +44,7 @@ public class AccountValue { @ColumnInfo private float value; @ColumnInfo(defaultValue = "0") - private int generation = 0; + private long generation = 0; public long getId() { return id; } @@ -70,10 +70,10 @@ public class AccountValue { public void setValue(float value) { this.value = value; } - public int getGeneration() { + public long getGeneration() { return generation; } - public void setGeneration(int generation) { + public void setGeneration(long generation) { this.generation = generation; } } diff --git a/app/src/main/java/net/ktnx/mobileledger/db/DB.java b/app/src/main/java/net/ktnx/mobileledger/db/DB.java index c67e1a13..2e7ca655 100644 --- a/app/src/main/java/net/ktnx/mobileledger/db/DB.java +++ b/app/src/main/java/net/ktnx/mobileledger/db/DB.java @@ -31,7 +31,9 @@ import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.dao.AccountDAO; import net.ktnx.mobileledger.dao.AccountValueDAO; import net.ktnx.mobileledger.dao.CurrencyDAO; +import net.ktnx.mobileledger.dao.DescriptionHistoryDAO; import net.ktnx.mobileledger.dao.OptionDAO; +import net.ktnx.mobileledger.dao.ProfileDAO; import net.ktnx.mobileledger.dao.TemplateAccountDAO; import net.ktnx.mobileledger.dao.TemplateHeaderDAO; import net.ktnx.mobileledger.dao.TransactionDAO; @@ -175,4 +177,8 @@ abstract public class DB extends RoomDatabase { public abstract TransactionDAO getTransactionDAO(); public abstract OptionDAO getOptionDAO(); + + public abstract DescriptionHistoryDAO getDescriptionHistoryDAO(); + + public abstract ProfileDAO getProfileDAO(); } diff --git a/app/src/main/java/net/ktnx/mobileledger/db/TransactionWithAccounts.java b/app/src/main/java/net/ktnx/mobileledger/db/TransactionWithAccounts.java new file mode 100644 index 00000000..5e91e4e8 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/db/TransactionWithAccounts.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2021 Damyan Ivanov. + * This file is part of MoLe. + * MoLe is free software: you can distribute it and/or modify it + * under the term of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your opinion), any later version. + * + * MoLe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License terms for details. + * + * You should have received a copy of the GNU General Public License + * along with MoLe. If not, see . + */ + +package net.ktnx.mobileledger.db; + +import androidx.room.Embedded; +import androidx.room.Relation; + +import java.util.List; + +public class TransactionWithAccounts { + @Embedded + public Transaction transaction; + @Relation(parentColumn = "id", entityColumn = "transaction_id") + public List accounts; +} diff --git a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java index eef71a32..0016bb37 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java @@ -24,7 +24,6 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.Bundle; -import android.text.TextUtils; import android.util.SparseArray; import androidx.annotation.Nullable; @@ -34,8 +33,12 @@ import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.R; import net.ktnx.mobileledger.async.DbOpQueue; import net.ktnx.mobileledger.dao.AccountDAO; +import net.ktnx.mobileledger.dao.DescriptionHistoryDAO; import net.ktnx.mobileledger.dao.OptionDAO; +import net.ktnx.mobileledger.dao.ProfileDAO; import net.ktnx.mobileledger.dao.TransactionDAO; +import net.ktnx.mobileledger.db.AccountValue; +import net.ktnx.mobileledger.db.AccountWithAmounts; import net.ktnx.mobileledger.db.DB; import net.ktnx.mobileledger.json.API; import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity; @@ -49,7 +52,6 @@ import org.jetbrains.annotations.Contract; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -160,7 +162,7 @@ public final class MobileLedgerProfile { try { int orderNo = 0; for (MobileLedgerProfile p : Objects.requireNonNull(Data.profiles.getValue())) { - db.execSQL("update profiles set order_no=? where uuid=?", + db.execSQL("update profiles set order_no=? where id=?", new Object[]{orderNo, p.getId()}); p.orderNo = orderNo; orderNo++; @@ -384,49 +386,6 @@ public final class MobileLedgerProfile { }); // debug("accounts", String.format("Stored account '%s' in DB [%s]", acc.getName(), uuid)); } - public void storeAccountValue(SQLiteDatabase db, int generation, String name, String currency, - Float amount) { - if (!TextUtils.isEmpty(currency)) { - boolean exists; - try (Cursor c = db.rawQuery("select 1 from currencies where name=?", - new String[]{currency})) - { - exists = c.moveToFirst(); - } - if (!exists) { - db.execSQL( - "insert into currencies(id, name, position, has_gap) values((select max" + - "(id) from currencies)+1, ?, ?, ?)", new Object[]{currency, - Objects.requireNonNull( - Data.currencySymbolPosition.getValue()).toString(), - Data.currencyGap.getValue() - }); - } - } - - long accId = findAddAccount(db, name); - - db.execSQL("replace into account_values(account_id, " + - "currency, value, generation) values(?, ?, ?, ?);", - new Object[]{accId, Misc.emptyIsNull(currency), amount, generation}); - } - private long findAddAccount(SQLiteDatabase db, String accountName) { - try (Cursor c = db.rawQuery("select id from accounts where profile_id=? and name=?", - new String[]{String.valueOf(id), accountName})) - { - if (c.moveToFirst()) - return c.getLong(0); - - } - - try (Cursor c = db.rawQuery( - "insert into accounts(profile_id, name, name_upper) values(?, ?, ?) returning id", - new String[]{String.valueOf(id), accountName, accountName.toUpperCase()})) - { - c.moveToFirst(); - return c.getInt(0); - } - } public void storeTransaction(SQLiteDatabase db, int generation, LedgerTransaction tr) { tr.fillDataHash(); // Logger.debug("storeTransaction", String.format(Locale.US, "ID %d", tr.getId())); @@ -520,20 +479,9 @@ public final class MobileLedgerProfile { setOption(name, String.valueOf(value)); } public void removeFromDB() { - SQLiteDatabase db = App.getDatabase(); - debug("db", String.format(Locale.ROOT, "removing profile %d from DB", id)); - db.beginTransactionNonExclusive(); - try { - Object[] id_param = new Object[]{id}; - db.execSQL("delete from transactions where profile_id=?", id_param); - db.execSQL("delete from accounts where profile=?", id_param); - db.execSQL("delete from options where profile=?", id_param); - db.execSQL("delete from profiles where id=?", id_param); - db.setTransactionSuccessful(); - } - finally { - db.endTransaction(); - } + ProfileDAO dao = DB.get() + .getProfileDAO(); + AsyncTask.execute(() -> dao.deleteSync(dao.getByIdSync(id))); } public LedgerTransaction loadTransaction(int transactionId) { LedgerTransaction tr = new LedgerTransaction(transactionId, this.id); @@ -562,24 +510,6 @@ public final class MobileLedgerProfile { } return 1; } - private int getNextAccountsGeneration(SQLiteDatabase db) { - try (Cursor c = db.rawQuery("SELECT generation FROM accounts WHERE profile_id=? LIMIT 1", - new String[]{String.valueOf(id)})) - { - if (c.moveToFirst()) - return c.getInt(0) + 1; - } - return 1; - } - private void deleteNotPresentAccounts(SQLiteDatabase db, int generation) { - Logger.debug("db/benchmark", "Deleting obsolete accounts"); - db.execSQL("DELETE FROM account_values WHERE (select a.profile_id from accounts a where a" + - ".id=account_values.account_id)=? AND generation <> ?", - new Object[]{id, generation}); - db.execSQL("DELETE FROM accounts WHERE profile_id=? AND generation <> ?", - new Object[]{id, generation}); - Logger.debug("db/benchmark", "Done deleting obsolete accounts"); - } private void deleteNotPresentTransactions(SQLiteDatabase db, int generation) { Logger.debug("db/benchmark", "Deleting obsolete transactions"); db.execSQL( @@ -603,6 +533,10 @@ public final class MobileLedgerProfile { TransactionDAO trnDao = DB.get() .getTransactionDAO(); trnDao.deleteSync(trnDao.allForProfileSync(id)); + + DescriptionHistoryDAO descDao = DB.get() + .getDescriptionHistoryDAO(); + descDao.sweepSync(); } public void wipeAllData() { AsyncTask.execute(this::wipeAllDataSync); @@ -751,26 +685,10 @@ public final class MobileLedgerProfile { SQLiteDatabase db = App.getDatabase(); db.beginTransactionNonExclusive(); try { - int accountsGeneration = profile.getNextAccountsGeneration(db); - if (isInterrupted()) - return; - int transactionsGeneration = profile.getNextTransactionsGeneration(db); if (isInterrupted()) return; - for (LedgerAccount acc : accounts) { - profile.storeAccount(db, accountsGeneration, acc, false); - if (isInterrupted()) - return; - for (LedgerAmount amt : acc.getAmounts()) { - profile.storeAccountValue(db, accountsGeneration, acc.getName(), - amt.getCurrency(), amt.getAmount()); - if (isInterrupted()) - return; - } - } - for (LedgerTransaction tr : transactions) { profile.storeTransaction(db, transactionsGeneration, tr); if (isInterrupted()) @@ -781,9 +699,6 @@ public final class MobileLedgerProfile { if (isInterrupted()) { return; } - profile.deleteNotPresentAccounts(db, accountsGeneration); - if (isInterrupted()) - return; Map unique = new HashMap<>(); @@ -810,6 +725,37 @@ public final class MobileLedgerProfile { finally { db.endTransaction(); } + + AsyncTask.execute(() -> { + List list = new ArrayList<>(); + + final AccountDAO dao = DB.get() + .getAccountDAO(); + + for (LedgerAccount acc : accounts) { + AccountWithAmounts rec = new AccountWithAmounts(); + rec.account = acc.toDBO(); + + if (isInterrupted()) + return; + + rec.amounts = new ArrayList<>(); + for (LedgerAmount amt : acc.getAmounts()) { + AccountValue av = new AccountValue(); + av.setCurrency(amt.getCurrency()); + av.setValue(amt.getAmount()); + + rec.amounts.add(av); + } + + list.add(rec); + } + + if (isInterrupted()) + return; + + dao.storeAccountsSync(list, profile.getId()); + }); } private void storeDescription(SQLiteDatabase db, int generation, String description, String descriptionUpper) { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java index aec736c4..eb7a0641 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java @@ -18,10 +18,8 @@ package net.ktnx.mobileledger.ui; import android.os.AsyncTask; -import android.os.Build; import android.text.TextUtils; -import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; @@ -35,7 +33,6 @@ import net.ktnx.mobileledger.model.LedgerAccount; import net.ktnx.mobileledger.model.LedgerTransaction; import net.ktnx.mobileledger.model.MobileLedgerProfile; import net.ktnx.mobileledger.model.TransactionListItem; -import net.ktnx.mobileledger.utils.LockHolder; import net.ktnx.mobileledger.utils.Locker; import net.ktnx.mobileledger.utils.Logger; import net.ktnx.mobileledger.utils.MLDB; @@ -43,11 +40,8 @@ import net.ktnx.mobileledger.utils.SimpleDate; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Map; import static net.ktnx.mobileledger.utils.Logger.debug; @@ -61,65 +55,13 @@ public class MainModel extends ViewModel { new MutableLiveData<>(); private final Locker accountsLocker = new Locker(); private final MutableLiveData updateError = new MutableLiveData<>(); - private final Map accountMap = new HashMap<>(); private MobileLedgerProfile profile; - private List allAccounts = new ArrayList<>(); + private final List allAccounts = new ArrayList<>(); private SimpleDate firstTransactionDate; private SimpleDate lastTransactionDate; transient private RetrieveTransactionsTask retrieveTransactionsTask; transient private Thread displayedAccountsUpdater; private TransactionsDisplayedFilter displayedTransactionsUpdater; - public static ArrayList mergeAccountListsFromWeb(List oldList, - List newList) { - LedgerAccount oldAcc, newAcc; - ArrayList merged = new ArrayList<>(); - - Iterator oldIterator = oldList.iterator(); - Iterator newIterator = newList.iterator(); - - while (true) { - if (!oldIterator.hasNext()) { - // the rest of the incoming are new - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - newIterator.forEachRemaining(merged::add); - } - else { - while (newIterator.hasNext()) - merged.add(newIterator.next()); - } - break; - } - oldAcc = oldIterator.next(); - - if (!newIterator.hasNext()) { - // no more incoming accounts. ignore the rest of the old - break; - } - newAcc = newIterator.next(); - - // ignore now missing old items - if (oldAcc.getName() - .compareTo(newAcc.getName()) < 0) - continue; - - // add newly found items - if (oldAcc.getName() - .compareTo(newAcc.getName()) > 0) - { - merged.add(newAcc); - continue; - } - - // two items with same account names; forward-merge UI-controlled fields - // it is important that the result list contains a new LedgerAccount instance - // so that the change is propagated to the UI - newAcc.setExpanded(oldAcc.isExpanded()); - newAcc.setAmountsExpanded(oldAcc.amountsExpanded()); - merged.add(newAcc); - } - - return merged; - } private void setLastUpdateStamp(long transactionCount) { debug("db", "Updating transaction value stamp"); Date now = new Date(); @@ -200,13 +142,6 @@ public class MainModel extends ViewModel { accountsLocker.lockForWriting(); return accountsLocker; } - public void mergeAccountListFromWeb(List newList) { - - try (LockHolder l = accountsLocker.lockForWriting()) { - allAccounts = mergeAccountListsFromWeb(allAccounts, newList); - updateAccountsMap(allAccounts); - } - } public LiveData> getDisplayedAccounts() { return displayedAccounts; } @@ -216,18 +151,8 @@ public class MainModel extends ViewModel { setLastUpdateStamp(transactions.size()); - mergeAccountListFromWeb(accounts); - updateDisplayedAccounts(); - updateDisplayedTransactionsFromWeb(transactions); } - synchronized public void updateDisplayedAccounts() { - if (displayedAccountsUpdater != null) { - displayedAccountsUpdater.interrupt(); - } - displayedAccountsUpdater = new AccountListDisplayedFilter(this, allAccounts); - displayedAccountsUpdater.start(); - } synchronized public void updateDisplayedTransactionsFromWeb(List list) { if (displayedTransactionsUpdater != null) { displayedTransactionsUpdater.interrupt(); @@ -235,19 +160,6 @@ public class MainModel extends ViewModel { displayedTransactionsUpdater = new TransactionsDisplayedFilter(this, list); displayedTransactionsUpdater.start(); } - public List getAllAccounts() { - return allAccounts; - } - private void updateAccountsMap(List newAccounts) { - accountMap.clear(); - for (LedgerAccount acc : newAccounts) { - accountMap.put(acc.getName(), acc); - } - } - @Nullable - public LedgerAccount locateAccount(String name) { - return accountMap.get(name); - } public void clearUpdateError() { updateError.postValue(null); } @@ -255,38 +167,6 @@ public class MainModel extends ViewModel { public void clearTransactions() { displayedTransactions.setValue(new ArrayList<>()); } - static class AccountListDisplayedFilter extends Thread { - private final MainModel model; - private final List list; - AccountListDisplayedFilter(MainModel model, List list) { - this.model = model; - this.list = list; - } - @Override - public void run() { - List newDisplayed = new ArrayList<>(); - Logger.debug("dFilter", "waiting for synchronized block"); - Logger.debug("dFilter", String.format(Locale.US, - "entered synchronized block (about to examine %d accounts)", list.size())); - newDisplayed.add(new AccountListItem.Header(Data.lastAccountsUpdateText)); // header - - int count = 0; - for (LedgerAccount a : list) { - if (isInterrupted()) - return; - - if (a.isVisible()) { - newDisplayed.add(new AccountListItem.Account(a)); - count++; - } - } - if (!isInterrupted()) { - model.displayedAccounts.postValue(newDisplayed); - Data.lastUpdateAccountCount.postValue(count); - } - Logger.debug("dFilter", "left synchronized block"); - } - } static class TransactionsDisplayedFilter extends Thread { private final MainModel model;