+ mergeAccountList(list);
+ updateDisplayedAccounts();
+ }
+ public synchronized Locker lockAccountsForWriting() {
+ accountsLocker.lockForWriting();
+ return accountsLocker;
+ }
+ public void setAndStoreTransactionList(ArrayList<LedgerTransaction> list) {
+ storeTransactionListAsync(this, list);
+ SQLiteDatabase db = App.getDatabase();
+ db.beginTransactionNonExclusive();
+ try {
+ markTransactionsAsNotPresent(db);
+ for (LedgerTransaction tr : list)
+ storeTransaction(db, tr);
+ deleteNotPresentTransactions(db);
+ setLastUpdateStamp();
+ db.setTransactionSuccessful();
+ }
+ finally {
+ db.endTransaction();
+ }
+
+ allTransactions.postValue(list);
+ }
+ private void storeTransactionListAsync(MobileLedgerProfile mobileLedgerProfile,
+ List<LedgerTransaction> list) {
+ if (transactionListSaver != null)
+ transactionListSaver.interrupt();
+
+ transactionListSaver = new TransactionListSaver(this, list);
+ transactionListSaver.start();
+ }
+ public void setAndStoreAccountAndTransactionListFromWeb(List<LedgerAccount> accounts,
+ List<LedgerTransaction> transactions) {
+ storeAccountAndTransactionListAsync(accounts, transactions, false);
+
+ mergeAccountList(accounts);
+ updateDisplayedAccounts();
+
+ allTransactions.postValue(transactions);
+ }
+ private void storeAccountAndTransactionListAsync(List<LedgerAccount> accounts,
+ List<LedgerTransaction> transactions,
+ boolean storeAccUiFields) {
+ if (accountAndTransactionListSaver != null)
+ accountAndTransactionListSaver.interrupt();
+
+ accountAndTransactionListSaver =
+ new AccountAndTransactionListSaver(this, accounts, transactions, storeAccUiFields);
+ accountAndTransactionListSaver.start();
+ }
+ synchronized public void updateDisplayedAccounts() {
+ if (displayedAccountsUpdater != null) {
+ displayedAccountsUpdater.interrupt();
+ }
+ displayedAccountsUpdater = new AccountListDisplayedFilter(this, allAccounts);
+ displayedAccountsUpdater.start();
+ }
+ public List<LedgerAccount> getAllAccounts() {
+ return allAccounts;
+ }
+ private void updateAccountsMap(List<LedgerAccount> newAccounts) {
+ accountMap.clear();
+ for (LedgerAccount acc : newAccounts) {
+ accountMap.put(acc.getName(), acc);
+ }
+ }
+ @Nullable
+ public LedgerAccount locateAccount(String name) {
+ return accountMap.get(name);
+ }
+
+ public enum FutureDates {
+ None(0), OneWeek(7), TwoWeeks(14), OneMonth(30), TwoMonths(60), ThreeMonths(90),
+ SixMonths(180), OneYear(365), All(-1);
+ private static SparseArray<FutureDates> map = new SparseArray<>();
+
+ static {
+ for (FutureDates item : FutureDates.values()) {
+ map.put(item.value, item);
+ }
+ }
+
+ private int value;
+ FutureDates(int value) {
+ this.value = value;
+ }
+ public static FutureDates valueOf(int i) {
+ return map.get(i, None);
+ }
+ public int toInt() {
+ return this.value;
+ }
+ public String getText(Resources resources) {
+ switch (value) {
+ case 7:
+ return resources.getString(R.string.future_dates_7);
+ case 14:
+ return resources.getString(R.string.future_dates_14);
+ case 30:
+ return resources.getString(R.string.future_dates_30);
+ case 60:
+ return resources.getString(R.string.future_dates_60);
+ case 90:
+ return resources.getString(R.string.future_dates_90);
+ case 180:
+ return resources.getString(R.string.future_dates_180);
+ case 365:
+ return resources.getString(R.string.future_dates_365);
+ case -1:
+ return resources.getString(R.string.future_dates_all);
+ default:
+ return resources.getString(R.string.future_dates_none);
+ }
+ }
+ }
+
+ static class AccountListLoader extends Thread {
+ MobileLedgerProfile profile;
+ AccountListLoader(MobileLedgerProfile profile) {
+ this.profile = profile;
+ }
+ @Override
+ public void run() {
+ Logger.debug("async-acc", "AccountListLoader::run() entered");
+ String profileUUID = profile.getUuid();
+ ArrayList<LedgerAccount> list = new ArrayList<>();
+ HashMap<String, LedgerAccount> map = new HashMap<>();
+
+ String sql = "SELECT a.name, a.expanded, a.amounts_expanded";
+ sql += " from accounts a WHERE a.profile = ?";
+ sql += " ORDER BY a.name";
+
+ SQLiteDatabase db = App.getDatabase();
+ Logger.debug("async-acc", "AccountListLoader::run() connected to DB");
+ try (Cursor cursor = db.rawQuery(sql, new String[]{profileUUID})) {
+ Logger.debug("async-acc", "AccountListLoader::run() executed query");
+ while (cursor.moveToNext()) {
+ if (isInterrupted())
+ return;
+
+ final String accName = cursor.getString(0);
+// debug("accounts",
+// String.format("Read account '%s' from DB [%s]", accName,
+// profileUUID));
+ String parentName = LedgerAccount.extractParentName(accName);
+ LedgerAccount parent;
+ if (parentName != null) {
+ parent = map.get(parentName);
+ if (parent == null)
+ throw new IllegalStateException(
+ String.format("Can't load account '%s': parent '%s' not loaded",
+ accName, parentName));
+ parent.setHasSubAccounts(true);
+ }
+ else
+ parent = null;
+
+ LedgerAccount acc = new LedgerAccount(profile, accName, parent);
+ acc.setExpanded(cursor.getInt(1) == 1);
+ acc.setAmountsExpanded(cursor.getInt(2) == 1);
+ acc.setHasSubAccounts(false);
+
+ try (Cursor c2 = db.rawQuery(
+ "SELECT value, currency FROM account_values WHERE profile = ?" + " " +
+ "AND account = ?", new String[]{profileUUID, accName}))
+ {
+ while (c2.moveToNext()) {
+ acc.addAmount(c2.getFloat(0), c2.getString(1));
+ }
+ }
+
+ list.add(acc);
+ map.put(accName, acc);
+ }
+ Logger.debug("async-acc", "AccountListLoader::run() query execution done");
+ }
+
+ if (isInterrupted())
+ return;
+
+ Logger.debug("async-acc", "AccountListLoader::run() posting new list");
+ profile.allAccounts = list;
+ profile.updateAccountsMap(list);
+ profile.updateDisplayedAccounts();
+ }
+ }
+
+ static class AccountListDisplayedFilter extends Thread {
+ private final MobileLedgerProfile profile;
+ private final List<LedgerAccount> list;
+ AccountListDisplayedFilter(MobileLedgerProfile profile, List<LedgerAccount> list) {
+ this.profile = profile;
+ this.list = list;
+ }
+ @Override
+ public void run() {
+ List<LedgerAccount> 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()));
+ for (LedgerAccount a : list) {
+ if (isInterrupted()) {
+ return;
+ }
+
+ if (a.isVisible()) {
+ newDisplayed.add(a);
+ }
+ }
+ if (!isInterrupted()) {
+ profile.displayedAccounts.postValue(newDisplayed);
+ }
+ Logger.debug("dFilter", "left synchronized block");
+ }
+ }
+
+ private static class AccountListSaver extends Thread {
+ private final MobileLedgerProfile profile;
+ private final List<LedgerAccount> list;
+ private final boolean storeUiFields;
+ AccountListSaver(MobileLedgerProfile profile, List<LedgerAccount> list,
+ boolean storeUiFields) {
+ this.list = list;
+ this.profile = profile;
+ this.storeUiFields = storeUiFields;
+ }
+ @Override
+ public void run() {
+ SQLiteDatabase db = App.getDatabase();
+ db.beginTransactionNonExclusive();
+ try {
+ profile.markAccountsAsNotPresent(db);
+ if (isInterrupted())
+ return;
+ for (LedgerAccount acc : list) {
+ profile.storeAccount(db, acc, storeUiFields);
+ if (isInterrupted())
+ return;
+ }
+ profile.deleteNotPresentAccounts(db);
+ if (isInterrupted())
+ return;
+ profile.setLastUpdateStamp();
+ db.setTransactionSuccessful();
+ }
+ finally {
+ db.endTransaction();
+ }
+ }
+ }
+
+ private static class TransactionListSaver extends Thread {
+ private final MobileLedgerProfile profile;
+ private final List<LedgerTransaction> list;
+ TransactionListSaver(MobileLedgerProfile profile, List<LedgerTransaction> list) {
+ this.list = list;
+ this.profile = profile;
+ }
+ @Override
+ public void run() {
+ SQLiteDatabase db = App.getDatabase();
+ db.beginTransactionNonExclusive();
+ try {
+ profile.markTransactionsAsNotPresent(db);
+ if (isInterrupted())
+ return;
+ for (LedgerTransaction tr : list) {
+ profile.storeTransaction(db, tr);
+ if (isInterrupted())
+ return;
+ }
+ profile.deleteNotPresentTransactions(db);
+ if (isInterrupted())
+ return;
+ profile.setLastUpdateStamp();
+ db.setTransactionSuccessful();
+ }
+ finally {
+ db.endTransaction();
+ }
+ }
+ }
+
+ private static class AccountAndTransactionListSaver extends Thread {
+ private final MobileLedgerProfile profile;
+ private final List<LedgerAccount> accounts;
+ private final List<LedgerTransaction> transactions;
+ private final boolean storeAccUiFields;
+ AccountAndTransactionListSaver(MobileLedgerProfile profile, List<LedgerAccount> accounts,
+ List<LedgerTransaction> transactions,
+ boolean storeAccUiFields) {
+ this.accounts = accounts;
+ this.transactions = transactions;
+ this.profile = profile;
+ this.storeAccUiFields = storeAccUiFields;
+ }
+ @Override
+ public void run() {
+ SQLiteDatabase db = App.getDatabase();
+ db.beginTransactionNonExclusive();
+ try {
+ profile.markAccountsAsNotPresent(db);
+ if (isInterrupted())
+ return;
+
+ profile.markTransactionsAsNotPresent(db);
+ if (isInterrupted()) {
+ return;
+ }
+
+ for (LedgerAccount acc : accounts) {
+ profile.storeAccount(db, acc, storeAccUiFields);
+ if (isInterrupted())
+ return;
+ }
+
+ for (LedgerTransaction tr : transactions) {
+ profile.storeTransaction(db, tr);
+ if (isInterrupted()) {
+ return;
+ }
+ }
+
+ profile.deleteNotPresentAccounts(db);
+ if (isInterrupted()) {
+ return;
+ }
+ profile.deleteNotPresentTransactions(db);
+ if (isInterrupted())
+ return;
+
+ profile.setLastUpdateStamp();
+
+ db.setTransactionSuccessful();
+ }
+ finally {
+ db.endTransaction();
+ }
+ }