]> git.ktnx.net Git - mobile-ledger.git/commitdiff
Room-based profile management
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 15 Apr 2021 20:07:38 +0000 (20:07 +0000)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 15 Apr 2021 20:07:38 +0000 (20:07 +0000)
MobileLedgerProfile is gone, Room spreads gradually

53 files changed:
app/src/main/java/net/ktnx/mobileledger/App.java
app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java
app/src/main/java/net/ktnx/mobileledger/async/SendTransactionTask.java
app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java
app/src/main/java/net/ktnx/mobileledger/async/UpdateTransactionsTask.java
app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java
app/src/main/java/net/ktnx/mobileledger/dao/CurrencyDAO.java
app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java
app/src/main/java/net/ktnx/mobileledger/dao/TransactionAccountDAO.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java
app/src/main/java/net/ktnx/mobileledger/db/AccountAutocompleteAdapter.java
app/src/main/java/net/ktnx/mobileledger/db/DB.java
app/src/main/java/net/ktnx/mobileledger/db/TemplateAccount.java
app/src/main/java/net/ktnx/mobileledger/db/TemplateHeader.java
app/src/main/java/net/ktnx/mobileledger/db/TemplateWithAccounts.java
app/src/main/java/net/ktnx/mobileledger/db/Transaction.java
app/src/main/java/net/ktnx/mobileledger/db/TransactionAccount.java
app/src/main/java/net/ktnx/mobileledger/model/Currency.java
app/src/main/java/net/ktnx/mobileledger/model/Data.java
app/src/main/java/net/ktnx/mobileledger/model/FutureDates.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java
app/src/main/java/net/ktnx/mobileledger/model/LedgerTransaction.java
app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java
app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java [deleted file]
app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorModel.java
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorRecyclerViewAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/DatePickerFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java
app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencyLongClickListener.java
app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencySelectedListener.java
app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/ProfileThemedActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionAccountRowItemHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionHeaderItemHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemViewHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemsAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfileDetailActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfileDetailFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfileDetailModel.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfilesRecyclerViewAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsViewModel.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListFragment.java
app/src/main/java/net/ktnx/mobileledger/utils/Colors.java
app/src/main/java/net/ktnx/mobileledger/utils/NetworkUtil.java
app/src/test/java/net/ktnx/mobileledger/model/MobileLedgerProfileTest.java [deleted file]

index a781ccf105bebc377117bd8d5744ce79e2f8c00d..d6197c568a63a5a98e78f93c42db8c5aff3b4468 100644 (file)
@@ -64,11 +64,9 @@ public class App extends Application {
         profileModel = null;
     }
     public static void storeStartupProfileAndTheme(long currentProfileId, int currentTheme) {
-        SharedPreferences prefs =
-                instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+        SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
         SharedPreferences.Editor editor = prefs.edit();
-        editor.putLong(PREF_PROFILE_ID,
-                currentProfileId);
+        editor.putLong(PREF_PROFILE_ID, currentProfileId);
         editor.putInt(PREF_THEME_HUE, currentTheme);
         editor.apply();
     }
@@ -90,7 +88,7 @@ public class App extends Application {
         if (profileModel != null)
             return profileModel.getAuthUserName();
         return Data.getProfile()
-                   .getAuthUserName();
+                   .getAuthUser();
     }
     private String getAuthPassword() {
         if (profileModel != null)
@@ -102,7 +100,7 @@ public class App extends Application {
         if (profileModel != null)
             return profileModel.getUseAuthentication();
         return Data.getProfile()
-                   .isAuthEnabled();
+                   .useAuthentication();
     }
     @Override
     public void onCreate() {
index aa47d73d403cc456bf6fca05a7046e8dbd421241..8b795ac1296ab703a74918a3a53393ba7418882f 100644 (file)
@@ -23,10 +23,22 @@ import android.os.AsyncTask;
 import android.os.OperationCanceledException;
 
 import androidx.annotation.NonNull;
+import androidx.room.Transaction;
 
 import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
 
 import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.dao.AccountDAO;
+import net.ktnx.mobileledger.dao.AccountValueDAO;
+import net.ktnx.mobileledger.dao.TransactionAccountDAO;
+import net.ktnx.mobileledger.dao.TransactionDAO;
+import net.ktnx.mobileledger.db.Account;
+import net.ktnx.mobileledger.db.AccountWithAmounts;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Option;
+import net.ktnx.mobileledger.db.Profile;
+import net.ktnx.mobileledger.db.TransactionAccount;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.err.HTTPException;
 import net.ktnx.mobileledger.json.API;
 import net.ktnx.mobileledger.json.AccountListParser;
@@ -36,9 +48,9 @@ import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerAccount;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.MainModel;
 import net.ktnx.mobileledger.utils.Logger;
+import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.NetworkUtil;
 
 import java.io.BufferedReader;
@@ -52,6 +64,7 @@ import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -81,15 +94,11 @@ public class RetrieveTransactionsTask extends
     private final Pattern reAccountValue = Pattern.compile(
             "<span class=\"[^\"]*\\bamount\\b[^\"]*\">\\s*([-+]?[\\d.,]+)(?:\\s+(\\S+))?</span>");
     private final MainModel mainModel;
-    private final MobileLedgerProfile profile;
-    private final List<LedgerAccount> prevAccounts;
+    private final Profile profile;
     private int expectedPostingsCount = -1;
-    public RetrieveTransactionsTask(@NonNull MainModel mainModel,
-                                    @NonNull MobileLedgerProfile profile,
-                                    List<LedgerAccount> accounts) {
+    public RetrieveTransactionsTask(@NonNull MainModel mainModel, @NonNull Profile profile) {
         this.mainModel = mainModel;
         this.profile = profile;
-        this.prevAccounts = accounts;
     }
     private static void L(String msg) {
         //debug("transaction-parser", msg);
@@ -325,7 +334,7 @@ public class RetrieveTransactionsTask extends
 
                             state = ParserState.EXPECTING_TRANSACTION;
                             L(String.format("transaction %s parsed → expecting transaction",
-                                    transaction.getId()));
+                                    transaction.getLedgerId()));
 
 // sounds like a good idea, but transaction-1 may not be the first one chronologically
 // for example, when you add the initial seeding transaction after entering some others
@@ -340,8 +349,9 @@ public class RetrieveTransactionsTask extends
                             LedgerTransactionAccount lta = parseTransactionAccountLine(line);
                             if (lta != null) {
                                 transaction.addAccount(lta);
-                                L(String.format(Locale.ENGLISH, "%d: %s = %s", transaction.getId(),
-                                        lta.getAccountName(), lta.getAmount()));
+                                L(String.format(Locale.ENGLISH, "%d: %s = %s",
+                                        transaction.getLedgerId(), lta.getAccountName(),
+                                        lta.getAmount()));
                             }
                             else
                                 throw new IllegalStateException(
@@ -384,7 +394,7 @@ public class RetrieveTransactionsTask extends
     }
     private List<LedgerAccount> retrieveAccountList()
             throws IOException, HTTPException, ApiNotSupportedException {
-        final API apiVersion = profile.getApiVersion();
+        final API apiVersion = API.valueOf(profile.getApiVersion());
         if (apiVersion.equals(API.auto)) {
             return retrieveAccountListAnyVersion();
         }
@@ -430,9 +440,6 @@ public class RetrieveTransactionsTask extends
         SQLiteDatabase db = App.getDatabase();
         ArrayList<LedgerAccount> list = new ArrayList<>();
         HashMap<String, LedgerAccount> map = new HashMap<>();
-        HashMap<String, LedgerAccount> currentMap = new HashMap<>();
-        for (LedgerAccount acc : prevAccounts)
-            currentMap.put(acc.getName(), acc);
         throwIfCancelled();
         try (InputStream resp = http.getInputStream()) {
             throwIfCancelled();
@@ -452,20 +459,11 @@ public class RetrieveTransactionsTask extends
             throwIfCancelled();
         }
 
-        // the current account tree may have changed, update the new-to be tree to match
-        for (LedgerAccount acc : list) {
-            LedgerAccount prevData = currentMap.get(acc.getName());
-            if (prevData != null) {
-                acc.setExpanded(prevData.isExpanded());
-                acc.setAmountsExpanded(prevData.amountsExpanded());
-            }
-        }
-
         return list;
     }
     private List<LedgerTransaction> retrieveTransactionList()
             throws ParseException, HTTPException, IOException, ApiNotSupportedException {
-        final API apiVersion = profile.getApiVersion();
+        final API apiVersion = API.valueOf(profile.getApiVersion());
         if (apiVersion.equals(API.auto)) {
             return retrieveTransactionListAnyVersion();
         }
@@ -552,7 +550,7 @@ public class RetrieveTransactionsTask extends
                         .compareTo(o1.getDate());
             if (res != 0)
                 return res;
-            return Long.compare(o2.getId(), o1.getId());
+            return Long.compare(o2.getLedgerId(), o1.getLedgerId());
         });
         return trList;
     }
@@ -579,7 +577,10 @@ public class RetrieveTransactionsTask extends
                 transactions = new ArrayList<>();
                 retrieveTransactionListLegacy(accounts, transactions);
             }
-            mainModel.setAndStoreAccountAndTransactionListFromWeb(accounts, transactions);
+
+            storeAccountsAndTransactions(accounts, transactions);
+
+            mainModel.updateDisplayedTransactionsFromWeb(transactions);
 
             return new Result(accounts, transactions);
         }
@@ -616,6 +617,55 @@ public class RetrieveTransactionsTask extends
             Data.backgroundTaskFinished();
         }
     }
+    @Transaction
+    private void storeAccountsAndTransactions(List<LedgerAccount> accounts,
+                                              List<LedgerTransaction> transactions) {
+        AccountDAO accDao = DB.get()
+                              .getAccountDAO();
+        TransactionDAO trDao = DB.get()
+                                 .getTransactionDAO();
+        TransactionAccountDAO trAccDao = DB.get()
+                                           .getTransactionAccountDAO();
+        AccountValueDAO valDao = DB.get()
+                                   .getAccountValueDAO();
+
+        final List<AccountWithAmounts> list = new ArrayList<>();
+        for (LedgerAccount acc : accounts) {
+            final AccountWithAmounts a = acc.toDBOWithAmounts();
+            Account existing = accDao.getByNameSync(profile.getId(), acc.getName());
+            if (existing != null) {
+                a.account.setExpanded(existing.isExpanded());
+                a.account.setAmountsExpanded(existing.isAmountsExpanded());
+                a.account.setId(
+                        existing.getId()); // not strictly needed, but since we have it anyway...
+            }
+
+            list.add(a);
+        }
+        accDao.storeAccountsSync(list, profile.getId());
+
+        long trGen = trDao.getGenerationSync(profile.getId());
+        for (LedgerTransaction tr : transactions) {
+            TransactionWithAccounts tran = tr.toDBO();
+            tran.transaction.setGeneration(trGen);
+            tran.transaction.setProfileId(profile.getId());
+
+            tran.transaction.setId(trDao.insertSync(tran.transaction));
+
+            for (TransactionAccount trAcc : tran.accounts) {
+                trAcc.setGeneration(trGen);
+                trAcc.setTransactionId(tran.transaction.getId());
+                trAcc.setId(trAccDao.insertSync(trAcc));
+            }
+        }
+
+        trDao.purgeOldTransactionsSync(profile.getId(), trGen);
+
+        DB.get()
+          .getOptionDAO()
+          .insertSync(new Option(profile.getId(), MLDB.OPT_LAST_SCRAPE,
+                  String.valueOf((new Date()).getTime())));
+    }
     public void throwIfCancelled() {
         if (isCancelled())
             throw new OperationCanceledException(null);
index dbe4bc88647d87b129315d3d3edaff076028ba37..f7370adb506528e6336a55e94e08b3e26529013c 100644 (file)
@@ -20,12 +20,12 @@ package net.ktnx.mobileledger.async;
 import android.os.AsyncTask;
 import android.util.Log;
 
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.json.API;
 import net.ktnx.mobileledger.json.ApiNotSupportedException;
 import net.ktnx.mobileledger.json.Gateway;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.NetworkUtil;
@@ -56,20 +56,20 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
 
 public class SendTransactionTask extends AsyncTask<LedgerTransaction, Void, Void> {
     private final TaskCallback taskCallback;
-    private final MobileLedgerProfile mProfile;
+    private final Profile mProfile;
     private final boolean simulate;
     protected String error;
     private String token;
     private String session;
     private LedgerTransaction transaction;
 
-    public SendTransactionTask(TaskCallback callback, MobileLedgerProfile profile,
+    public SendTransactionTask(TaskCallback callback, Profile profile,
                                boolean simulate) {
         taskCallback = callback;
         mProfile = profile;
         this.simulate = simulate;
     }
-    public SendTransactionTask(TaskCallback callback, MobileLedgerProfile profile) {
+    public SendTransactionTask(TaskCallback callback, Profile profile) {
         taskCallback = callback;
         mProfile = profile;
         simulate = false;
@@ -253,7 +253,7 @@ public class SendTransactionTask extends AsyncTask<LedgerTransaction, Void, Void
         try {
             transaction = ledgerTransactions[0];
 
-            final API profileApiVersion = mProfile.getApiVersion();
+            final API profileApiVersion = API.valueOf(mProfile.getApiVersion());
             switch (profileApiVersion) {
                 case auto:
                     boolean sendOK = false;
index e7d21ed202a0973a3ad95bbec797421f7a13a28c..89fa2edd19d84b6c4f7e318980e6bde1a7739903 100644 (file)
@@ -30,7 +30,6 @@ public class TransactionAccumulator {
     private SimpleDate earliestDate, latestDate;
     private SimpleDate lastDate;
     private boolean done;
-    private int transactionCount = 0;
     public TransactionAccumulator(MainModel model) {
         this.model = model;
 
@@ -55,11 +54,10 @@ public class TransactionAccumulator {
         list.add(new TransactionListItem(transaction));
 
         lastDate = date;
-        transactionCount++;
     }
     public void done() {
         done = true;
-        model.setDisplayedTransactions(list, transactionCount);
+        model.setDisplayedTransactions(list);
         model.setFirstTransactionDate(earliestDate);
         model.setLastTransactionDate(latestDate);
     }
index c7f0ad828e43a89c6b40dcca98a7f759acfedea8..06bec67ef73d84cf6a355eac0c3d10977e562aba 100644 (file)
@@ -22,9 +22,9 @@ import android.database.sqlite.SQLiteDatabase;
 import android.os.AsyncTask;
 
 import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransaction;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.MainModel;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
@@ -32,7 +32,7 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
 
 public class UpdateTransactionsTask extends AsyncTask<MainModel, Void, String> {
     protected String doInBackground(MainModel[] model) {
-        final MobileLedgerProfile profile = Data.getProfile();
+        final Profile profile = Data.getProfile();
 
         long profile_id = profile.getId();
         Data.backgroundTaskStarted();
index 7bca44ced51ec35f0658b08fd0730a08b4eff105..f37b940b1df9e9377325374e277f5ba96107c3cf 100644 (file)
@@ -59,16 +59,7 @@ public abstract class AccountDAO extends BaseDAO<Account> {
         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);
-        }
+        account.setId(insertSync(account));
         for (AccountValue value : accountWithAmounts.amounts) {
             value.setAccountId(account.getId());
             value.setGeneration(account.getGeneration());
index f650ff2d7a8ca307c5f48c5a38d98f2268e3d948..b9d186166495985d1565dd752614467b0a14ee6e 100644 (file)
@@ -21,6 +21,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;
 
@@ -29,21 +30,27 @@ import net.ktnx.mobileledger.db.Currency;
 import java.util.List;
 
 @Dao
-public interface CurrencyDAO {
-    @Insert
-    void insert(Currency... items);
+public abstract class CurrencyDAO extends BaseDAO<Currency> {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    abstract long insertSync(Currency item);
 
     @Update
-    void update(Currency... items);
+    abstract void updateSync(Currency item);
 
     @Delete
-    void delete(Currency item);
+    public abstract void deleteSync(Currency item);
 
     @Query("SELECT * FROM currencies")
-    LiveData<List<Currency>> getCurrencies();
+    public abstract LiveData<List<Currency>> getAll();
 
     @Query("SELECT * FROM currencies WHERE id = :id")
-    LiveData<Currency> getCurrencyById(Long id);
+    abstract LiveData<Currency> getById(long id);
+
+    @Query("SELECT * FROM currencies WHERE id = :id")
+    public abstract Currency getByIdSync(long id);
+
+    @Query("SELECT * FROM currencies WHERE name = :name")
+    public abstract LiveData<Currency> getByName(String name);
 
 //    not useful for now
 //    @Transaction
index 52071745c4fc1eb35d620e0c7e8e023bb8f938e0..77697230d60fd0e97d4c6d3bd9b756cdb54f5023 100644 (file)
 
 package net.ktnx.mobileledger.dao;
 
+import android.os.AsyncTask;
+
 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.Transaction;
 import androidx.room.Update;
 
 import net.ktnx.mobileledger.db.Profile;
 
+import java.util.List;
+
 @Dao
 public abstract class ProfileDAO extends BaseDAO<Profile> {
-    @Insert
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
     abstract long insertSync(Profile item);
 
+    @Transaction
+    public long insertLastSync(Profile item) {
+        int count = getProfileCountSync();
+        item.setOrderNo(count + 1);
+        return insertSync(item);
+    }
+    public void insertLast(Profile item, OnInsertedReceiver onInsertedReceiver) {
+        AsyncTask.execute(() -> {
+            long id = insertLastSync(item);
+            if (onInsertedReceiver != null)
+                onInsertedReceiver.onInsert(id);
+        });
+    }
+
     @Update
     abstract void updateSync(Profile item);
 
@@ -43,6 +63,32 @@ public abstract class ProfileDAO extends BaseDAO<Profile> {
     @Query("SELECT * FROM profiles WHERE id=:profileId")
     public abstract LiveData<Profile> getById(long profileId);
 
+    @Query("SELECT * FROM profiles ORDER BY order_no")
+    public abstract List<Profile> getAllOrderedSync();
+
+    @Query("SELECT * FROM profiles ORDER BY order_no")
+    public abstract LiveData<List<Profile>> getAllOrdered();
+
     @Query("SELECT * FROM profiles LIMIT 1")
     public abstract Profile getAnySync();
+
+    @Query("SELECT MAX(order_no) FROM profiles")
+    public abstract int getProfileCountSync();
+    public void updateOrderSync(List<Profile> list) {
+        if (list == null)
+            list = getAllOrderedSync();
+        int order = 1;
+        for (Profile p : list) {
+            p.setOrderNo(order++);
+            updateSync(p);
+        }
+    }
+    public void updateOrder(List<Profile> list, Runnable onDone) {
+        AsyncTask.execute(() -> {
+            updateOrderSync(list);
+            if (onDone != null)
+                onDone.run();
+
+        });
+    }
 }
diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionAccountDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionAccountDAO.java
new file mode 100644 (file)
index 0000000..eae2626
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.dao;
+
+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;
+
+import net.ktnx.mobileledger.db.TransactionAccount;
+
+import java.util.List;
+
+@Dao
+public abstract class TransactionAccountDAO extends BaseDAO<TransactionAccount> {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    public abstract long insertSync(TransactionAccount item);
+
+    @Update
+    public abstract void updateSync(TransactionAccount item);
+
+    @Delete
+    public abstract void deleteSync(TransactionAccount item);
+
+    @Delete
+    public abstract void deleteSync(List<TransactionAccount> items);
+
+    @Query("SELECT * FROM transaction_accounts WHERE id = :id")
+    public abstract LiveData<TransactionAccount> getById(long id);
+}
index 485f65f10ecdce2ef5847bdc5bbefd35c22da3b2..c03da12b1649befe77e421e5c7f9e8bb1153cd2c 100644 (file)
@@ -23,6 +23,7 @@ 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.Update;
 
@@ -42,7 +43,7 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
 
         return result;
     }
-    @Insert
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
     public abstract long insertSync(Transaction item);
 
     @Update
@@ -68,6 +69,10 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
     @Query("SELECT * FROM transactions WHERE id = :transactionId")
     public abstract LiveData<TransactionWithAccounts> getByIdWithAccounts(long transactionId);
 
+    @androidx.room.Transaction
+    @Query("SELECT * FROM transactions WHERE id = :transactionId")
+    public abstract TransactionWithAccounts getByIdWithAccountsSync(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 " +
@@ -76,9 +81,41 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
            "ORDER BY ordering, description_upper, rowid ")
     public abstract List<DescriptionContainer> lookupDescriptionSync(@NonNull String term);
 
+    @androidx.room.Transaction
+    @Query("SELECT * from transactions WHERE description = :description ORDER BY year desc, month" +
+           " desc, day desc LIMIT 1")
+    public abstract TransactionWithAccounts getFirstByDescriptionSync(@NonNull String description);
+
+    @androidx.room.Transaction
+    @Query("SELECT * from transactions tr JOIN transaction_accounts t_a ON t_a.transaction_id = " +
+           "tr.id WHERE tr.description = :description AND t_a.account_name LIKE " +
+           "'%'||:accountTerm||'%' ORDER BY year desc, month desc, day desc LIMIT 1")
+    public abstract TransactionWithAccounts getFirstByDescriptionHavingAccountSync(
+            @NonNull String description, @NonNull String accountTerm);
+
     @Query("SELECT * from transactions WHERE profile_id = :profileId")
     public abstract List<Transaction> allForProfileSync(long profileId);
 
+    @Query("SELECT generation FROM transactions WHERE profile_id = :profileId LIMIT 1")
+    protected abstract TransactionGenerationContainer getGenerationPOJOSync(long profileId);
+    public long getGenerationSync(long profileId) {
+        TransactionGenerationContainer result = getGenerationPOJOSync(profileId);
+
+        if (result == null)
+            return 0;
+        return result.generation;
+    }
+    @Query("DELETE FROM transactions WHERE profile_id = :profileId AND generation <> " +
+           ":currentGeneration")
+    public abstract void purgeOldTransactionsSync(long profileId, long currentGeneration);
+    static class TransactionGenerationContainer {
+        @ColumnInfo
+        long generation;
+        public TransactionGenerationContainer(long generation) {
+            this.generation = generation;
+        }
+    }
+
     static public class DescriptionContainer {
         @ColumnInfo
         public String description;
index 42bf4c6b50f4e9579fcf4e894d7f97be85ecc564..9b6d41ba9505ccc413e0c9f4982289333ef34fd9 100644 (file)
@@ -24,7 +24,6 @@ import android.widget.Filter;
 import androidx.annotation.NonNull;
 
 import net.ktnx.mobileledger.dao.AccountDAO;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.utils.Logger;
 
 import java.util.ArrayList;
@@ -38,7 +37,7 @@ public class AccountAutocompleteAdapter extends ArrayAdapter<String> {
     public AccountAutocompleteAdapter(Context context) {
         super(context, android.R.layout.simple_dropdown_item_1line, new ArrayList<>());
     }
-    public AccountAutocompleteAdapter(Context context, @NonNull MobileLedgerProfile profile) {
+    public AccountAutocompleteAdapter(Context context, @NonNull Profile profile) {
         this(context);
         profileId = profile.getId();
     }
index 683d54b50d774cd28a045da3c873175ac5563615..41b6f7c0ba2110af2d41c1b3b40b71bf6b6e82b2 100644 (file)
@@ -37,6 +37,7 @@ 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.TransactionAccountDAO;
 import net.ktnx.mobileledger.dao.TransactionDAO;
 import net.ktnx.mobileledger.utils.Logger;
 
@@ -206,6 +207,8 @@ abstract public class DB extends RoomDatabase {
 
     public abstract TransactionDAO getTransactionDAO();
 
+    public abstract TransactionAccountDAO getTransactionAccountDAO();
+
     public abstract OptionDAO getOptionDAO();
 
     public abstract DescriptionHistoryDAO getDescriptionHistoryDAO();
index b17250917cac753d482d758f4b8c5abc13b9b336..e8f2448615b9f06760ca9c92f5b2bc9e1f986c15 100644 (file)
@@ -38,11 +38,9 @@ import org.jetbrains.annotations.NotNull;
 })
 public class TemplateAccount extends TemplateBase {
     @PrimaryKey(autoGenerate = true)
-    @NotNull
-    private Long id;
-    @NonNull
+    private long id;
     @ColumnInfo(name = "template_id")
-    private Long templateId;
+    private long templateId;
     @ColumnInfo(name = "acc")
     private String accountName;
     @ColumnInfo(name = "position")
@@ -50,8 +48,8 @@ public class TemplateAccount extends TemplateBase {
     private Long position;
     @ColumnInfo(name = "acc_match_group")
     private Integer accountNameMatchGroup;
-    @ColumnInfo(name = "currency")
-    private Integer currency;
+    @ColumnInfo
+    private Long currency;
     @ColumnInfo(name = "currency_match_group")
     private Integer currencyMatchGroup;
     @ColumnInfo(name = "amount")
@@ -83,10 +81,10 @@ public class TemplateAccount extends TemplateBase {
         accountCommentMatchGroup = o.accountCommentMatchGroup;
         negateAmount = o.negateAmount;
     }
-    public Long getId() {
+    public long getId() {
         return id;
     }
-    public void setId(Long id) {
+    public void setId(long id) {
         this.id = id;
     }
     public Boolean getNegateAmount() {
@@ -124,12 +122,19 @@ public class TemplateAccount extends TemplateBase {
     public void setAccountNameMatchGroup(Integer accountNameMatchGroup) {
         this.accountNameMatchGroup = accountNameMatchGroup;
     }
-    public Integer getCurrency() {
+    public Long getCurrency() {
         return currency;
     }
-    public void setCurrency(Integer currency) {
+    public void setCurrency(Long currency) {
         this.currency = currency;
     }
+    public Currency getCurrencyObject() {
+        if (currency == null || currency <= 0)
+            return null;
+        return DB.get()
+                 .getCurrencyDAO()
+                 .getByIdSync(currency);
+    }
     public Integer getCurrencyMatchGroup() {
         return currencyMatchGroup;
     }
@@ -160,10 +165,10 @@ public class TemplateAccount extends TemplateBase {
     public void setAccountCommentMatchGroup(Integer accountCommentMatchGroup) {
         this.accountCommentMatchGroup = accountCommentMatchGroup;
     }
-    public TemplateAccount createDuplicate() {
+    public TemplateAccount createDuplicate(TemplateHeader header) {
         TemplateAccount dup = new TemplateAccount(this);
-        dup.id = null;
-        dup.templateId = null;
+        dup.id = 0;
+        dup.templateId = header.getId();
 
         return dup;
     }
index b5afb89eef9814b09fe497fd29beb44f4528a1da..9d355d09cfc46de8309d573bf59eb3f20813248a 100644 (file)
@@ -30,8 +30,7 @@ import org.jetbrains.annotations.NotNull;
 @Entity(tableName = "templates")
 public class TemplateHeader extends TemplateBase {
     @PrimaryKey(autoGenerate = true)
-    @NonNull
-    private Long id;
+    private long id;
     @ColumnInfo(name = "name")
     @NonNull
     private String name;
@@ -127,11 +126,10 @@ public class TemplateHeader extends TemplateBase {
     public void setDateDay(Integer dateDay) {
         this.dateDay = dateDay;
     }
-    @NonNull
-    public Long getId() {
+    public long getId() {
         return id;
     }
-    public void setId(@NonNull Long id) {
+    public void setId(long id) {
         this.id = id;
     }
     @NonNull
@@ -203,7 +201,7 @@ public class TemplateHeader extends TemplateBase {
     }
     public TemplateHeader createDuplicate() {
         TemplateHeader dup = new TemplateHeader(this);
-        dup.id = null;
+        dup.id = 0;
 
         return dup;
     }
index 8572167ddc63677bd416080ac2741875ac7d434c..a96236e6fb0554f00e907c09e34873809398f00d 100644 (file)
@@ -47,7 +47,7 @@ public class TemplateWithAccounts {
         result.header = header.createDuplicate();
         result.accounts = new ArrayList<>();
         for (TemplateAccount acc : accounts) {
-            result.accounts.add(acc.createDuplicate());
+            result.accounts.add(acc.createDuplicate(result.header));
         }
 
         return result;
index 05e63d8c0e92dd05c3db2aa48603a65159d5cd6e..d1757a2bd80c2d3a1856fecc554ddcb9fcf65a1d 100644 (file)
@@ -64,7 +64,7 @@ public class Transaction {
     @ColumnInfo
     private String comment;
     @ColumnInfo
-    private int generation = 0;
+    private long generation = 0;
     public long getLedgerId() {
         return ledgerId;
     }
@@ -119,10 +119,10 @@ public class Transaction {
     public void setComment(String comment) {
         this.comment = comment;
     }
-    public int getGeneration() {
+    public long getGeneration() {
         return generation;
     }
-    public void setGeneration(int generation) {
+    public void setGeneration(long generation) {
         this.generation = generation;
     }
 
index 80b185bede2f69f3aaf727a7b01fee54136a64e1..d556731f7ac709164cd296b5e5b4758ee0dafc9c 100644 (file)
@@ -37,7 +37,7 @@ public class TransactionAccount {
     @PrimaryKey(autoGenerate = true)
     private long id;
     @ColumnInfo(name = "transaction_id")
-    private int transactionId;
+    private long transactionId;
     @ColumnInfo(name = "order_no")
     private int orderNo;
     @ColumnInfo(name = "account_name")
@@ -51,7 +51,7 @@ public class TransactionAccount {
     @ColumnInfo
     private String comment;
     @ColumnInfo(defaultValue = "0")
-    private int generation = 0;
+    private long generation = 0;
     public long getId() {
         return id;
     }
@@ -59,10 +59,10 @@ public class TransactionAccount {
         this.id = id;
     }
     @NonNull
-    public int getTransactionId() {
+    public long getTransactionId() {
         return transactionId;
     }
-    public void setTransactionId(int transactionId) {
+    public void setTransactionId(long transactionId) {
         this.transactionId = transactionId;
     }
     public int getOrderNo() {
@@ -97,10 +97,10 @@ public class TransactionAccount {
     public void setComment(String comment) {
         this.comment = comment;
     }
-    public int getGeneration() {
+    public long getGeneration() {
         return generation;
     }
-    public void setGeneration(int generation) {
+    public void setGeneration(long generation) {
         this.generation = generation;
     }
 }
index 1c7dbf7f81f014062c1282d55d2f9633d47abe9b..bfe84ea031355d93baf1fc03b375b3ec372a8cb0 100644 (file)
 
 package net.ktnx.mobileledger.model;
 
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.DiffUtil;
-
-import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.utils.Misc;
 
 public class Currency {
-    public static final DiffUtil.ItemCallback<Currency> DIFF_CALLBACK =
-            new DiffUtil.ItemCallback<Currency>() {
-                @Override
-                public boolean areItemsTheSame(@NonNull Currency oldItem,
-                                               @NonNull Currency newItem) {
-                    return oldItem.id == newItem.id;
-                }
-                @Override
-                public boolean areContentsTheSame(@NonNull Currency oldItem,
-                                                  @NonNull Currency newItem) {
-                    return oldItem.name.equals(newItem.name) &&
-                           oldItem.position.equals(newItem.position) &&
-                           (oldItem.hasGap == newItem.hasGap);
-                }
-            };
     private final int id;
     private String name;
     private Position position;
@@ -58,28 +36,6 @@ public class Currency {
         this.position = position;
         this.hasGap = hasGap;
     }
-    public Currency(MobileLedgerProfile profile, String name, Position position, boolean hasGap) {
-        SQLiteDatabase db = App.getDatabase();
-
-        try (Cursor c = db.rawQuery("select max(rowid) from currencies", null)) {
-            c.moveToNext();
-            this.id = c.getInt(0) + 1;
-        }
-        db.execSQL("insert into currencies(id, name, position, has_gap) values(?, ?, ?, ?)",
-                new Object[]{this.id, name, position.toString(), hasGap});
-
-        this.name = name;
-        this.position = position;
-        this.hasGap = hasGap;
-    }
-    public static Currency loadByName(String name) {
-        MobileLedgerProfile profile = Data.getProfile();
-        return profile.loadCurrencyByName(name);
-    }
-    public static Currency loadById(int id) {
-        MobileLedgerProfile profile = Data.getProfile();
-        return profile.loadCurrencyById(id);
-    }
     static public boolean equal(Currency left, Currency right) {
         if (left == null) {
             return right == null;
index 15d78ac034e475fde08bbf5724282189b98c6dd1..35f39358c46efa6d0634bea443c6a5bb01501af5 100644 (file)
 package net.ktnx.mobileledger.model;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.Observer;
 
 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
-import net.ktnx.mobileledger.utils.LockHolder;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.utils.Locker;
 import net.ktnx.mobileledger.utils.Logger;
 
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.ParsePosition;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
@@ -45,8 +45,7 @@ public final class Data {
             new MutableLiveData<>(false);
     public static final MutableLiveData<RetrieveTransactionsTask.Progress> backgroundTaskProgress =
             new MutableLiveData<>();
-    public static final MutableLiveData<ArrayList<MobileLedgerProfile>> profiles =
-            new MutableLiveData<>(null);
+    public static final LiveData<List<Profile>> profiles = DB.get().getProfileDAO().getAllOrdered();
     public static final MutableLiveData<Currency.Position> currencySymbolPosition =
             new MutableLiveData<>();
     public static final MutableLiveData<Boolean> currencyGap = new MutableLiveData<>(true);
@@ -59,7 +58,7 @@ public final class Data {
     public static final MutableLiveData<String> lastTransactionsUpdateText =
             new MutableLiveData<>();
     public static final MutableLiveData<String> lastAccountsUpdateText = new MutableLiveData<>();
-    private static final MutableLiveData<MobileLedgerProfile> profile =
+    private static final MutableLiveData<Profile> profile =
             new InertMutableLiveData<>();
     private static final AtomicInteger backgroundTaskCount = new AtomicInteger(0);
     private static final Locker profilesLocker = new Locker();
@@ -70,7 +69,7 @@ public final class Data {
     }
 
     @NonNull
-    public static MobileLedgerProfile getProfile() {
+    public static Profile getProfile() {
         return Objects.requireNonNull(profile.getValue());
     }
     public static void backgroundTaskStarted() {
@@ -87,61 +86,12 @@ public final class Data {
                         cnt));
         backgroundTasksRunning.postValue(cnt > 0);
     }
-    public static void setCurrentProfile(@NonNull MobileLedgerProfile newProfile) {
+    public static void setCurrentProfile(@NonNull Profile newProfile) {
         profile.setValue(newProfile);
     }
-    public static void postCurrentProfile(@NonNull MobileLedgerProfile newProfile) {
+    public static void postCurrentProfile(@NonNull Profile newProfile) {
         profile.postValue(newProfile);
     }
-    public static int getProfileIndex(MobileLedgerProfile profile) {
-        try (LockHolder ignored = profilesLocker.lockForReading()) {
-            List<MobileLedgerProfile> prList = profiles.getValue();
-            if (prList == null)
-                throw new AssertionError();
-            for (int i = 0; i < prList.size(); i++) {
-                MobileLedgerProfile p = prList.get(i);
-                if (p.equals(profile))
-                    return i;
-            }
-
-            return -1;
-        }
-    }
-    @SuppressWarnings("WeakerAccess")
-    public static int getProfileIndex(long profileId) {
-        try (LockHolder ignored = profilesLocker.lockForReading()) {
-            List<MobileLedgerProfile> prList = profiles.getValue();
-            if (prList == null)
-                throw new AssertionError();
-            for (int i = 0; i < prList.size(); i++) {
-                MobileLedgerProfile p = prList.get(i);
-                if (p.getId() == profileId)
-                    return i;
-            }
-
-            return -1;
-        }
-    }
-    @Nullable
-    public static MobileLedgerProfile getProfile(long profileId) {
-        MobileLedgerProfile profile;
-        try (LockHolder readLock = profilesLocker.lockForReading()) {
-            List<MobileLedgerProfile> prList = profiles.getValue();
-            if ((prList == null) || prList.isEmpty()) {
-                readLock.close();
-                try (LockHolder ignored = profilesLocker.lockForWriting()) {
-                    profile = MobileLedgerProfile.loadAllFromDB(profileId);
-                }
-            }
-            else {
-                int i = getProfileIndex(profileId);
-                if (i == -1)
-                    i = 0;
-                profile = prList.get(i);
-            }
-        }
-        return profile;
-    }
     public static void refreshCurrencyData(Locale locale) {
         NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
         java.util.Currency currency = formatter.getCurrency();
@@ -186,7 +136,7 @@ public final class Data {
         return numberFormatter.format(number);
     }
     public static void observeProfile(LifecycleOwner lifecycleOwner,
-                                      Observer<MobileLedgerProfile> observer) {
+                                      Observer<Profile> observer) {
         profile.observe(lifecycleOwner, observer);
     }
     public static void removeProfileObservers(LifecycleOwner owner) {
diff --git a/app/src/main/java/net/ktnx/mobileledger/model/FutureDates.java b/app/src/main/java/net/ktnx/mobileledger/model/FutureDates.java
new file mode 100644 (file)
index 0000000..db80db1
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.model;
+
+import android.content.res.Resources;
+import android.util.SparseArray;
+
+import net.ktnx.mobileledger.R;
+
+public enum FutureDates {
+    None(0), OneWeek(7), TwoWeeks(14), OneMonth(30), TwoMonths(60), ThreeMonths(90),
+    SixMonths(180), OneYear(365), All(-1);
+    private static final SparseArray<FutureDates> map = new SparseArray<>();
+
+    static {
+        for (FutureDates item : FutureDates.values()) {
+            map.put(item.value, item);
+        }
+    }
+
+    private final 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);
+        }
+    }
+}
index c76f2d564970193e29b37740f88a5ec86857c5d4..1e2a1a4f6834ed306d1ffb948715f26379c5df7f 100644 (file)
@@ -212,6 +212,21 @@ public class LedgerAccount {
 
         return dbo;
     }
+    @NonNull
+    public AccountWithAmounts toDBOWithAmounts() {
+        AccountWithAmounts dbo = new AccountWithAmounts();
+        dbo.account = toDBO();
+
+        dbo.amounts = new ArrayList<>();
+        for (LedgerAmount amt : getAmounts()) {
+            AccountValue val = new AccountValue();
+            val.setCurrency(amt.getCurrency());
+            val.setValue(amt.getAmount());
+            dbo.amounts.add(val);
+        }
+
+        return dbo;
+    }
     public long getId() {
         return dbId;
     }
index 001c107e0193b2a5c5a272bb6c60ec232a1a5e74..b9c6021fd43f28be0112ce9f9c2dba557c89624b 100644 (file)
@@ -24,6 +24,10 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.db.Profile;
+import net.ktnx.mobileledger.db.Transaction;
+import net.ktnx.mobileledger.db.TransactionAccount;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.utils.Digest;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.SimpleDate;
@@ -53,38 +57,77 @@ public class LedgerTransaction {
         return Float.compare(o1.getAmount(), o2.getAmount());
     };
     private final long profile;
-    private final long id;
+    private final long ledgerId;
     private final List<LedgerTransactionAccount> accounts;
+    private long dbId;
     private SimpleDate date;
     private String description;
     private String comment;
     private String dataHash;
     private boolean dataLoaded;
-    public LedgerTransaction(long id, String dateString, String description) throws ParseException {
-        this(id, Globals.parseLedgerDate(dateString), description);
+    public LedgerTransaction(long ledgerId, String dateString, String description)
+            throws ParseException {
+        this(ledgerId, Globals.parseLedgerDate(dateString), description);
     }
-    public LedgerTransaction(long id, SimpleDate date, String description,
-                             MobileLedgerProfile profile) {
+    public LedgerTransaction(TransactionWithAccounts dbo) {
+        this(dbo.transaction.getLedgerId(), dbo.transaction.getProfileId());
+        dbId = dbo.transaction.getId();
+        date = new SimpleDate(dbo.transaction.getYear(), dbo.transaction.getMonth(),
+                dbo.transaction.getDay());
+        description = dbo.transaction.getDescription();
+        comment = dbo.transaction.getComment();
+        dataHash = dbo.transaction.getDataHash();
+        if (dbo.accounts != null)
+            for (TransactionAccount acc : dbo.accounts) {
+                accounts.add(new LedgerTransactionAccount(acc));
+            }
+        dataLoaded = true;
+    }
+    public TransactionWithAccounts toDBO() {
+        TransactionWithAccounts o = new TransactionWithAccounts();
+        o.transaction = new Transaction();
+        o.transaction.setId(dbId);
+        o.transaction.setProfileId(profile);
+        o.transaction.setLedgerId(ledgerId);
+        o.transaction.setYear(date.year);
+        o.transaction.setMonth(date.month);
+        o.transaction.setDay(date.day);
+        o.transaction.setDescription(description);
+        o.transaction.setComment(comment);
+        fillDataHash();
+        o.transaction.setDataHash(dataHash);
+
+        o.accounts = new ArrayList<>();
+        int orderNo = 1;
+        for (LedgerTransactionAccount acc : accounts) {
+            TransactionAccount a = acc.toDBO();
+            a.setOrderNo(orderNo++);
+            a.setTransactionId(dbId);
+            o.accounts.add(a);
+        }
+        return o;
+    }
+    public LedgerTransaction(long ledgerId, SimpleDate date, String description, Profile profile) {
         this.profile = profile.getId();
-        this.id = id;
+        this.ledgerId = ledgerId;
         this.date = date;
         this.description = description;
         this.accounts = new ArrayList<>();
         this.dataHash = null;
         dataLoaded = false;
     }
-    public LedgerTransaction(long id, SimpleDate date, String description) {
-        this(id, date, description, Data.getProfile());
+    public LedgerTransaction(long ledgerId, SimpleDate date, String description) {
+        this(ledgerId, date, description, Data.getProfile());
     }
     public LedgerTransaction(SimpleDate date, String description) {
         this(0, date, description);
     }
-    public LedgerTransaction(int id) {
-        this(id, (SimpleDate) null, null);
+    public LedgerTransaction(int ledgerId) {
+        this(ledgerId, (SimpleDate) null, null);
     }
-    public LedgerTransaction(int id, long profileId) {
+    public LedgerTransaction(long ledgerId, long profileId) {
         this.profile = profileId;
-        this.id = id;
+        this.ledgerId = ledgerId;
         this.date = null;
         this.description = null;
         this.accounts = new ArrayList<>();
@@ -125,8 +168,8 @@ public class LedgerTransaction {
     public void setComment(String comment) {
         this.comment = comment;
     }
-    public long getId() {
-        return id;
+    public long getLedgerId() {
+        return ledgerId;
     }
     protected void fillDataHash() {
         loadData(App.getDatabase());
@@ -137,7 +180,7 @@ public class LedgerTransaction {
             StringBuilder data = new StringBuilder();
             data.append("ver1");
             data.append(profile);
-            data.append(getId());
+            data.append(getLedgerId());
             data.append('\0');
             data.append(getDescription());
             data.append('\0');
@@ -169,7 +212,7 @@ public class LedgerTransaction {
 
         try (Cursor cTr = db.rawQuery(
                 "SELECT year, month, day, description, comment from transactions WHERE id=?",
-                new String[]{String.valueOf(id)}))
+                new String[]{String.valueOf(ledgerId)}))
         {
             if (cTr.moveToFirst()) {
                 date = new SimpleDate(cTr.getInt(0), cTr.getInt(1), cTr.getInt(2));
@@ -181,7 +224,7 @@ public class LedgerTransaction {
                 try (Cursor cAcc = db.rawQuery(
                         "SELECT account_name, amount, currency, comment FROM " +
                         "transaction_accounts WHERE transaction_id = ?",
-                        new String[]{String.valueOf(id)}))
+                        new String[]{String.valueOf(ledgerId)}))
                 {
                     while (cAcc.moveToNext()) {
 //                        debug("transactions",
index 1791cfbc5962e79b5b3c16ab4516c5e921a1765b..53b6a65f3bca893186d73bb0710719fdab8d3fae 100644 (file)
@@ -19,6 +19,7 @@ package net.ktnx.mobileledger.model;
 
 import androidx.annotation.NonNull;
 
+import net.ktnx.mobileledger.db.TransactionAccount;
 import net.ktnx.mobileledger.utils.Misc;
 
 import java.util.Locale;
@@ -31,6 +32,7 @@ public class LedgerTransactionAccount {
     private String currency;
     private String comment;
     private boolean amountValid = true;
+    private long dbId;
     public LedgerTransactionAccount(String accountName, float amount, String currency,
                                     String comment) {
         this.setAccountName(accountName);
@@ -56,6 +58,13 @@ public class LedgerTransactionAccount {
         amountValid = origin.amountValid;
         currency = origin.getCurrency();
     }
+    public LedgerTransactionAccount(TransactionAccount dbo) {
+        this(dbo.getAccountName(), dbo.getAmount(), Misc.emptyIsNull(dbo.getCurrency()),
+                Misc.emptyIsNull(dbo.getComment()));
+        amountSet = true;
+        amountValid = true;
+        dbId = dbo.getId();
+    }
     public String getComment() {
         return comment;
     }
@@ -114,4 +123,15 @@ public class LedgerTransactionAccount {
 
         return sb.toString();
     }
+    public TransactionAccount toDBO() {
+        TransactionAccount dbo = new TransactionAccount();
+        dbo.setAccountName(accountName);
+        if (amountSet)
+            dbo.setAmount(amount);
+        dbo.setComment(comment);
+        dbo.setCurrency(currency);
+        dbo.setId(dbId);
+
+        return dbo;
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java
deleted file mode 100644 (file)
index 8cdc9c9..0000000
+++ /dev/null
@@ -1,793 +0,0 @@
-/*
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.model;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.SparseArray;
-
-import androidx.annotation.Nullable;
-import androidx.room.Transaction;
-
-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.db.Profile;
-import net.ktnx.mobileledger.json.API;
-import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity;
-import net.ktnx.mobileledger.ui.profiles.ProfileDetailFragment;
-import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.Misc;
-import net.ktnx.mobileledger.utils.SimpleDate;
-
-import org.jetbrains.annotations.Contract;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import static net.ktnx.mobileledger.utils.Logger.debug;
-
-public final class MobileLedgerProfile {
-    // N.B. when adding new fields, update the copy-constructor below
-    private final long id;
-    private String name;
-    private boolean permitPosting;
-    private boolean showCommentsByDefault;
-    private boolean showCommodityByDefault;
-    private String defaultCommodity;
-    private String preferredAccountsFilter;
-    private String url;
-    private boolean authEnabled;
-    private String authUserName;
-    private String authPassword;
-    private int themeHue;
-    private int orderNo = -1;
-    private API apiVersion = API.auto;
-    private FutureDates futureDates = FutureDates.None;
-    private boolean accountsLoaded;
-    private boolean transactionsLoaded;
-    private HledgerVersion detectedVersion;
-    // N.B. when adding new fields, update the copy-constructor below
-    transient private AccountAndTransactionListSaver accountAndTransactionListSaver;
-    public MobileLedgerProfile(long id) {
-        this.id = id;
-    }
-    public MobileLedgerProfile(MobileLedgerProfile origin) {
-        id = origin.id;
-        name = origin.name;
-        permitPosting = origin.permitPosting;
-        showCommentsByDefault = origin.showCommentsByDefault;
-        showCommodityByDefault = origin.showCommodityByDefault;
-        preferredAccountsFilter = origin.preferredAccountsFilter;
-        url = origin.url;
-        authEnabled = origin.authEnabled;
-        authUserName = origin.authUserName;
-        authPassword = origin.authPassword;
-        themeHue = origin.themeHue;
-        orderNo = origin.orderNo;
-        futureDates = origin.futureDates;
-        apiVersion = origin.apiVersion;
-        defaultCommodity = origin.defaultCommodity;
-        accountsLoaded = origin.accountsLoaded;
-        transactionsLoaded = origin.transactionsLoaded;
-        if (origin.detectedVersion != null)
-            detectedVersion = new HledgerVersion(origin.detectedVersion);
-    }
-    // loads all profiles into Data.profiles
-    // returns the profile with the given UUID
-    public static MobileLedgerProfile loadAllFromDB(long currentProfileId) {
-        MobileLedgerProfile result = null;
-        ArrayList<MobileLedgerProfile> list = new ArrayList<>();
-        SQLiteDatabase db = App.getDatabase();
-        try (Cursor cursor = db.rawQuery("SELECT id, name, url, use_authentication, auth_user, " +
-                                         "auth_password, permit_posting, theme, order_no, " +
-                                         "preferred_accounts_filter, future_dates, api_version, " +
-                                         "show_commodity_by_default, default_commodity, " +
-                                         "show_comments_by_default, detected_version_pre_1_19, " +
-                                         "detected_version_major, detected_version_minor FROM " +
-                                         "profiles order by order_no", null))
-        {
-            while (cursor.moveToNext()) {
-                MobileLedgerProfile item = new MobileLedgerProfile(cursor.getLong(0));
-                item.setName(cursor.getString(1));
-                item.setUrl(cursor.getString(2));
-                item.setAuthEnabled(cursor.getInt(3) == 1);
-                item.setAuthUserName(cursor.getString(4));
-                item.setAuthPassword(cursor.getString(5));
-                item.setPostingPermitted(cursor.getInt(6) == 1);
-                item.setThemeId(cursor.getInt(7));
-                item.orderNo = cursor.getInt(8);
-                item.setPreferredAccountsFilter(cursor.getString(9));
-                item.setFutureDates(cursor.getInt(10));
-                item.setApiVersion(cursor.getInt(11));
-                item.setShowCommodityByDefault(cursor.getInt(12) == 1);
-                item.setDefaultCommodity(cursor.getString(13));
-                item.setShowCommentsByDefault(cursor.getInt(14) == 1);
-                {
-                    boolean pre_1_20 = cursor.getInt(15) == 1;
-                    int major = cursor.getInt(16);
-                    int minor = cursor.getInt(17);
-
-                    if (!pre_1_20 && major == 0 && minor == 0) {
-                        item.detectedVersion = null;
-                    }
-                    else if (pre_1_20) {
-                        item.detectedVersion = new HledgerVersion(true);
-                    }
-                    else {
-                        item.detectedVersion = new HledgerVersion(major, minor);
-                    }
-                }
-                list.add(item);
-                if (item.getId() == currentProfileId)
-                    result = item;
-            }
-        }
-        Data.profiles.postValue(list);
-        return result;
-    }
-    public static void storeProfilesOrder() {
-        SQLiteDatabase db = App.getDatabase();
-        db.beginTransactionNonExclusive();
-        try {
-            int orderNo = 0;
-            for (MobileLedgerProfile p : Objects.requireNonNull(Data.profiles.getValue())) {
-                db.execSQL("update profiles set order_no=? where id=?",
-                        new Object[]{orderNo, p.getId()});
-                p.orderNo = orderNo;
-                orderNo++;
-            }
-            db.setTransactionSuccessful();
-        }
-        finally {
-            db.endTransaction();
-        }
-    }
-    static public void startEditProfileActivity(Context context, MobileLedgerProfile profile) {
-        Intent intent = new Intent(context, ProfileDetailActivity.class);
-        Bundle args = new Bundle();
-        if (profile != null) {
-            int index = Data.getProfileIndex(profile);
-            if (index != -1)
-                intent.putExtra(ProfileDetailFragment.ARG_ITEM_ID, index);
-        }
-        intent.putExtras(args);
-        context.startActivity(intent, args);
-    }
-    public static MobileLedgerProfile fromDBO(Profile newProfile) {
-        MobileLedgerProfile p = new MobileLedgerProfile(newProfile.getId());
-        p.setDetectedVersion(new HledgerVersion(newProfile.getDetectedVersionMajor(),
-                newProfile.getDetectedVersionMinor()));
-        p.setApiVersion(newProfile.getApiVersion());
-        p.setAuthEnabled(newProfile.useAuthentication());
-        p.setAuthUserName(newProfile.getAuthUser());
-        p.setAuthPassword(newProfile.getAuthPassword());
-        p.setDefaultCommodity(newProfile.getDefaultCommodity());
-        p.setFutureDates(newProfile.getFutureDates());
-        p.setName(newProfile.getName());
-        p.setPostingPermitted(newProfile.permitPosting());
-        p.setPreferredAccountsFilter(newProfile.getPreferredAccountsFilter());
-        p.setShowCommentsByDefault(newProfile.getShowCommentsByDefault());
-        p.setShowCommodityByDefault(newProfile.getShowCommodityByDefault());
-        p.setUrl(newProfile.getUrl());
-        p.setThemeId(newProfile.getTheme());
-
-        return p;
-    }
-    public HledgerVersion getDetectedVersion() {
-        return detectedVersion;
-    }
-    public void setDetectedVersion(HledgerVersion detectedVersion) {
-        this.detectedVersion = detectedVersion;
-    }
-    @Contract(value = "null -> false", pure = true)
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (obj == null)
-            return false;
-        if (obj == this)
-            return true;
-        if (obj.getClass() != this.getClass())
-            return false;
-
-        MobileLedgerProfile p = (MobileLedgerProfile) obj;
-        if (id != p.id)
-            return false;
-        if (!name.equals(p.name))
-            return false;
-        if (permitPosting != p.permitPosting)
-            return false;
-        if (showCommentsByDefault != p.showCommentsByDefault)
-            return false;
-        if (showCommodityByDefault != p.showCommodityByDefault)
-            return false;
-        if (!Objects.equals(defaultCommodity, p.defaultCommodity))
-            return false;
-        if (!Objects.equals(preferredAccountsFilter, p.preferredAccountsFilter))
-            return false;
-        if (!Objects.equals(url, p.url))
-            return false;
-        if (authEnabled != p.authEnabled)
-            return false;
-        if (!Objects.equals(authUserName, p.authUserName))
-            return false;
-        if (!Objects.equals(authPassword, p.authPassword))
-            return false;
-        if (themeHue != p.themeHue)
-            return false;
-        if (apiVersion != p.apiVersion)
-            return false;
-        if (!Objects.equals(detectedVersion, p.detectedVersion))
-            return false;
-        return futureDates == p.futureDates;
-    }
-    public boolean getShowCommentsByDefault() {
-        return showCommentsByDefault;
-    }
-    public void setShowCommentsByDefault(boolean newValue) {
-        this.showCommentsByDefault = newValue;
-    }
-    public boolean getShowCommodityByDefault() {
-        return showCommodityByDefault;
-    }
-    public void setShowCommodityByDefault(boolean showCommodityByDefault) {
-        this.showCommodityByDefault = showCommodityByDefault;
-    }
-    public String getDefaultCommodity() {
-        return defaultCommodity;
-    }
-    public void setDefaultCommodity(String defaultCommodity) {
-        this.defaultCommodity = defaultCommodity;
-    }
-    public void setDefaultCommodity(CharSequence defaultCommodity) {
-        if (defaultCommodity == null)
-            this.defaultCommodity = null;
-        else
-            this.defaultCommodity = String.valueOf(defaultCommodity);
-    }
-    public API getApiVersion() {
-        return apiVersion;
-    }
-    public void setApiVersion(API apiVersion) {
-        this.apiVersion = apiVersion;
-    }
-    public void setApiVersion(int apiVersion) {
-        this.apiVersion = API.valueOf(apiVersion);
-    }
-    public FutureDates getFutureDates() {
-        return futureDates;
-    }
-    public void setFutureDates(int anInt) {
-        futureDates = FutureDates.valueOf(anInt);
-    }
-    public void setFutureDates(FutureDates futureDates) {
-        this.futureDates = futureDates;
-    }
-    public String getPreferredAccountsFilter() {
-        return preferredAccountsFilter;
-    }
-    public void setPreferredAccountsFilter(String preferredAccountsFilter) {
-        this.preferredAccountsFilter = preferredAccountsFilter;
-    }
-    public void setPreferredAccountsFilter(CharSequence preferredAccountsFilter) {
-        setPreferredAccountsFilter(String.valueOf(preferredAccountsFilter));
-    }
-    public boolean isPostingPermitted() {
-        return permitPosting;
-    }
-    public void setPostingPermitted(boolean permitPosting) {
-        this.permitPosting = permitPosting;
-    }
-    public long getId() {
-        return id;
-    }
-    public String getName() {
-        return name;
-    }
-    public void setName(CharSequence text) {
-        setName(String.valueOf(text));
-    }
-    public void setName(String name) {
-        this.name = name;
-    }
-    public String getUrl() {
-        return url;
-    }
-    public void setUrl(CharSequence text) {
-        setUrl(String.valueOf(text));
-    }
-    public void setUrl(String url) {
-        this.url = url;
-    }
-    public boolean isAuthEnabled() {
-        return authEnabled;
-    }
-    public void setAuthEnabled(boolean authEnabled) {
-        this.authEnabled = authEnabled;
-    }
-    public String getAuthUserName() {
-        return authUserName;
-    }
-    public void setAuthUserName(CharSequence text) {
-        setAuthUserName(String.valueOf(text));
-    }
-    public void setAuthUserName(String authUserName) {
-        this.authUserName = authUserName;
-    }
-    public String getAuthPassword() {
-        return authPassword;
-    }
-    public void setAuthPassword(CharSequence text) {
-        setAuthPassword(String.valueOf(text));
-    }
-    public void setAuthPassword(String authPassword) {
-        this.authPassword = authPassword;
-    }
-    public void storeInDB() {
-        SQLiteDatabase db = App.getDatabase();
-        db.beginTransactionNonExclusive();
-        try {
-//            debug("profiles", String.format("Storing profile in DB: uuid=%s, name=%s, " +
-//                                            "url=%s, permit_posting=%s, authEnabled=%s, " +
-//                                            "themeHue=%d", uuid, name, url,
-//                    permitPosting ? "TRUE" : "FALSE", authEnabled ? "TRUE" : "FALSE", themeHue));
-            db.execSQL("REPLACE INTO profiles(id, name, permit_posting, url, " +
-                       "use_authentication, auth_user, auth_password, theme, order_no, " +
-                       "preferred_accounts_filter, future_dates, api_version, " +
-                       "show_commodity_by_default, default_commodity, show_comments_by_default," +
-                       "detected_version_pre_1_19, detected_version_major, " +
-                       "detected_version_minor) " +
-                       "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                    new Object[]{id, name, permitPosting, url, authEnabled,
-                                 authEnabled ? authUserName : null,
-                                 authEnabled ? authPassword : null, themeHue, orderNo,
-                                 preferredAccountsFilter, futureDates.toInt(), apiVersion.toInt(),
-                                 showCommodityByDefault, defaultCommodity, showCommentsByDefault,
-                                 (detectedVersion != null) && detectedVersion.isPre_1_20_1(),
-                                 (detectedVersion == null) ? 0 : detectedVersion.getMajor(),
-                                 (detectedVersion == null) ? 0 : detectedVersion.getMinor()
-                    });
-            db.setTransactionSuccessful();
-        }
-        finally {
-            db.endTransaction();
-        }
-    }
-    public void storeAccount(SQLiteDatabase db, int generation, LedgerAccount acc,
-                             boolean storeUiFields) {
-        // replace into is a bad idea because it would reset hidden to its default value
-        // we like the default, but for new accounts only
-        String sql = "update accounts set generation = ?";
-        List<Object> params = new ArrayList<>();
-        params.add(generation);
-        if (storeUiFields) {
-            sql += ", expanded=?";
-            params.add(acc.isExpanded() ? 1 : 0);
-        }
-        sql += " where profile_id=? and name=?";
-        params.add(id);
-        params.add(acc.getName());
-        db.execSQL(sql, params.toArray());
-
-        db.execSQL("insert into accounts(profile_id, name, name_upper, parent_name, level, " +
-                   "expanded, generation) select ?,?,?,?,?,0,? where (select changes() = 0)",
-                new Object[]{id, acc.getName(), acc.getName().toUpperCase(), acc.getParentName(),
-                             acc.getLevel(), generation
-                });
-//        debug("accounts", String.format("Stored account '%s' in DB [%s]", acc.getName(), uuid));
-    }
-    public void storeTransaction(SQLiteDatabase db, int generation, LedgerTransaction tr) {
-        tr.fillDataHash();
-//        Logger.debug("storeTransaction", String.format(Locale.US, "ID %d", tr.getId()));
-        SimpleDate d = tr.getDate();
-        db.execSQL("UPDATE transactions SET year=?, month=?, day=?, description=?, comment=?, " +
-                   "data_hash=?, generation=? WHERE profile_id=? AND ledger_id=?",
-                new Object[]{d.year, d.month, d.day, tr.getDescription(), tr.getComment(),
-                             tr.getDataHash(), generation, id, tr.getId()
-                });
-        db.execSQL(
-                "INSERT INTO transactions(profile_id, ledger_id, year, month, day, description, " +
-                "comment, data_hash, generation) " +
-                "select ?,?,?,?,?,?,?,?,? WHERE (select changes() = 0)",
-                new Object[]{id, tr.getId(), tr.getDate().year, tr.getDate().month,
-                             tr.getDate().day, tr.getDescription(), tr.getComment(),
-                             tr.getDataHash(), generation
-                });
-
-        int accountOrderNo = 1;
-        for (LedgerTransactionAccount item : tr.getAccounts()) {
-            db.execSQL("UPDATE transaction_accounts SET account_name=?, amount=?, currency=?, " +
-                       "comment=?, generation=? " + "WHERE transaction_id=? AND order_no=?",
-                    new Object[]{item.getAccountName(), item.getAmount(),
-                                 Misc.nullIsEmpty(item.getCurrency()), item.getComment(),
-                                 generation, tr.getId(), accountOrderNo
-                    });
-            db.execSQL("INSERT INTO transaction_accounts(transaction_id, " +
-                       "order_no, account_name, amount, currency, comment, generation) " +
-                       "select ?, ?, ?, ?, ?, ?, ? WHERE (select changes() = 0)",
-                    new Object[]{tr.getId(), accountOrderNo, item.getAccountName(),
-                                 item.getAmount(), Misc.nullIsEmpty(item.getCurrency()),
-                                 item.getComment(), generation
-                    });
-
-            accountOrderNo++;
-        }
-//        debug("profile", String.format("Transaction %d stored", tr.getId()));
-    }
-    public String getOption(String name, String default_value) {
-        SQLiteDatabase db = App.getDatabase();
-        try (Cursor cursor = db.rawQuery(
-                "select value from options where profile_id = ? and name=?",
-                new String[]{String.valueOf(id), name}))
-        {
-            if (cursor.moveToFirst()) {
-                String result = cursor.getString(0);
-
-                if (result == null) {
-                    debug("profile", "returning default value for " + name);
-                    result = default_value;
-                }
-                else
-                    debug("profile", String.format("option %s=%s", name, result));
-
-                return result;
-            }
-            else
-                return default_value;
-        }
-        catch (Exception e) {
-            debug("db", "returning default value for " + name, e);
-            return default_value;
-        }
-    }
-    public long getLongOption(String name, long default_value) {
-        long longResult;
-        String result = getOption(name, "");
-        if ((result == null) || result.isEmpty()) {
-            debug("profile", String.format("Returning default value for option %s", name));
-            longResult = default_value;
-        }
-        else {
-            try {
-                longResult = Long.parseLong(result);
-                debug("profile", String.format("option %s=%s", name, result));
-            }
-            catch (Exception e) {
-                debug("profile", String.format("Returning default value for option %s", name), e);
-                longResult = default_value;
-            }
-        }
-
-        return longResult;
-    }
-    public void setOption(String name, String value) {
-        debug("profile", String.format("setting option %s=%s", name, value));
-        DbOpQueue.add("insert or replace into options(profile_id, name, value) values(?, ?, ?);",
-                new String[]{String.valueOf(id), name, value});
-    }
-    public void setLongOption(String name, long value) {
-        setOption(name, String.valueOf(value));
-    }
-    public void removeFromDB() {
-        ProfileDAO dao = DB.get()
-                           .getProfileDAO();
-        AsyncTask.execute(() -> dao.deleteSync(dao.getByIdSync(id)));
-    }
-    public LedgerTransaction loadTransaction(int transactionId) {
-        LedgerTransaction tr = new LedgerTransaction(transactionId, this.id);
-        tr.loadData(App.getDatabase());
-
-        return tr;
-    }
-    public int getThemeHue() {
-//        debug("profile", String.format("Profile.getThemeHue() returning %d", themeHue));
-        return this.themeHue;
-    }
-    public void setThemeHue(Object o) {
-        setThemeId(Integer.parseInt(String.valueOf(o)));
-    }
-    public void setThemeId(int themeHue) {
-//        debug("profile", String.format("Profile.setThemeHue(%d) called", themeHue));
-        this.themeHue = themeHue;
-    }
-    public int getNextTransactionsGeneration(SQLiteDatabase db) {
-        try (Cursor c = db.rawQuery(
-                "SELECT generation FROM transactions WHERE profile_id=? LIMIT 1",
-                new String[]{String.valueOf(id)}))
-        {
-            if (c.moveToFirst())
-                return c.getInt(0) + 1;
-        }
-        return 1;
-    }
-    private void deleteNotPresentTransactions(SQLiteDatabase db, int generation) {
-        Logger.debug("db/benchmark", "Deleting obsolete transactions");
-        db.execSQL(
-                "DELETE FROM transaction_accounts WHERE (select t.profile_id from transactions t " +
-                "where t.id=transaction_accounts.transaction_id)=? AND generation" + " <> ?",
-                new Object[]{id, generation});
-        db.execSQL("DELETE FROM transactions WHERE profile_id=? AND generation <> ?",
-                new Object[]{id, generation});
-        Logger.debug("db/benchmark", "Done deleting obsolete transactions");
-    }
-    @Transaction
-    public void wipeAllDataSync() {
-        OptionDAO optDao = DB.get()
-                             .getOptionDAO();
-        optDao.deleteSync(optDao.allForProfileSync(id));
-
-        AccountDAO accDao = DB.get()
-                              .getAccountDAO();
-        accDao.deleteSync(accDao.allForProfileSync(id));
-
-        TransactionDAO trnDao = DB.get()
-                                  .getTransactionDAO();
-        trnDao.deleteSync(trnDao.allForProfileSync(id));
-
-        DescriptionHistoryDAO descDao = DB.get()
-                                          .getDescriptionHistoryDAO();
-        descDao.sweepSync();
-    }
-    public void wipeAllData() {
-        AsyncTask.execute(this::wipeAllDataSync);
-    }
-    public List<Currency> getCurrencies() {
-        SQLiteDatabase db = App.getDatabase();
-
-        ArrayList<Currency> result = new ArrayList<>();
-
-        try (Cursor c = db.rawQuery("SELECT c.id, c.name, c.position, c.has_gap FROM currencies c",
-                new String[]{}))
-        {
-            while (c.moveToNext()) {
-                Currency currency = new Currency(c.getInt(0), c.getString(1),
-                        Currency.Position.valueOf(c.getString(2)), c.getInt(3) == 1);
-                result.add(currency);
-            }
-        }
-
-        return result;
-    }
-    Currency loadCurrencyByName(String name) {
-        SQLiteDatabase db = App.getDatabase();
-        Currency result = tryLoadCurrencyByName(db, name);
-        if (result == null)
-            throw new RuntimeException(String.format("Unable to load currency '%s'", name));
-        return result;
-    }
-    private Currency tryLoadCurrencyByName(SQLiteDatabase db, String name) {
-        try (Cursor cursor = db.rawQuery(
-                "SELECT c.id, c.name, c.position, c.has_gap FROM currencies c WHERE c.name=?",
-                new String[]{name}))
-        {
-            if (cursor.moveToFirst()) {
-                return new Currency(cursor.getInt(0), cursor.getString(1),
-                        Currency.Position.valueOf(cursor.getString(2)), cursor.getInt(3) == 1);
-            }
-            return null;
-        }
-    }
-    public void storeAccountAndTransactionListAsync(List<LedgerAccount> accounts,
-                                                    List<LedgerTransaction> transactions) {
-        if (accountAndTransactionListSaver != null)
-            accountAndTransactionListSaver.interrupt();
-
-        accountAndTransactionListSaver =
-                new AccountAndTransactionListSaver(this, accounts, transactions);
-        accountAndTransactionListSaver.start();
-    }
-    private Currency tryLoadCurrencyById(SQLiteDatabase db, int id) {
-        try (Cursor cursor = db.rawQuery(
-                "SELECT c.id, c.name, c.position, c.has_gap FROM currencies c WHERE c.id=?",
-                new String[]{String.valueOf(id)}))
-        {
-            if (cursor.moveToFirst()) {
-                return new Currency(cursor.getInt(0), cursor.getString(1),
-                        Currency.Position.valueOf(cursor.getString(2)), cursor.getInt(3) == 1);
-            }
-            return null;
-        }
-    }
-    public Currency loadCurrencyById(int id) {
-        SQLiteDatabase db = App.getDatabase();
-        Currency result = tryLoadCurrencyById(db, id);
-        if (result == null)
-            throw new RuntimeException(String.format("Unable to load currency with id '%d'", id));
-        return result;
-    }
-
-    public enum FutureDates {
-        None(0), OneWeek(7), TwoWeeks(14), OneMonth(30), TwoMonths(60), ThreeMonths(90),
-        SixMonths(180), OneYear(365), All(-1);
-        private static final SparseArray<FutureDates> map = new SparseArray<>();
-
-        static {
-            for (FutureDates item : FutureDates.values()) {
-                map.put(item.value, item);
-            }
-        }
-
-        private final 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);
-            }
-        }
-    }
-
-    private static class AccountAndTransactionListSaver extends Thread {
-        private final MobileLedgerProfile profile;
-        private final List<LedgerAccount> accounts;
-        private final List<LedgerTransaction> transactions;
-        AccountAndTransactionListSaver(MobileLedgerProfile profile, List<LedgerAccount> accounts,
-                                       List<LedgerTransaction> transactions) {
-            this.accounts = accounts;
-            this.transactions = transactions;
-            this.profile = profile;
-        }
-        public int getNextDescriptionsGeneration(SQLiteDatabase db) {
-            int generation = 1;
-            try (Cursor c = db.rawQuery("SELECT generation FROM description_history LIMIT 1",
-                    null))
-            {
-                if (c.moveToFirst()) {
-                    generation = c.getInt(0) + 1;
-                }
-            }
-            return generation;
-        }
-        void deleteNotPresentDescriptions(SQLiteDatabase db, int generation) {
-            Logger.debug("db/benchmark", "Deleting obsolete descriptions");
-            db.execSQL("DELETE FROM description_history WHERE generation <> ?",
-                    new Object[]{generation});
-            db.execSQL("DELETE FROM description_history WHERE generation <> ?",
-                    new Object[]{generation});
-            Logger.debug("db/benchmark", "Done deleting obsolete descriptions");
-        }
-        @Override
-        public void run() {
-            SQLiteDatabase db = App.getDatabase();
-            db.beginTransactionNonExclusive();
-            try {
-                int transactionsGeneration = profile.getNextTransactionsGeneration(db);
-                if (isInterrupted())
-                    return;
-
-                for (LedgerTransaction tr : transactions) {
-                    profile.storeTransaction(db, transactionsGeneration, tr);
-                    if (isInterrupted())
-                        return;
-                }
-
-                profile.deleteNotPresentTransactions(db, transactionsGeneration);
-                if (isInterrupted()) {
-                    return;
-                }
-
-                Map<String, Boolean> unique = new HashMap<>();
-
-                debug("descriptions", "Starting refresh");
-                int descriptionsGeneration = getNextDescriptionsGeneration(db);
-                try (Cursor c = db.rawQuery("SELECT distinct description from transactions",
-                        null))
-                {
-                    while (c.moveToNext()) {
-                        String description = c.getString(0);
-                        String descriptionUpper = description.toUpperCase();
-                        if (unique.containsKey(descriptionUpper))
-                            continue;
-
-                        storeDescription(db, descriptionsGeneration, description, descriptionUpper);
-
-                        unique.put(descriptionUpper, true);
-                    }
-                }
-                deleteNotPresentDescriptions(db, descriptionsGeneration);
-
-                db.setTransactionSuccessful();
-            }
-            finally {
-                db.endTransaction();
-            }
-
-            AsyncTask.execute(() -> {
-                List<AccountWithAmounts> 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) {
-            db.execSQL("UPDATE description_history SET description=?, generation=? WHERE " +
-                       "description_upper=?", new Object[]{description, generation, descriptionUpper
-            });
-            db.execSQL(
-                    "INSERT INTO description_history(description, description_upper, generation) " +
-                    "select ?,?,? WHERE (select changes() = 0)",
-                    new Object[]{description, descriptionUpper, generation
-                    });
-        }
-    }
-}
index 740806813b09456e149a798093921b4a8b36f522..67d6a4f374b5b05bcd9d01116e94bbe9e5afbf8a 100644 (file)
@@ -119,9 +119,7 @@ abstract public class TemplateDetailsItem {
                 acc.setAccountCommentMatchGroup(pa.getAccountCommentMatchGroup());
 
             if (pa.getCurrencyMatchGroup() == null) {
-                final Integer currencyId = pa.getCurrency();
-                if (currencyId != null && currencyId > 0)
-                    acc.setCurrency(Currency.loadById(currencyId));
+                acc.setCurrency(pa.getCurrencyObject());
             }
             else
                 acc.setCurrencyMatchGroup(pa.getCurrencyMatchGroup());
@@ -300,7 +298,8 @@ abstract public class TemplateDetailsItem {
                 PossiblyMatchedValue.withLiteralString("");
         private final PossiblyMatchedValue<Float> amount =
                 PossiblyMatchedValue.withLiteralFloat(null);
-        private final PossiblyMatchedValue<Currency> currency = new PossiblyMatchedValue<>();
+        private final PossiblyMatchedValue<net.ktnx.mobileledger.db.Currency> currency =
+                new PossiblyMatchedValue<>();
         private boolean negateAmount;
         public AccountRow() {
             super(Type.ACCOUNT_ITEM);
@@ -339,10 +338,10 @@ abstract public class TemplateDetailsItem {
         public void setCurrencyMatchGroup(int group) {
             currency.setMatchGroup(group);
         }
-        public Currency getCurrency() {
+        public net.ktnx.mobileledger.db.Currency getCurrency() {
             return currency.getValue();
         }
-        public void setCurrency(Currency currency) {
+        public void setCurrency(net.ktnx.mobileledger.db.Currency currency) {
             this.currency.setValue(currency);
         }
         public int getAccountNameMatchGroup() {
index e86d5015f77607302ffd0c1b367b7332235302ed..47f2353e6093eaa8de0418ac5545c3eb5197aa86 100644 (file)
@@ -35,16 +35,15 @@ import androidx.recyclerview.widget.RecyclerView;
 
 import com.google.android.material.switchmaterial.SwitchMaterial;
 
-import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.dao.CurrencyDAO;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Currency;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A fragment representing a list of Items.
@@ -109,11 +108,19 @@ public class CurrencySelectorFragment extends AppCompatDialogFragment
         model = new ViewModelProvider(this).get(CurrencySelectorModel.class);
         if (onCurrencySelectedListener != null)
             model.setOnCurrencySelectedListener(onCurrencySelectedListener);
-        MobileLedgerProfile profile = Objects.requireNonNull(Data.getProfile());
+        Profile profile = Data.getProfile();
 
-        model.currencies.setValue(new CopyOnWriteArrayList<>(profile.getCurrencies()));
         CurrencySelectorRecyclerViewAdapter adapter = new CurrencySelectorRecyclerViewAdapter();
-        model.currencies.observe(this, adapter::submitList);
+        DB.get()
+          .getCurrencyDAO()
+          .getAll()
+          .observe(this, list -> {
+              List<String> strings = new ArrayList<>();
+              for (net.ktnx.mobileledger.db.Currency c : list) {
+                  strings.add(c.getName());
+              }
+              adapter.submitList(strings);
+          });
 
         recyclerView.setAdapter(adapter);
         adapter.setCurrencySelectedListener(this);
@@ -146,11 +153,11 @@ public class CurrencySelectorFragment extends AppCompatDialogFragment
 
             String currName = String.valueOf(tvNewCurrName.getText());
             if (!currName.isEmpty()) {
-                List<Currency> list = new ArrayList<>(model.currencies.getValue());
+                DB.get()
+                  .getCurrencyDAO()
+                  .insert(new net.ktnx.mobileledger.db.Currency(null,
+                          String.valueOf(tvNewCurrName.getText()), "after", false), null);
                 // FIXME hardcoded position and gap setting
-                list.add(new Currency(profile, String.valueOf(tvNewCurrName.getText()),
-                        Currency.Position.after, false));
-                model.currencies.setValue(list);
             }
 
             tvNewCurrName.setVisibility(View.GONE);
@@ -210,19 +217,18 @@ public class CurrencySelectorFragment extends AppCompatDialogFragment
         model.resetOnCurrencySelectedListener();
     }
     @Override
-    public void onCurrencySelected(Currency item) {
+    public void onCurrencySelected(String item) {
         model.triggerOnCurrencySelectedListener(item);
 
         dismiss();
     }
 
     @Override
-    public void onCurrencyLongClick(Currency item) {
-        ArrayList<Currency> list = new ArrayList<>(model.currencies.getValue());
-        App.getDatabase()
-           .execSQL("delete from currencies where id=?", new Object[]{item.getId()});
-        list.remove(item);
-        model.currencies.setValue(list);
+    public void onCurrencyLongClick(String item) {
+        CurrencyDAO dao = DB.get()
+                            .getCurrencyDAO();
+        dao.getByName(item)
+           .observe(this, dao::deleteSync);
     }
     public void showPositionAndPadding() {
         deferredShowPositionAndPadding = true;
index 4b346cdf7759d16ceb4b7e3bea18f3cd51ab8d2e..0fca9d6caf813827d8347eb49814ac5fd2277e1d 100644 (file)
@@ -22,18 +22,10 @@ import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.Observer;
 import androidx.lifecycle.ViewModel;
 
-import net.ktnx.mobileledger.model.Currency;
-
-import java.util.ArrayList;
-import java.util.List;
-
 public class CurrencySelectorModel extends ViewModel {
-    public final MutableLiveData<List<Currency>> currencies;
     private final MutableLiveData<Boolean> positionAndPaddingVisible = new MutableLiveData<>(true);
     private OnCurrencySelectedListener selectionListener;
-    public CurrencySelectorModel() {
-        this.currencies = new MutableLiveData<>(new ArrayList<>());
-    }
+    public CurrencySelectorModel() { }
     public void showPositionAndPadding() {
         positionAndPaddingVisible.postValue(true);
     }
@@ -50,7 +42,7 @@ public class CurrencySelectorModel extends ViewModel {
     void resetOnCurrencySelectedListener() {
         selectionListener = null;
     }
-    void triggerOnCurrencySelectedListener(Currency c) {
+    void triggerOnCurrencySelectedListener(String c) {
         if (selectionListener != null)
             selectionListener.onCurrencySelected(c);
     }
index b7d90a3a87eb48bd6c9037365ac195b3ad1c7540..7d4c60bed2b1393c2dfd7f756c5c6f66f32aabee 100644 (file)
@@ -22,6 +22,8 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.ListAdapter;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -35,12 +37,24 @@ import org.jetbrains.annotations.NotNull;
  * specified {@link OnCurrencySelectedListener}.
  */
 public class CurrencySelectorRecyclerViewAdapter
-        extends ListAdapter<Currency, CurrencySelectorRecyclerViewAdapter.ViewHolder> {
+        extends ListAdapter<String, CurrencySelectorRecyclerViewAdapter.ViewHolder> {
+    private static final DiffUtil.ItemCallback<String> DIFF_CALLBACK =
+            new DiffUtil.ItemCallback<String>() {
+                @Override
+                public boolean areItemsTheSame(@NonNull String oldItem, @NonNull String newItem) {
+                    return oldItem.equals(newItem);
+                }
+                @Override
+                public boolean areContentsTheSame(@NonNull String oldItem,
+                                                  @NonNull String newItem) {
+                    return true;
+                }
+            };
 
     private OnCurrencySelectedListener currencySelectedListener;
     private OnCurrencyLongClickListener currencyLongClickListener;
     public CurrencySelectorRecyclerViewAdapter() {
-        super(Currency.DIFF_CALLBACK);
+        super(DIFF_CALLBACK);
     }
     @NotNull
     @Override
@@ -60,7 +74,7 @@ public class CurrencySelectorRecyclerViewAdapter
     public void resetCurrencySelectedListener() {
         currencySelectedListener = null;
     }
-    public void notifyCurrencySelected(Currency currency) {
+    public void notifyCurrencySelected(String currency) {
         if (null != currencySelectedListener)
             currencySelectedListener.onCurrencySelected(currency);
     }
@@ -68,14 +82,14 @@ public class CurrencySelectorRecyclerViewAdapter
         this.currencyLongClickListener = listener;
     }
     public void resetCurrencyLockClickListener() { currencyLongClickListener = null; }
-    private void notifyCurrencyLongClicked(Currency mItem) {
+    private void notifyCurrencyLongClicked(String mItem) {
         if (null != currencyLongClickListener)
             currencyLongClickListener.onCurrencyLongClick(mItem);
     }
 
     public class ViewHolder extends RecyclerView.ViewHolder {
         private final TextView mNameView;
-        private Currency mItem;
+        private String mItem;
 
         ViewHolder(View view) {
             super(view);
@@ -93,9 +107,9 @@ public class CurrencySelectorRecyclerViewAdapter
         public String toString() {
             return super.toString() + " '" + mNameView.getText() + "'";
         }
-        void bindTo(Currency item) {
+        void bindTo(String item) {
             mItem = item;
-            mNameView.setText(item.getName());
+            mNameView.setText(item);
         }
     }
 }
index f5fe08cbd7c9040caa441b4b6550cf41d5c54461..54a2093247e47ccf73982b1c64d5ce5d7a2a2ca4 100644 (file)
@@ -26,7 +26,7 @@ import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatDialogFragment;
 
 import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.model.FutureDates;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
 import java.util.Calendar;
@@ -54,8 +54,8 @@ public class DatePickerFragment extends AppCompatDialogFragment
         else
             this.maxDate = maxDate.toDate().getTime();
     }
-    public void setFutureDates(MobileLedgerProfile.FutureDates futureDates) {
-        if (futureDates == MobileLedgerProfile.FutureDates.All) {
+    public void setFutureDates(FutureDates futureDates) {
+        if (futureDates == FutureDates.All) {
             maxDate = Long.MAX_VALUE;
         }
         else {
index eb7a064160556a4479375acdc82ab2ba55fae7c5..c860e41b4f4e691e7011d3ad60bf22ae8c7ff9d9 100644 (file)
@@ -27,47 +27,30 @@ import androidx.lifecycle.ViewModel;
 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
 import net.ktnx.mobileledger.async.TransactionAccumulator;
 import net.ktnx.mobileledger.async.UpdateTransactionsTask;
-import net.ktnx.mobileledger.model.AccountListItem;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
 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.Locker;
 import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 
-import static net.ktnx.mobileledger.utils.Logger.debug;
-
 public class MainModel extends ViewModel {
     public final MutableLiveData<Integer> foundTransactionItemIndex = new MutableLiveData<>(null);
     private final MutableLiveData<Boolean> updatingFlag = new MutableLiveData<>(false);
     private final MutableLiveData<String> accountFilter = new MutableLiveData<>();
     private final MutableLiveData<List<TransactionListItem>> displayedTransactions =
             new MutableLiveData<>(new ArrayList<>());
-    private final MutableLiveData<List<AccountListItem>> displayedAccounts =
-            new MutableLiveData<>();
-    private final Locker accountsLocker = new Locker();
     private final MutableLiveData<String> updateError = new MutableLiveData<>();
-    private MobileLedgerProfile profile;
-    private final List<LedgerAccount> allAccounts = new ArrayList<>();
     private SimpleDate firstTransactionDate;
     private SimpleDate lastTransactionDate;
     transient private RetrieveTransactionsTask retrieveTransactionsTask;
     transient private Thread displayedAccountsUpdater;
     private TransactionsDisplayedFilter displayedTransactionsUpdater;
-    private void setLastUpdateStamp(long transactionCount) {
-        debug("db", "Updating transaction value stamp");
-        Date now = new Date();
-        profile.setLongOption(MLDB.OPT_LAST_SCRAPE, now.getTime());
-        Data.lastUpdateDate.postValue(now);
-    }
     public void scheduleTransactionListReload() {
         UpdateTransactionsTask task = new UpdateTransactionsTask();
         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
@@ -78,16 +61,12 @@ public class MainModel extends ViewModel {
     public LiveData<String> getUpdateError() {
         return updateError;
     }
-    public void setProfile(MobileLedgerProfile profile) {
-        stopTransactionsRetrieval();
-        this.profile = profile;
-    }
     public LiveData<List<TransactionListItem>> getDisplayedTransactions() {
         return displayedTransactions;
     }
-    public void setDisplayedTransactions(List<TransactionListItem> list, int transactionCount) {
+    public void setDisplayedTransactions(List<TransactionListItem> list) {
         displayedTransactions.postValue(list);
-        Data.lastUpdateTransactionCount.postValue(transactionCount);
+        Data.lastUpdateTransactionCount.postValue(list.size());
     }
     public SimpleDate getFirstTransactionDate() {
         return firstTransactionDate;
@@ -124,9 +103,9 @@ public class MainModel extends ViewModel {
             Logger.debug("db", "Ignoring request for transaction retrieval - already active");
             return;
         }
-        MobileLedgerProfile profile = Data.getProfile();
+        Profile profile = Data.getProfile();
 
-        retrieveTransactionsTask = new RetrieveTransactionsTask(this, profile, allAccounts);
+        retrieveTransactionsTask = new RetrieveTransactionsTask(this, profile);
         Logger.debug("db", "Created a background transaction retrieval task");
 
         retrieveTransactionsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -138,21 +117,6 @@ public class MainModel extends ViewModel {
     public void transactionRetrievalDone() {
         retrieveTransactionsTask = null;
     }
-    public synchronized Locker lockAccountsForWriting() {
-        accountsLocker.lockForWriting();
-        return accountsLocker;
-    }
-    public LiveData<List<AccountListItem>> getDisplayedAccounts() {
-        return displayedAccounts;
-    }
-    public synchronized void setAndStoreAccountAndTransactionListFromWeb(
-            List<LedgerAccount> accounts, List<LedgerTransaction> transactions) {
-        profile.storeAccountAndTransactionListAsync(accounts, transactions);
-
-        setLastUpdateStamp(transactions.size());
-
-        updateDisplayedTransactionsFromWeb(transactions);
-    }
     synchronized public void updateDisplayedTransactionsFromWeb(List<LedgerTransaction> list) {
         if (displayedTransactionsUpdater != null) {
             displayedTransactionsUpdater.interrupt();
@@ -163,7 +127,6 @@ public class MainModel extends ViewModel {
     public void clearUpdateError() {
         updateError.postValue(null);
     }
-    public void clearAccounts() { displayedAccounts.postValue(new ArrayList<>()); }
     public void clearTransactions() {
         displayedTransactions.setValue(new ArrayList<>());
     }
index f681f9990af8ea7149c5f51548249c57ac39dbc5..e464e73e674d8f9945583fd46df1cc264d3f2e5a 100644 (file)
@@ -17,8 +17,6 @@
 
 package net.ktnx.mobileledger.ui;
 
-import net.ktnx.mobileledger.model.Currency;
-
 /**
  * This interface must be implemented by activities that contain this
  * fragment to allow an interaction in this fragment to be communicated
@@ -30,5 +28,5 @@ import net.ktnx.mobileledger.model.Currency;
  * >Communicating with Other Fragments</a> for more information.
  */
 public interface OnCurrencyLongClickListener {
-    void onCurrencyLongClick(Currency item);
+    void onCurrencyLongClick(String item);
 }
index c12f32a2b517ae08455f1331413be3bb88fcd5a3..94e417e26ddd3a2a8d4fefb6fef9757332dbaec1 100644 (file)
@@ -17,8 +17,6 @@
 
 package net.ktnx.mobileledger.ui;
 
-import net.ktnx.mobileledger.model.Currency;
-
 /**
  * This interface must be implemented by activities that contain this
  * fragment to allow an interaction in this fragment to be communicated
@@ -30,5 +28,5 @@ import net.ktnx.mobileledger.model.Currency;
  * >Communicating with Other Fragments</a> for more information.
  */
 public interface OnCurrencySelectedListener {
-    void onCurrencySelected(Currency item);
+    void onCurrencySelected(String item);
 }
index 9d9c2c64bc4c1689facb75b83775811c62d67663..d5964e4b3ce53002f50a1224391ca862bf500433 100644 (file)
@@ -34,6 +34,7 @@ import androidx.recyclerview.widget.RecyclerView;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.db.AccountWithAmounts;
 import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.AccountListItem;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerAccount;
@@ -104,10 +105,12 @@ public class AccountSummaryFragment extends MobileLedgerListFragment {
             model.scheduleTransactionListRetrieval();
         });
 
+        Data.observeProfile(this, this::onProfileChanged);
+    }
+    private void onProfileChanged(Profile profile) {
         DB.get()
           .getAccountDAO()
-          .getAllWithAmounts(Data.getProfile()
-                                 .getId())
+          .getAllWithAmounts(profile.getId())
           .observe(getViewLifecycleOwner(), list -> AsyncTask.execute(() -> {
               List<AccountListItem> adapterList = new ArrayList<>();
               adapterList.add(new AccountListItem.Header(Data.lastAccountsUpdateText));
index 28a1af842325996c663d993083c8b9f54620840b..945e81c51043d0865a80d0d83bb4344b1d22681d 100644 (file)
@@ -34,6 +34,7 @@ import android.view.animation.AnimationUtils;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.app.ActionBarDrawerToggle;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.view.GravityCompat;
@@ -51,12 +52,14 @@ import com.google.android.material.snackbar.Snackbar;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
 import net.ktnx.mobileledger.databinding.ActivityMainBinding;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.FabManager;
 import net.ktnx.mobileledger.ui.MainModel;
 import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
 import net.ktnx.mobileledger.ui.new_transaction.NewTransactionActivity;
+import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity;
 import net.ktnx.mobileledger.ui.profiles.ProfilesRecyclerViewAdapter;
 import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
 import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
@@ -89,7 +92,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
     private DrawerLayout.SimpleDrawerListener drawerListener;
     private ActionBarDrawerToggle barDrawerToggle;
     private ViewPager2.OnPageChangeCallback pageChangeCallback;
-    private MobileLedgerProfile profile;
+    private Profile profile;
     private MainModel mainModel;
     private ActivityMainBinding b;
     private int fabVerticalOffset;
@@ -150,6 +153,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         Data.observeProfile(this, this::onProfileChanged);
 
         Data.profiles.observe(this, this::onProfileListChanged);
+
         Data.backgroundTaskProgress.observe(this, this::onRetrieveProgress);
         Data.backgroundTasksRunning.observe(this, this::onRetrieveRunningChanged);
 
@@ -208,13 +212,11 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                      .setValue(savedInstanceState.getString(STATE_ACC_FILTER, null));
         }
 
-        b.btnNoProfilesAdd.setOnClickListener(
-                v -> MobileLedgerProfile.startEditProfileActivity(this, null));
+        b.btnNoProfilesAdd.setOnClickListener(v -> ProfileDetailActivity.start(this, null));
 
         b.btnAddTransaction.setOnClickListener(this::fabNewTransactionClicked);
 
-        b.navNewProfileButton.setOnClickListener(
-                v -> MobileLedgerProfile.startEditProfileActivity(this, null));
+        b.navNewProfileButton.setOnClickListener(v -> ProfileDetailActivity.start(this, null));
 
         b.transactionListCancelDownload.setOnClickListener(this::onStopTransactionRefreshClick);
 
@@ -332,18 +334,18 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             mainModel.scheduleTransactionListRetrieval();
         }
     }
-    private void createShortcuts(List<MobileLedgerProfile> list) {
+    private void createShortcuts(List<Profile> list) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1)
             return;
 
         ShortcutManager sm = getSystemService(ShortcutManager.class);
         List<ShortcutInfo> shortcuts = new ArrayList<>();
         int i = 0;
-        for (MobileLedgerProfile p : list) {
+        for (Profile p : list) {
             if (shortcuts.size() >= sm.getMaxShortcutCountPerActivity())
                 break;
 
-            if (!p.isPostingPermitted())
+            if (!p.permitPosting())
                 continue;
 
             final ShortcutInfo.Builder builder =
@@ -356,7 +358,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                                              ProfileThemedActivity.PARAM_PROFILE_ID, p.getId())
                                                                           .putExtra(
                                                                                   ProfileThemedActivity.PARAM_THEME,
-                                                                                  p.getThemeHue()))
+                                                                                  p.getTheme()))
                                      .setRank(i)
                                      .build();
             shortcuts.add(si);
@@ -364,7 +366,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
         sm.setDynamicShortcuts(shortcuts);
     }
-    private void onProfileListChanged(List<MobileLedgerProfile> newList) {
+    private void onProfileListChanged(List<Profile> newList) {
         if ((newList == null) || newList.isEmpty()) {
             b.noProfilesLayout.setVisibility(View.VISIBLE);
             b.mainAppLayout.setVisibility(View.GONE);
@@ -378,35 +380,31 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                 (int) (getResources().getDimension(R.dimen.thumb_row_height) * newList.size()));
 
         Logger.debug("profiles", "profile list changed");
-        mProfileListAdapter.notifyDataSetChanged();
+        mProfileListAdapter.setProfileList(newList);
 
         createShortcuts(newList);
     }
     /**
      * called when the current profile has changed
      */
-    private void onProfileChanged(MobileLedgerProfile profile) {
+    private void onProfileChanged(@Nullable Profile newProfile) {
         if (this.profile == null) {
-            if (profile == null)
+            if (newProfile == null)
                 return;
         }
         else {
-            if (this.profile.equals(profile))
+            if (this.profile.equals(newProfile))
                 return;
         }
 
-        boolean haveProfile = profile != null;
+        boolean haveProfile = newProfile != null;
 
         if (haveProfile)
-            setTitle(profile.getName());
+            setTitle(newProfile.getName());
         else
             setTitle(R.string.app_name);
 
-        mainModel.setProfile(profile);
-
-        this.profile = profile;
-
-        int newProfileTheme = haveProfile ? profile.getThemeHue() : -1;
+        int newProfileTheme = haveProfile ? newProfile.getTheme() : -1;
         if (newProfileTheme != Colors.profileThemeId) {
             Logger.debug("profiles",
                     String.format(Locale.ENGLISH, "profile theme %d → %d", Colors.profileThemeId,
@@ -418,19 +416,18 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             return;
         }
 
+        final boolean sameProfileId = (newProfile != null) && (this.profile != null) &&
+                                      this.profile.getId() == newProfile.getId();
+
+        this.profile = newProfile;
+
         b.noProfilesLayout.setVisibility(haveProfile ? View.GONE : View.VISIBLE);
         b.pagerLayout.setVisibility(haveProfile ? View.VISIBLE : View.VISIBLE);
 
         mProfileListAdapter.notifyDataSetChanged();
 
-        mainModel.clearAccounts();
-        mainModel.clearTransactions();
-
         if (haveProfile) {
-            Logger.debug("transactions", "requesting list reload");
-            mainModel.scheduleTransactionListReload();
-
-            if (profile.isPostingPermitted()) {
+            if (newProfile.permitPosting()) {
                 b.toolbar.setSubtitle(null);
                 b.btnAddTransaction.show();
             }
@@ -445,6 +442,21 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
 
         updateLastUpdateTextFromDB();
+
+        if (sameProfileId) {
+            Logger.debug(TAG, String.format(Locale.ROOT, "Short-cut profile 'changed' to %d",
+                    newProfile.getId()));
+            return;
+        }
+
+        mainModel.stopTransactionsRetrieval();
+
+        mainModel.clearTransactions();
+
+        if (haveProfile) {
+            Logger.debug("transactions", "requesting list reload");
+            mainModel.scheduleTransactionListReload();
+        }
     }
     private void profileThemeChanged() {
         // un-hook all observed LiveData
@@ -460,7 +472,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
     public void fabNewTransactionClicked(View view) {
         Intent intent = new Intent(this, NewTransactionActivity.class);
         intent.putExtra(ProfileThemedActivity.PARAM_PROFILE_ID, profile.getId());
-        intent.putExtra(ProfileThemedActivity.PARAM_THEME, profile.getThemeHue());
+        intent.putExtra(ProfileThemedActivity.PARAM_THEME, profile.getTheme());
         startActivity(intent);
         overridePendingTransition(R.anim.slide_in_up, R.anim.dummy);
     }
@@ -523,18 +535,30 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         if (profile == null)
             return;
 
-        long lastUpdate = profile.getLongOption(MLDB.OPT_LAST_SCRAPE, 0L);
-
-        Logger.debug("transactions", String.format(Locale.ENGLISH, "Last update = %d", lastUpdate));
-        if (lastUpdate == 0) {
-            Data.lastUpdateDate.postValue(null);
-        }
-        else {
-            Data.lastUpdateDate.postValue(new Date(lastUpdate));
-        }
-
-        scheduleDataRetrievalIfStale(lastUpdate);
-
+        DB.get()
+          .getOptionDAO()
+          .load(profile.getId(), MLDB.OPT_LAST_SCRAPE)
+          .observe(this, opt -> {
+              long lastUpdate = 0;
+              if (opt != null) {
+                  try {
+                      lastUpdate = Long.parseLong(opt.getValue());
+                  }
+                  catch (NumberFormatException ex) {
+                      Logger.debug(TAG, String.format("Error parsing '%s' as long", opt.getValue()),
+                              ex);
+                  }
+              }
+
+              if (lastUpdate == 0) {
+                  Data.lastUpdateDate.postValue(null);
+              }
+              else {
+                  Data.lastUpdateDate.postValue(new Date(lastUpdate));
+              }
+
+              scheduleDataRetrievalIfStale(lastUpdate);
+          });
     }
     private void refreshLastUpdateInfo() {
         final int formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
@@ -601,7 +625,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                 builder.setMessage(error);
                 builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
                     Logger.debug("error", "will start profile editor");
-                    MobileLedgerProfile.startEditProfileActivity(this, profile);
+                    ProfileDetailActivity.start(this, profile);
                 });
                 builder.create()
                        .show();
@@ -643,7 +667,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
     }
     public void fabShouldShow() {
-        if ((profile != null) && profile.isPostingPermitted() && !b.drawerLayout.isOpen())
+        if ((profile != null) && profile.permitPosting() && !b.drawerLayout.isOpen())
             fabManager.showFab();
     }
     @Override
index 4aa60241dd3bab3fda24b661d901f793f7b9763a..43776addf18181f7756edd9c07acaa54447d6f6d 100644 (file)
@@ -28,7 +28,6 @@ import net.ktnx.mobileledger.dao.ProfileDAO;
 import net.ktnx.mobileledger.db.DB;
 import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Logger;
 
@@ -39,7 +38,7 @@ public class ProfileThemedActivity extends CrashReportingActivity {
     public static final String TAG = "prf-thm-act";
     protected static final String PARAM_PROFILE_ID = "profile-id";
     protected static final String PARAM_THEME = "theme";
-    protected MobileLedgerProfile mProfile;
+    protected Profile mProfile;
     private boolean themeSetUp = false;
     private boolean mIgnoreProfileChange;
     private int mThemeHue;
@@ -81,7 +80,7 @@ public class ProfileThemedActivity extends CrashReportingActivity {
             }
 
             mProfile = profile;
-            int hue = profile.getThemeHue();
+            int hue = profile.getTheme();
 
             if (hue != mThemeHue) {
                 storeProfilePref(profile);
@@ -91,8 +90,8 @@ public class ProfileThemedActivity extends CrashReportingActivity {
 
         super.onCreate(savedInstanceState);
     }
-    public void storeProfilePref(MobileLedgerProfile profile) {
-        App.storeStartupProfileAndTheme(profile.getId(), profile.getThemeHue());
+    public void storeProfilePref(Profile profile) {
+        App.storeStartupProfileAndTheme(profile.getId(), profile.getTheme());
     }
     protected void initProfile() {
         long profileId = App.getStartupProfile();
@@ -119,6 +118,6 @@ public class ProfileThemedActivity extends CrashReportingActivity {
             profile = dao.getAnySync();
         }
 
-        Data.postCurrentProfile(MobileLedgerProfile.fromDBO(profile));
+        Data.postCurrentProfile(profile);
     }
 }
index 9b35b947c4e402c8243b9934316995fc27304f86..495a328f9a2aef6c30c7231db3333e6c3462806f 100644 (file)
@@ -25,7 +25,7 @@ import android.os.Handler;
 import androidx.annotation.Nullable;
 
 import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.db.DB;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.MobileLedgerDatabase;
 
@@ -99,7 +99,7 @@ public class SplashActivity extends CrashReportingActivity {
     private static class DatabaseInitTask extends AsyncTask<Void, Void, Void> {
         @Override
         protected Void doInBackground(Void... voids) {
-            MobileLedgerProfile.loadAllFromDB(0);
+            long ignored = DB.get().getProfileDAO().getProfileCountSync();
 
             return null;
         }
index 870cf7eec141bbb505088802df1cf8f6f078f866..4e3a6cf8ef95a2a45896bb623af36375e5daeb3f 100644 (file)
@@ -177,8 +177,8 @@ class NewTransactionAccountRowItemHolder extends NewTransactionItemViewHolder {
         b.currencyButton.setOnClickListener(v -> {
             CurrencySelectorFragment cpf = new CurrencySelectorFragment();
             cpf.showPositionAndPadding();
-            cpf.setOnCurrencySelectedListener(c -> adapter.setItemCurrency(getAdapterPosition(),
-                    (c == null) ? null : c.getName()));
+            cpf.setOnCurrencySelectedListener(
+                    c -> adapter.setItemCurrency(getAdapterPosition(), c));
             cpf.show(activity.getSupportFragmentManager(), "currency-selector");
         });
 
index 11d014d369bd983144220d76a6267846a92e6d9e..791bbcefa5a5cc44300caac28c04b133af08ece0 100644 (file)
@@ -20,17 +20,15 @@ package net.ktnx.mobileledger.ui.new_transaction;
 import android.content.Context;
 import android.content.Intent;
 import android.database.AbstractCursor;
-import android.database.Cursor;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.ParcelFormatException;
-import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 
 import androidx.activity.result.ActivityResultLauncher;
-import androidx.annotation.NonNull;
 import androidx.core.view.MenuCompat;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.ViewModelProvider;
@@ -38,7 +36,6 @@ import androidx.navigation.NavController;
 import androidx.navigation.fragment.NavHostFragment;
 
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.google.android.material.snackbar.Snackbar;
 
 import net.ktnx.mobileledger.BuildConfig;
 import net.ktnx.mobileledger.R;
@@ -46,9 +43,11 @@ import net.ktnx.mobileledger.async.AsyncCrasher;
 import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
 import net.ktnx.mobileledger.async.SendTransactionTask;
 import net.ktnx.mobileledger.async.TaskCallback;
+import net.ktnx.mobileledger.dao.TransactionDAO;
 import net.ktnx.mobileledger.databinding.ActivityNewTransactionBinding;
 import net.ktnx.mobileledger.db.DB;
 import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.MatchedTemplate;
@@ -57,7 +56,6 @@ import net.ktnx.mobileledger.ui.QR;
 import net.ktnx.mobileledger.ui.activity.ProfileThemedActivity;
 import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
 import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.Misc;
 
 import java.util.ArrayList;
@@ -349,86 +347,25 @@ public class NewTransactionActivity extends ProfileThemedActivity
         if (!model.accountListIsEmpty())
             return;
 
-        String accFilter = mProfile.getPreferredAccountsFilter();
+        AsyncTask.execute(() -> {
+            String accFilter = mProfile.getPreferredAccountsFilter();
 
-        ArrayList<String> params = new ArrayList<>();
-        StringBuilder sb = new StringBuilder("select t.profile, t.id from transactions t");
+            TransactionDAO trDao = DB.get()
+                                     .getTransactionDAO();
 
-        if (!TextUtils.isEmpty(accFilter)) {
-            sb.append(" JOIN transaction_accounts ta")
-              .append(" ON ta.profile = t.profile")
-              .append(" AND ta.transaction_id = t.id");
-        }
-
-        sb.append(" WHERE t.description=?");
-        params.add(description);
-
-        if (!TextUtils.isEmpty(accFilter)) {
-            sb.append(" AND ta.account_name LIKE '%'||?||'%'");
-            params.add(accFilter);
-        }
+            TransactionWithAccounts tr;
 
-        sb.append(" ORDER BY t.year desc, t.month desc, t.day desc LIMIT 1");
-
-        final String sql = sb.toString();
-        debug("description", sql);
-        debug("description", params.toString());
-
-        // FIXME: handle exceptions?
-        MLDB.queryInBackground(sql, params.toArray(new String[]{}), new MLDB.CallbackHelper() {
-            @Override
-            public void onStart() {
-                model.incrementBusyCounter();
-            }
-            @Override
-            public void onDone() {
-                model.decrementBusyCounter();
-            }
-            @Override
-            public boolean onRow(@NonNull Cursor cursor) {
-                final long profileId = cursor.getLong(0);
-                final int transactionId = cursor.getInt(1);
-                runOnUiThread(() -> model.loadTransactionIntoModel(profileId, transactionId));
-                return false; // limit 1, by the way
-            }
-            @Override
-            public void onNoRows() {
-                if (TextUtils.isEmpty(accFilter))
+            if (Misc.emptyIsNull(accFilter) != null) {
+                tr = trDao.getFirstByDescriptionHavingAccountSync(description, accFilter);
+                if (tr != null) {
+                    model.loadTransactionIntoModel(tr);
                     return;
-
-                debug("description", "Trying transaction search without preferred account filter");
-
-                final String broaderSql =
-                        "select t.profile, t.id from transactions t where t.description=?" +
-                        " ORDER BY year desc, month desc, day desc LIMIT 1";
-                params.remove(1);
-                debug("description", broaderSql);
-                debug("description", description);
-
-                runOnUiThread(() -> Snackbar.make(b.newTransactionNav,
-                        R.string.ignoring_preferred_account, Snackbar.LENGTH_INDEFINITE)
-                                            .show());
-
-                MLDB.queryInBackground(broaderSql, new String[]{description},
-                        new MLDB.CallbackHelper() {
-                            @Override
-                            public void onStart() {
-                                model.incrementBusyCounter();
-                            }
-                            @Override
-                            public boolean onRow(@NonNull Cursor cursor) {
-                                final long profileId = cursor.getLong(0);
-                                final int transactionId = cursor.getInt(1);
-                                runOnUiThread(() -> model.loadTransactionIntoModel(profileId,
-                                        transactionId));
-                                return false;
-                            }
-                            @Override
-                            public void onDone() {
-                                model.decrementBusyCounter();
-                            }
-                        });
+                }
             }
+
+            tr = trDao.getFirstByDescriptionSync(description);
+            if (tr != null)
+                model.loadTransactionIntoModel(tr);
         });
     }
     private void onFabPressed() {
index c54c11798fde11acea4b9fae083ca985792838b4..7741d18ac6656902485662583ba02d924078761c 100644 (file)
@@ -41,12 +41,13 @@ import androidx.recyclerview.widget.RecyclerView;
 import com.google.android.material.snackbar.Snackbar;
 
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.json.API;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransaction;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.FabManager;
 import net.ktnx.mobileledger.ui.QR;
+import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity;
 import net.ktnx.mobileledger.utils.Logger;
 
 import org.jetbrains.annotations.NotNull;
@@ -64,7 +65,7 @@ public class NewTransactionFragment extends Fragment {
     private NewTransactionItemsAdapter listAdapter;
     private NewTransactionModel viewModel;
     private OnNewTransactionFragmentInteractionListener mListener;
-    private MobileLedgerProfile mProfile;
+    private Profile mProfile;
     public NewTransactionFragment() {
         // Required empty public constructor
         setHasOptionsMenu(true);
@@ -163,15 +164,15 @@ public class NewTransactionFragment extends Fragment {
                            .append("\n\n")
                            .append(error)
                            .append("\n\n");
-                    if (mProfile.getApiVersion()
-                                .equals(API.auto))
+                    if (API.valueOf(mProfile.getApiVersion())
+                           .equals(API.auto))
                         message.append(
                                 resources.getString(R.string.err_json_send_error_unsupported));
                     else {
                         message.append(resources.getString(R.string.err_json_send_error_tail));
                         builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
                             Logger.debug("error", "will start profile editor");
-                            MobileLedgerProfile.startEditProfileActivity(context, mProfile);
+                            ProfileDetailActivity.start(context, mProfile);
                         });
                     }
                     builder.setMessage(message);
index 81afa05dc69fbe938c22ee4229f0fe8e592d43bd..feaf745971624ac937daba3f966d991cdb0c8b5b 100644 (file)
@@ -34,6 +34,7 @@ import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.databinding.NewTransactionHeaderRowBinding;
 import net.ktnx.mobileledger.db.TransactionDescriptionAutocompleteAdapter;
 import net.ktnx.mobileledger.model.Data;
+import net.ktnx.mobileledger.model.FutureDates;
 import net.ktnx.mobileledger.ui.DatePickerFragment;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
@@ -282,7 +283,7 @@ class NewTransactionHeaderItemHolder extends NewTransactionItemViewHolder
     }
     private void pickTransactionDate() {
         DatePickerFragment picker = new DatePickerFragment();
-        picker.setFutureDates(mProfile.getFutureDates());
+        picker.setFutureDates(FutureDates.valueOf(mProfile.getFutureDates()));
         picker.setOnDatePickedListener(this);
         picker.setCurrentDateFromText(b.newTransactionDate.getText());
         picker.show(((NewTransactionActivity) b.getRoot()
index 38c2796a6b3cdec7183e984a5d94ea5aacfcb18a..f78fdbe81efc680cea373124bb620f4a3c2d58eb 100644 (file)
@@ -22,12 +22,12 @@ import android.view.View;
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 
 abstract class NewTransactionItemViewHolder extends RecyclerView.ViewHolder {
     final NewTransactionItemsAdapter mAdapter;
-    final MobileLedgerProfile mProfile;
+    final Profile mProfile;
     public NewTransactionItemViewHolder(@NonNull View itemView,
                                         NewTransactionItemsAdapter adapter) {
         super(itemView);
index 3760d70f2cf8e141926f267920e42cb634f4352d..73ddc8fe37f929ca495cf1158f9eafb0e474db1b 100644 (file)
@@ -28,7 +28,7 @@ import androidx.recyclerview.widget.RecyclerView;
 
 import net.ktnx.mobileledger.databinding.NewTransactionAccountRowBinding;
 import net.ktnx.mobileledger.databinding.NewTransactionHeaderRowBinding;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.utils.Logger;
 
 import java.util.List;
@@ -61,9 +61,9 @@ class NewTransactionItemsAdapter extends RecyclerView.Adapter<NewTransactionItem
                     return oldItem.equalContents(newItem);
                 }
             });
-    private MobileLedgerProfile mProfile;
+    private Profile mProfile;
     private int checkHoldCounter = 0;
-    NewTransactionItemsAdapter(NewTransactionModel viewModel, MobileLedgerProfile profile) {
+    NewTransactionItemsAdapter(NewTransactionModel viewModel, Profile profile) {
         super();
         setHasStableIds(true);
         model = viewModel;
@@ -139,7 +139,7 @@ class NewTransactionItemsAdapter extends RecyclerView.Adapter<NewTransactionItem
                      .get(position)
                      .getId();
     }
-    public void setProfile(MobileLedgerProfile profile) {
+    public void setProfile(Profile profile) {
         mProfile = profile;
     }
     @NonNull
index fdcab48788b94f6336f41254298f92c02748e4c6..7ff061a6ea30994bb15437d3297ee4683334f795 100644 (file)
@@ -32,14 +32,15 @@ import androidx.lifecycle.ViewModel;
 
 import net.ktnx.mobileledger.BuildConfig;
 import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.db.TemplateAccount;
 import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.InertMutableLiveData;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.model.MatchedTemplate;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
@@ -73,7 +74,7 @@ public class NewTransactionModel extends ViewModel {
     private final MutableLiveData<Boolean> simulateSave = new InertMutableLiveData<>(false);
     private final AtomicInteger busyCounter = new AtomicInteger(0);
     private final MutableLiveData<Boolean> busyFlag = new InertMutableLiveData<>(false);
-    private final Observer<MobileLedgerProfile> profileObserver = profile -> {
+    private final Observer<Profile> profileObserver = profile -> {
         showCurrency.postValue(profile.getShowCommodityByDefault());
         showComments.postValue(profile.getShowCommentsByDefault());
     };
@@ -461,23 +462,19 @@ public class NewTransactionModel extends ViewModel {
 
         return tr;
     }
-    void loadTransactionIntoModel(long profileId, int transactionId) {
+    void loadTransactionIntoModel(@NonNull TransactionWithAccounts tr) {
         List<Item> newList = new ArrayList<>();
         Item.resetIdDispenser();
-        LedgerTransaction tr;
-        MobileLedgerProfile profile = Data.getProfile(profileId);
-        if (profile == null)
-            throw new RuntimeException(String.format(
-                    "Unable to find profile %s, which is supposed to contain transaction %d",
-                    profileId, transactionId));
 
-        tr = profile.loadTransaction(transactionId);
-        TransactionHead head = new TransactionHead(tr.getDescription());
-        head.setComment(tr.getComment());
+        TransactionHead head = new TransactionHead(tr.transaction.getDescription());
+        head.setComment(tr.transaction.getComment());
 
         newList.add(head);
 
-        List<LedgerTransactionAccount> accounts = tr.getAccounts();
+        List<LedgerTransactionAccount> accounts = new ArrayList<>();
+        for (net.ktnx.mobileledger.db.TransactionAccount acc : tr.accounts) {
+            accounts.add(new LedgerTransactionAccount(acc));
+        }
 
         TransactionAccount firstNegative = null;
         TransactionAccount firstPositive = null;
@@ -526,9 +523,10 @@ public class NewTransactionModel extends ViewModel {
             moveItemLast(newList, singlePositiveIndex);
         }
 
-        setItems(newList);
-
-        noteFocusChanged(1, FocusedElement.Amount);
+        new Handler(Looper.getMainLooper()).post(() -> {
+            setItems(newList);
+            noteFocusChanged(1, FocusedElement.Amount);
+        });
     }
     /**
      * A transaction is submittable if:
index d860a678649bc37833f3cf2f98461b863d2c39b8..d389dd8ab9a52cd55e4e56eb610b1ca2ab77524c 100644 (file)
 
 package net.ktnx.mobileledger.ui.profiles;
 
+import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.annotation.Nullable;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.widget.Toolbar;
 import androidx.lifecycle.ViewModelProvider;
 
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.activity.CrashReportingActivity;
 import net.ktnx.mobileledger.utils.Colors;
 
 import org.jetbrains.annotations.NotNull;
 
-import java.util.ArrayList;
-import java.util.Locale;
-
 import static net.ktnx.mobileledger.utils.Logger.debug;
 
 /**
@@ -45,40 +48,41 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
  * in a ProfileListActivity (not really).
  */
 public class ProfileDetailActivity extends CrashReportingActivity {
-    private MobileLedgerProfile profile = null;
     private ProfileDetailFragment mFragment;
+    public static void start(Context context, @Nullable Profile profile) {
+        Intent starter = new Intent(context, ProfileDetailActivity.class);
+        if (profile != null) {
+            starter.putExtra(ProfileDetailFragment.ARG_ITEM_ID, profile.getId());
+            starter.putExtra(ProfileDetailFragment.ARG_ITEM_ID, profile.getTheme());
+        }
+        context.startActivity(starter);
+    }
     @NotNull
     private ProfileDetailModel getModel() {
         return new ViewModelProvider(this).get(ProfileDetailModel.class);
     }
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        final int index = getIntent().getIntExtra(ProfileDetailFragment.ARG_ITEM_ID, -1);
-
-        if (index != -1) {
-            ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
-            if (profiles != null) {
-                profile = profiles.get(index);
-                if (profile == null)
-                    throw new AssertionError(
-                            String.format("Can't get profile " + "(index:%d) from the global list",
-                                    index));
-
-                debug("profiles", String.format(Locale.ENGLISH, "Editing profile %s (%s); hue=%d",
-                        profile.getName(), profile.getId(), profile.getThemeHue()));
-            }
-        }
+        final long id = getIntent().getLongExtra(ProfileDetailFragment.ARG_ITEM_ID, -1);
+
+        if (id == -1)
+            throw new RuntimeException("Invalid or missing profile ID");
+
+        DB.get()
+          .getProfileDAO()
+          .getById(id)
+          .observe(this, this::setProfile);
+
+        int themeHue = getIntent().getIntExtra(ProfileDetailFragment.ARG_HUE, -1);
 
         super.onCreate(savedInstanceState);
-        int themeHue;
-        if (profile != null)
-            themeHue = profile.getThemeHue();
-        else {
+        if (themeHue == -1) {
             themeHue = Colors.getNewProfileThemeHue(Data.profiles.getValue());
         }
         Colors.setupTheme(this, themeHue);
         final ProfileDetailModel model = getModel();
         model.initialThemeHue = themeHue;
+        model.setThemeId(themeHue);
         setContentView(R.layout.activity_profile_detail);
         Toolbar toolbar = findViewById(R.id.detail_toolbar);
         setSupportActionBar(toolbar);
@@ -103,7 +107,6 @@ public class ProfileDetailActivity extends CrashReportingActivity {
             // Create the detail fragment and add it to the activity
             // using a fragment transaction.
             Bundle arguments = new Bundle();
-            arguments.putInt(ProfileDetailFragment.ARG_ITEM_ID, index);
             arguments.putInt(ProfileDetailFragment.ARG_HUE, themeHue);
             mFragment = new ProfileDetailFragment();
             mFragment.setArguments(arguments);
@@ -112,6 +115,17 @@ public class ProfileDetailActivity extends CrashReportingActivity {
                                        .commit();
         }
     }
+    private void setProfile(Profile profile) {
+        ProfileDetailModel model = new ViewModelProvider(this).get(ProfileDetailModel.class);
+        CollapsingToolbarLayout appBarLayout = findViewById(R.id.toolbar_layout);
+        if (appBarLayout != null) {
+            if (profile != null)
+                appBarLayout.setTitle(profile.getName());
+            else
+                appBarLayout.setTitle(getResources().getString(R.string.new_profile_title));
+        }
+        model.setValuesFromProfile(profile);
+    }
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
index f5b48aef3e22c5dc4d0ec52f9ef8670f6bbdd485..100918956c71b71659185b47464182393596237d 100644 (file)
@@ -40,16 +40,18 @@ import androidx.fragment.app.FragmentActivity;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.ViewModelProvider;
 
-import com.google.android.material.appbar.CollapsingToolbarLayout;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.textfield.TextInputLayout;
 
 import net.ktnx.mobileledger.BuildConfig;
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.dao.ProfileDAO;
 import net.ktnx.mobileledger.databinding.ProfileDetailBinding;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.json.API;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.model.FutureDates;
 import net.ktnx.mobileledger.ui.CurrencySelectorFragment;
 import net.ktnx.mobileledger.ui.HueRingDialog;
 import net.ktnx.mobileledger.utils.Colors;
@@ -60,8 +62,7 @@ import org.jetbrains.annotations.NotNull;
 
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.Objects;
+import java.util.List;
 
 import static net.ktnx.mobileledger.utils.Logger.debug;
 
@@ -79,7 +80,7 @@ public class ProfileDetailFragment extends Fragment {
     public static final String ARG_HUE = "hue";
     @NonNls
 
-    private MobileLedgerProfile mProfile;
+    private Profile mProfile;
     private boolean defaultCommoditySet;
     private boolean syncingModelFromUI = false;
     private ProfileDetailBinding binding;
@@ -97,7 +98,7 @@ public class ProfileDetailFragment extends Fragment {
         inflater.inflate(R.menu.profile_details, menu);
         final MenuItem menuDeleteProfile = menu.findItem(R.id.menuDelete);
         menuDeleteProfile.setOnMenuItemClickListener(item -> onDeleteProfile());
-        final ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
+        final List<Profile> profiles = Data.profiles.getValue();
 
         if (BuildConfig.DEBUG) {
             final MenuItem menuWipeProfileData = menu.findItem(R.id.menuWipeData);
@@ -111,17 +112,9 @@ public class ProfileDetailFragment extends Fragment {
         builder.setMessage(R.string.remove_profile_dialog_message);
         builder.setPositiveButton(R.string.Remove, (dialog, which) -> {
             debug("profiles", String.format("[fragment] removing profile %s", mProfile.getId()));
-            mProfile.removeFromDB();
-            ArrayList<MobileLedgerProfile> oldList = Data.profiles.getValue();
-            if (oldList == null)
-                throw new AssertionError();
-            ArrayList<MobileLedgerProfile> newList = new ArrayList<>(oldList);
-            newList.remove(mProfile);
-            Data.profiles.setValue(newList);
-            if (mProfile.equals(Data.getProfile())) {
-                debug("profiles", "[fragment] setting current profile to 0");
-                Data.setCurrentProfile(newList.get(0));
-            }
+            ProfileDAO dao = DB.get()
+                               .getProfileDAO();
+            dao.delete(mProfile, () -> dao.updateOrderSync(dao.getAllOrderedSync()));
 
             final FragmentActivity activity = getActivity();
             if (activity != null)
@@ -133,24 +126,8 @@ public class ProfileDetailFragment extends Fragment {
     private boolean onWipeDataMenuClicked() {
         // this is a development option, so no confirmation
         mProfile.wipeAllData();
-        if (mProfile.equals(Data.getProfile()))
-            triggerProfileChange();
         return true;
     }
-    private void triggerProfileChange() {
-        int index = Data.getProfileIndex(mProfile);
-        MobileLedgerProfile newProfile = new MobileLedgerProfile(mProfile);
-        final ArrayList<MobileLedgerProfile> profiles =
-                Objects.requireNonNull(Data.profiles.getValue());
-        profiles.set(index, newProfile);
-
-        ProfilesRecyclerViewAdapter viewAdapter = ProfilesRecyclerViewAdapter.getInstance();
-        if (viewAdapter != null)
-            viewAdapter.notifyItemChanged(index);
-
-        if (mProfile.equals(Data.getProfile()))
-            Data.setCurrentProfile(newProfile);
-    }
     private void hookTextChangeSyncRoutine(TextView view, TextChangeSyncRoutine syncRoutine) {
         view.addTextChangedListener(new TextWatcher() {
             @Override
@@ -176,30 +153,12 @@ public class ProfileDetailFragment extends Fragment {
         if (context == null)
             return;
 
-        if ((getArguments() != null) && getArguments().containsKey(ARG_ITEM_ID)) {
-            int index = getArguments().getInt(ARG_ITEM_ID, -1);
-            ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
-            if ((profiles != null) && (index != -1) && (index < profiles.size()))
-                mProfile = profiles.get(index);
-
-            Activity activity = this.getActivity();
-            if (activity == null)
-                throw new AssertionError();
-            CollapsingToolbarLayout appBarLayout = activity.findViewById(R.id.toolbar_layout);
-            if (appBarLayout != null) {
-                if (mProfile != null)
-                    appBarLayout.setTitle(mProfile.getName());
-                else
-                    appBarLayout.setTitle(getResources().getString(R.string.new_profile_title));
-            }
-        }
-
         final LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
         final ProfileDetailModel model = getModel();
 
         model.observeDefaultCommodity(viewLifecycleOwner, c -> {
             if (c != null)
-                setDefaultCommodity(c.getName());
+                setDefaultCommodity(c);
             else
                 resetDefaultCommodity();
         });
@@ -325,11 +284,6 @@ public class ProfileDetailFragment extends Fragment {
         hookClearErrorOnFocusListener(binding.authUserName, binding.authUserNameLayout);
         hookClearErrorOnFocusListener(binding.password, binding.passwordLayout);
 
-        if (savedInstanceState == null) {
-            model.setValuesFromProfile(mProfile, getArguments().getInt(ARG_HUE, -1));
-        }
-        checkInsecureSchemeWithAuth();
-
         binding.url.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@@ -380,32 +334,32 @@ public class ProfileDetailFragment extends Fragment {
         });
         menu.show();
     }
-    private MobileLedgerProfile.FutureDates futureDatesSettingFromMenuItemId(int itemId) {
+    private FutureDates futureDatesSettingFromMenuItemId(int itemId) {
         if (itemId == R.id.menu_future_dates_7) {
-            return MobileLedgerProfile.FutureDates.OneWeek;
+            return FutureDates.OneWeek;
         }
         else if (itemId == R.id.menu_future_dates_14) {
-            return MobileLedgerProfile.FutureDates.TwoWeeks;
+            return FutureDates.TwoWeeks;
         }
         else if (itemId == R.id.menu_future_dates_30) {
-            return MobileLedgerProfile.FutureDates.OneMonth;
+            return FutureDates.OneMonth;
         }
         else if (itemId == R.id.menu_future_dates_60) {
-            return MobileLedgerProfile.FutureDates.TwoMonths;
+            return FutureDates.TwoMonths;
         }
         else if (itemId == R.id.menu_future_dates_90) {
-            return MobileLedgerProfile.FutureDates.ThreeMonths;
+            return FutureDates.ThreeMonths;
         }
         else if (itemId == R.id.menu_future_dates_180) {
-            return MobileLedgerProfile.FutureDates.SixMonths;
+            return FutureDates.SixMonths;
         }
         else if (itemId == R.id.menu_future_dates_365) {
-            return MobileLedgerProfile.FutureDates.OneYear;
+            return FutureDates.OneYear;
         }
         else if (itemId == R.id.menu_future_dates_all) {
-            return MobileLedgerProfile.FutureDates.All;
+            return FutureDates.All;
         }
-        return MobileLedgerProfile.FutureDates.None;
+        return FutureDates.None;
     }
     @NotNull
     private ProfileDetailModel getModel() {
@@ -416,40 +370,19 @@ public class ProfileDetailFragment extends Fragment {
             return;
 
         ProfileDetailModel model = getModel();
-        final ArrayList<MobileLedgerProfile> profiles =
-                Objects.requireNonNull(Data.profiles.getValue());
+        ProfileDAO dao = DB.get()
+                           .getProfileDAO();
 
         if (mProfile != null) {
-            int pos = Data.profiles.getValue()
-                                   .indexOf(mProfile);
-            mProfile = new MobileLedgerProfile(mProfile);
             model.updateProfile(mProfile);
-            mProfile.storeInDB();
+            dao.update(mProfile, null);
             debug("profiles", "profile stored in DB");
-            profiles.set(pos, mProfile);
 //                debug("profiles", String.format("Selected item is %d", mProfile.getThemeHue()));
-
-            final MobileLedgerProfile currentProfile = Data.getProfile();
-            if (mProfile.getId() == currentProfile.getId()) {
-                Data.setCurrentProfile(mProfile);
-            }
-
-            ProfilesRecyclerViewAdapter viewAdapter = ProfilesRecyclerViewAdapter.getInstance();
-            if (viewAdapter != null)
-                viewAdapter.notifyItemChanged(pos);
         }
         else {
-            mProfile = new MobileLedgerProfile(0);
+            mProfile = new Profile();
             model.updateProfile(mProfile);
-            mProfile.storeInDB();
-            final ArrayList<MobileLedgerProfile> newList = new ArrayList<>(profiles);
-            newList.add(mProfile);
-            Data.profiles.setValue(newList);
-            MobileLedgerProfile.storeProfilesOrder();
-
-            // first profile ever?
-            if (newList.size() == 1)
-                Data.setCurrentProfile(mProfile);
+            dao.insertLast(mProfile, null);
         }
 
         Activity activity = getActivity();
index 92d3ab508dd98930c8fd2fa1a4aec07ef2328312..e1f956acb3b7e52ccde9ab59b2601ce4fcdeb0d6 100644 (file)
@@ -25,10 +25,10 @@ import androidx.lifecycle.Observer;
 import androidx.lifecycle.ViewModel;
 
 import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.json.API;
-import net.ktnx.mobileledger.model.Currency;
+import net.ktnx.mobileledger.model.FutureDates;
 import net.ktnx.mobileledger.model.HledgerVersion;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
@@ -48,9 +48,9 @@ public class ProfileDetailModel extends ViewModel {
     private static final String HTTPS_URL_START = "https://";
     private final MutableLiveData<String> profileName = new MutableLiveData<>();
     private final MutableLiveData<Boolean> postingPermitted = new MutableLiveData<>(true);
-    private final MutableLiveData<Currency> defaultCommodity = new MutableLiveData<>(null);
-    private final MutableLiveData<MobileLedgerProfile.FutureDates> futureDates =
-            new MutableLiveData<>(MobileLedgerProfile.FutureDates.None);
+    private final MutableLiveData<String> defaultCommodity = new MutableLiveData<>(null);
+    private final MutableLiveData<FutureDates> futureDates =
+            new MutableLiveData<>(FutureDates.None);
     private final MutableLiveData<Boolean> showCommodityByDefault = new MutableLiveData<>(false);
     private final MutableLiveData<Boolean> showCommentsByDefault = new MutableLiveData<>(true);
     private final MutableLiveData<Boolean> useAuthentication = new MutableLiveData<>(false);
@@ -97,24 +97,24 @@ public class ProfileDetailModel extends ViewModel {
     void observeShowCommentsByDefault(LifecycleOwner lfo, Observer<Boolean> o) {
         showCommentsByDefault.observe(lfo, o);
     }
-    MobileLedgerProfile.FutureDates getFutureDates() {
+    FutureDates getFutureDates() {
         return futureDates.getValue();
     }
-    void setFutureDates(MobileLedgerProfile.FutureDates newValue) {
+    void setFutureDates(FutureDates newValue) {
         if (newValue != futureDates.getValue())
             futureDates.setValue(newValue);
     }
-    void observeFutureDates(LifecycleOwner lfo, Observer<MobileLedgerProfile.FutureDates> o) {
+    void observeFutureDates(LifecycleOwner lfo, Observer<FutureDates> o) {
         futureDates.observe(lfo, o);
     }
-    Currency getDefaultCommodity() {
+    String getDefaultCommodity() {
         return defaultCommodity.getValue();
     }
-    void setDefaultCommodity(Currency newValue) {
-        if (newValue != defaultCommodity.getValue())
+    void setDefaultCommodity(String newValue) {
+        if (!Misc.equalStrings(newValue, defaultCommodity.getValue()))
             defaultCommodity.setValue(newValue);
     }
-    void observeDefaultCommodity(LifecycleOwner lfo, Observer<Currency> o) {
+    void observeDefaultCommodity(LifecycleOwner lfo, Observer<String> o) {
         defaultCommodity.observe(lfo, o);
     }
     Boolean getShowCommodityByDefault() {
@@ -223,11 +223,10 @@ public class ProfileDetailModel extends ViewModel {
     void observeDetectingHledgerVersion(LifecycleOwner lfo, Observer<Boolean> o) {
         detectingHledgerVersion.observe(lfo, o);
     }
-    void setValuesFromProfile(MobileLedgerProfile mProfile, int newProfileHue) {
-        final int profileThemeId;
+    void setValuesFromProfile(Profile mProfile) {
         if (mProfile != null) {
             profileName.setValue(mProfile.getName());
-            postingPermitted.setValue(mProfile.isPostingPermitted());
+            postingPermitted.setValue(mProfile.permitPosting());
             showCommentsByDefault.setValue(mProfile.getShowCommentsByDefault());
             showCommodityByDefault.setValue(mProfile.getShowCommodityByDefault());
             {
@@ -235,17 +234,21 @@ public class ProfileDetailModel extends ViewModel {
                 if (TextUtils.isEmpty(comm))
                     setDefaultCommodity(null);
                 else
-                    setDefaultCommodity(new Currency(-1, comm));
+                    setDefaultCommodity(comm);
             }
-            futureDates.setValue(mProfile.getFutureDates());
-            apiVersion.setValue(mProfile.getApiVersion());
+            futureDates.setValue(
+                    FutureDates.valueOf(mProfile.getFutureDates()));
+            apiVersion.setValue(API.valueOf(mProfile.getApiVersion()));
             url.setValue(mProfile.getUrl());
-            useAuthentication.setValue(mProfile.isAuthEnabled());
-            authUserName.setValue(mProfile.isAuthEnabled() ? mProfile.getAuthUserName() : "");
-            authPassword.setValue(mProfile.isAuthEnabled() ? mProfile.getAuthPassword() : "");
+            useAuthentication.setValue(mProfile.useAuthentication());
+            authUserName.setValue(mProfile.useAuthentication() ? mProfile.getAuthUser() : "");
+            authPassword.setValue(mProfile.useAuthentication() ? mProfile.getAuthPassword() : "");
             preferredAccountsFilter.setValue(mProfile.getPreferredAccountsFilter());
-            themeId.setValue(mProfile.getThemeHue());
-            detectedVersion.setValue(mProfile.getDetectedVersion());
+            themeId.setValue(mProfile.getTheme());
+            detectedVersion.setValue(mProfile.detectedVersionPre_1_19() ? new HledgerVersion(true)
+                                                                        : new HledgerVersion(
+                                                                                mProfile.getDetectedVersionMajor(),
+                                                                                mProfile.getDetectedVersionMinor()));
         }
         else {
             profileName.setValue(null);
@@ -253,32 +256,35 @@ public class ProfileDetailModel extends ViewModel {
             postingPermitted.setValue(true);
             showCommentsByDefault.setValue(true);
             showCommodityByDefault.setValue(false);
-            setFutureDates(MobileLedgerProfile.FutureDates.None);
+            setFutureDates(FutureDates.None);
             setApiVersion(API.auto);
             useAuthentication.setValue(false);
             authUserName.setValue("");
             authPassword.setValue("");
             preferredAccountsFilter.setValue(null);
-            themeId.setValue(newProfileHue);
             detectedVersion.setValue(null);
         }
     }
-    void updateProfile(MobileLedgerProfile mProfile) {
+    void updateProfile(Profile mProfile) {
         mProfile.setName(profileName.getValue());
         mProfile.setUrl(url.getValue());
-        mProfile.setPostingPermitted(postingPermitted.getValue());
+        mProfile.setPermitPosting(postingPermitted.getValue());
         mProfile.setShowCommentsByDefault(showCommentsByDefault.getValue());
-        Currency c = defaultCommodity.getValue();
-        mProfile.setDefaultCommodity((c == null) ? null : c.getName());
+        mProfile.setDefaultCommodity(defaultCommodity.getValue());
         mProfile.setShowCommodityByDefault(showCommodityByDefault.getValue());
         mProfile.setPreferredAccountsFilter(preferredAccountsFilter.getValue());
-        mProfile.setAuthEnabled(useAuthentication.getValue());
-        mProfile.setAuthUserName(authUserName.getValue());
+        mProfile.setUseAuthentication(useAuthentication.getValue());
+        mProfile.setAuthUser(authUserName.getValue());
         mProfile.setAuthPassword(authPassword.getValue());
-        mProfile.setThemeHue(themeId.getValue());
-        mProfile.setFutureDates(futureDates.getValue());
-        mProfile.setApiVersion(apiVersion.getValue());
-        mProfile.setDetectedVersion(detectedVersion.getValue());
+        mProfile.setTheme(themeId.getValue());
+        mProfile.setFutureDates(futureDates.getValue()
+                                           .toInt());
+        mProfile.setApiVersion(apiVersion.getValue()
+                                         .toInt());
+        HledgerVersion version = detectedVersion.getValue();
+        mProfile.setDetectedVersionPre_1_19(version.isPre_1_20_1());
+        mProfile.setDetectedVersionMajor(version.getMajor());
+        mProfile.setDetectedVersionMinor(version.getMinor());
     }
     synchronized public void triggerVersionDetection() {
         if (versionDetectionThread != null)
@@ -297,7 +303,7 @@ public class ProfileDetailModel extends ViewModel {
         }
         private HledgerVersion detectVersion() {
             App.setAuthenticationDataFromProfileModel(model);
-            HttpURLConnection http = null;
+            HttpURLConnection http;
             try {
                 http = NetworkUtil.prepareConnection(model.getUrl(), "version",
                         model.getUseAuthentication());
index e7346b1b0487a4d4d09fea3ee474047f43592efa..688cd6d42f69deee4ab5d518d73b5445e0d284e6 100644 (file)
@@ -17,8 +17,6 @@
 
 package net.ktnx.mobileledger.ui.profiles;
 
-import android.content.Context;
-import android.content.Intent;
 import android.graphics.drawable.ColorDrawable;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -30,39 +28,48 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.lifecycle.MutableLiveData;
+import androidx.recyclerview.widget.AsyncListDiffer;
+import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.RecyclerView;
 
 import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
-import net.ktnx.mobileledger.ui.activity.MainActivity;
 import net.ktnx.mobileledger.utils.Colors;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 import static net.ktnx.mobileledger.utils.Logger.debug;
 
 public class ProfilesRecyclerViewAdapter
         extends RecyclerView.Adapter<ProfilesRecyclerViewAdapter.ProfileListViewHolder> {
-    private static WeakReference<ProfilesRecyclerViewAdapter> instanceRef;
     public final MutableLiveData<Boolean> editingProfiles = new MutableLiveData<>(false);
-    private final View.OnClickListener mOnClickListener = view -> {
-        MobileLedgerProfile profile = (MobileLedgerProfile) ((View) view.getParent()).getTag();
-        editProfile(view, profile);
-    };
     private final ItemTouchHelper rearrangeHelper;
+    private final AsyncListDiffer<Profile> listDiffer;
     private RecyclerView recyclerView;
     private boolean animationsEnabled = true;
+
     public ProfilesRecyclerViewAdapter() {
-        instanceRef = new WeakReference<>(this);
         debug("flow", "ProfilesRecyclerViewAdapter.new()");
 
+        setHasStableIds(true);
+        listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<Profile>() {
+            @Override
+            public boolean areItemsTheSame(@NonNull Profile oldItem, @NonNull Profile newItem) {
+                return oldItem.getId() == newItem.getId();
+            }
+            @Override
+            public boolean areContentsTheSame(@NonNull Profile oldItem, @NonNull Profile newItem) {
+                return oldItem.equals(newItem);
+            }
+        });
+
         ItemTouchHelper.Callback cb = new ItemTouchHelper.Callback() {
             @Override
             public int getMovementFlags(@NonNull RecyclerView recyclerView,
@@ -73,13 +80,13 @@ public class ProfilesRecyclerViewAdapter
             public boolean onMove(@NonNull RecyclerView recyclerView,
                                   @NonNull RecyclerView.ViewHolder viewHolder,
                                   @NonNull RecyclerView.ViewHolder target) {
-                final ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
-                if (profiles == null)
-                    throw new AssertionError();
+                final List<Profile> profiles = new ArrayList<>(listDiffer.getCurrentList());
                 Collections.swap(profiles, viewHolder.getAdapterPosition(),
                         target.getAdapterPosition());
-                MobileLedgerProfile.storeProfilesOrder();
-                notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
+                DB.get()
+                  .getProfileDAO()
+                  .updateOrder(profiles, null);
+//                notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
                 return true;
             }
             @Override
@@ -88,9 +95,14 @@ public class ProfilesRecyclerViewAdapter
         };
         rearrangeHelper = new ItemTouchHelper(cb);
     }
-    public static @Nullable
-    ProfilesRecyclerViewAdapter getInstance() {
-        return instanceRef.get();
+    @Override
+    public long getItemId(int position) {
+        return listDiffer.getCurrentList()
+                         .get(position)
+                         .getId();
+    }
+    public void setProfileList(List<Profile> list) {
+        listDiffer.submitList(list);
     }
     public void setAnimationsEnabled(boolean animationsEnabled) {
         this.animationsEnabled = animationsEnabled;
@@ -132,74 +144,22 @@ public class ProfilesRecyclerViewAdapter
         else
             startEditingProfiles();
     }
-    private void editProfile(View view, MobileLedgerProfile profile) {
-        int index = Data.getProfileIndex(profile);
-        Context context = view.getContext();
-        Intent intent = new Intent(context, ProfileDetailActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-        if (index != -1)
-            intent.putExtra(ProfileDetailFragment.ARG_ITEM_ID, index);
-
-        context.startActivity(intent);
-    }
-    private void onProfileRowClicked(View v) {
-        if (editingProfiles())
-            return;
-        MobileLedgerProfile profile = (MobileLedgerProfile) v.getTag();
-        if (profile == null)
-            throw new IllegalStateException("Profile row without associated profile");
-        debug("profiles", "Setting profile to " + profile.getName());
-        if (Data.getProfile() != profile) {
-            Data.drawerOpen.setValue(false);
-            ((MainActivity) v.getContext()).storeProfilePref(profile);
-        }
-        Data.setCurrentProfile(profile);
-    }
     @NonNull
     @Override
     public ProfileListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         View view = LayoutInflater.from(parent.getContext())
                                   .inflate(R.layout.profile_list_content, parent, false);
-        ProfileListViewHolder holder = new ProfileListViewHolder(view);
-
-        holder.mRow.setOnClickListener(this::onProfileRowClicked);
-        holder.mTitle.setOnClickListener(v -> {
-            View row = (View) v.getParent();
-            onProfileRowClicked(row);
-        });
-        holder.mColorTag.setOnClickListener(v -> {
-            View row = (View) v.getParent()
-                               .getParent();
-            onProfileRowClicked(row);
-        });
-        holder.mTitle.setOnLongClickListener(v -> {
-            flipEditingProfiles();
-            return true;
-        });
-
-        View.OnTouchListener dragStarter = (v, event) -> {
-            if (rearrangeHelper != null && editingProfiles()) {
-                rearrangeHelper.startDrag(holder);
-                return true;
-            }
-            return false;
-        };
-
-        holder.tagAndHandleLayout.setOnTouchListener(dragStarter);
-        return holder;
+        return new ProfileListViewHolder(view);
     }
     @Override
     public void onBindViewHolder(@NonNull final ProfileListViewHolder holder, int position) {
-        final ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
-        if (profiles == null)
-            throw new AssertionError();
-        final MobileLedgerProfile profile = profiles.get(position);
-        final MobileLedgerProfile currentProfile = Data.getProfile();
+        final Profile profile = listDiffer.getCurrentList()
+                                          .get(position);
+        final Profile currentProfile = Data.getProfile();
 //        debug("profiles", String.format(Locale.ENGLISH, "pos %d: %s, current: %s", position,
 //                profile.getUuid(), currentProfile.getUuid()));
-        holder.itemView.setTag(profile);
 
-        int hue = profile.getThemeHue();
+        int hue = profile.getTheme();
         if (hue == -1)
             holder.mColorTag.setBackgroundColor(
                     Colors.getPrimaryColorForHue(Colors.DEFAULT_HUE_DEG));
@@ -209,9 +169,13 @@ public class ProfilesRecyclerViewAdapter
         holder.mTitle.setText(profile.getName());
 //            holder.mSubTitle.setText(profile.getUrl());
 
-        holder.mEditButton.setOnClickListener(mOnClickListener);
+        holder.mEditButton.setOnClickListener(view -> {
+            Profile p = listDiffer.getCurrentList()
+                                  .get(holder.getAdapterPosition());
+            ProfileDetailActivity.start(view.getContext(), p);
+        });
 
-        final boolean sameProfile = currentProfile.equals(profile);
+        final boolean sameProfile = currentProfile.getId() == profile.getId();
         holder.itemView.setBackground(
                 sameProfile ? new ColorDrawable(Colors.tableRowDarkBG) : null);
         if (editingProfiles()) {
@@ -239,10 +203,10 @@ public class ProfilesRecyclerViewAdapter
     }
     @Override
     public int getItemCount() {
-        final ArrayList<MobileLedgerProfile> profiles = Data.profiles.getValue();
-        return profiles != null ? profiles.size() : 0;
+        return listDiffer.getCurrentList()
+                         .size();
     }
-    static class ProfileListViewHolder extends RecyclerView.ViewHolder {
+    class ProfileListViewHolder extends RecyclerView.ViewHolder {
         final TextView mEditButton;
         final TextView mTitle, mColorTag;
         final LinearLayout tagAndHandleLayout;
@@ -257,6 +221,47 @@ public class ProfilesRecyclerViewAdapter
             mRearrangeHandle = view.findViewById(R.id.profile_list_rearrange_handle);
             tagAndHandleLayout = view.findViewById(R.id.handle_and_tag);
             mRow = (ConstraintLayout) view;
+
+
+            mRow.setOnClickListener(this::onProfileRowClicked);
+            mTitle.setOnClickListener(v -> {
+                View row = (View) v.getParent();
+                onProfileRowClicked(row);
+            });
+            mColorTag.setOnClickListener(v -> {
+                View row = (View) v.getParent()
+                                   .getParent();
+                onProfileRowClicked(row);
+            });
+            mTitle.setOnLongClickListener(v -> {
+                flipEditingProfiles();
+                return true;
+            });
+
+            View.OnTouchListener dragStarter = (v, event) -> {
+                if (rearrangeHelper != null && editingProfiles()) {
+                    rearrangeHelper.startDrag(this);
+                    return true;
+                }
+                return false;
+            };
+
+            tagAndHandleLayout.setOnTouchListener(dragStarter);
+        }
+        private void onProfileRowClicked(View v) {
+            if (editingProfiles())
+                return;
+            Profile profile = listDiffer.getCurrentList()
+                                        .get(getAdapterPosition());
+            if (Data.getProfile() != profile) {
+                debug("profiles", "Setting profile to " + profile.getName());
+                Data.drawerOpen.setValue(false);
+                Data.setCurrentProfile(profile);
+            }
+            else
+                debug("profiles",
+                        "Not setting profile to the current profile " + profile.getName());
         }
+
     }
 }
index 8d0e29e64792eee5548e1072883e3b26dbe7cae1..c49e44d092f75d0b49490492136ebdcb2f474501 100644 (file)
@@ -218,7 +218,7 @@ public class TemplateDetailsViewModel extends ViewModel {
                                             .getTemplateDAO();
             TemplateHeader dbHeader = modelHeader.toDBO();
             if (newPattern) {
-                dbHeader.setId(null);
+                dbHeader.setId(0L);
                 dbHeader.setId(mPatternId = headerDAO.insertSync(dbHeader));
             }
             else
@@ -239,7 +239,7 @@ public class TemplateDetailsViewModel extends ViewModel {
                 dbAccount.setTemplateId(mPatternId);
                 dbAccount.setPosition(i);
                 if (dbAccount.getId() < 0) {
-                    dbAccount.setId(null);
+                    dbAccount.setId(0);
                     dbAccount.setId(taDAO.insertSync(dbAccount));
                 }
                 else
@@ -255,6 +255,10 @@ public class TemplateDetailsViewModel extends ViewModel {
     }
     private ArrayList<TemplateDetailsItem> copyItems() {
         List<TemplateDetailsItem> oldList = items.getValue();
+
+        if (oldList == null)
+            return new ArrayList<>();
+
         ArrayList<TemplateDetailsItem> result = new ArrayList<>(oldList.size());
 
         for (TemplateDetailsItem item : oldList) {
index 985556585aaa382cc360fea577cda184812be185..67a4922b175be66ffac07f41fd0e8b81dfd651a2 100644 (file)
@@ -75,8 +75,8 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
                                        .equals(newItem.getDate()));
                     case TRANSACTION:
                         return oldItem.getTransaction()
-                                      .getId() == newItem.getTransaction()
-                                                         .getId();
+                                      .getLedgerId() == newItem.getTransaction()
+                                                               .getLedgerId();
                     case HEADER:
                         return true;    // there can be only one header
                     default:
index 037661f17edcdf15d07ced91f66942502ff49903..a809a17eb2c243ee23fe482cdfcafba44076b343 100644 (file)
@@ -37,8 +37,8 @@ import androidx.recyclerview.widget.RecyclerView;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.TransactionDateFinder;
 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.DatePickerFragment;
 import net.ktnx.mobileledger.ui.FabManager;
 import net.ktnx.mobileledger.ui.MainModel;
@@ -112,7 +112,7 @@ public class TransactionListFragment extends MobileLedgerListFragment
         mainActivity.fabShouldShow();
 
         if (mainActivity instanceof FabManager.FabHandler)
-            FabManager.handle((FabManager.FabHandler) mainActivity, root);
+            FabManager.handle(mainActivity, root);
 
         LinearLayoutManager llm = new LinearLayoutManager(mainActivity);
 
@@ -129,7 +129,7 @@ public class TransactionListFragment extends MobileLedgerListFragment
         vAccountFilter = view.findViewById(R.id.transaction_list_account_name_filter);
         accNameFilter = view.findViewById(R.id.transaction_filter_account_name);
 
-        MobileLedgerProfile profile = Data.getProfile();
+        Profile profile = Data.getProfile();
         accNameFilter.setAdapter(new AccountAutocompleteAdapter(mainActivity, profile));
         accNameFilter.setOnItemClickListener((parent, v, position, id) -> {
 //                debug("tmp", "direct onItemClick");
index adefa6f30e701d538b4f6c02a452f15a4873adff..fa56c19fdaead8e9436c74aefa9890ca6effebf9 100644 (file)
@@ -28,12 +28,13 @@ import androidx.lifecycle.MutableLiveData;
 
 import net.ktnx.mobileledger.BuildConfig;
 import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.db.Profile;
 import net.ktnx.mobileledger.ui.HueRing;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 
@@ -151,7 +152,7 @@ public class Colors {
         }
         return colors;
     }
-    public static int getNewProfileThemeHue(ArrayList<MobileLedgerProfile> profiles) {
+    public static int getNewProfileThemeHue(List<Profile> profiles) {
         if ((profiles == null) || (profiles.size() == 0))
             return DEFAULT_HUE_DEG;
 
@@ -159,14 +160,14 @@ public class Colors {
 
         if (profiles.size() == 1) {
             int opposite = profiles.get(0)
-                                   .getThemeHue() + 180;
+                                   .getTheme() + 180;
             opposite %= 360;
             chosenHue = opposite;
         }
         else {
             ArrayList<Integer> hues = new ArrayList<>();
-            for (MobileLedgerProfile p : profiles) {
-                int hue = p.getThemeHue();
+            for (Profile p : profiles) {
+                int hue = p.getTheme();
                 if (hue == -1)
                     hue = DEFAULT_HUE_DEG;
                 hues.add(hue);
index c9f0151ba4b84b12175bbf828eb425e7607c07f8..97c09911f60a6aa5547eef8d12512c01f70b777a 100644 (file)
@@ -19,7 +19,7 @@ package net.ktnx.mobileledger.utils;
 
 import androidx.annotation.NonNull;
 
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
+import net.ktnx.mobileledger.db.Profile;
 
 import org.jetbrains.annotations.NotNull;
 
@@ -32,9 +32,9 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
 public final class NetworkUtil {
     private static final int thirtySeconds = 30000;
     @NotNull
-    public static HttpURLConnection prepareConnection(@NonNull MobileLedgerProfile profile,
+    public static HttpURLConnection prepareConnection(@NonNull Profile profile,
                                                       @NonNull String path) throws IOException {
-        return prepareConnection(profile.getUrl(), path, profile.isAuthEnabled());
+        return prepareConnection(profile.getUrl(), path, profile.useAuthentication());
     }
     public static HttpURLConnection prepareConnection(@NonNull String url, @NonNull String path,
                                                       boolean authEnabled) throws IOException {
diff --git a/app/src/test/java/net/ktnx/mobileledger/model/MobileLedgerProfileTest.java b/app/src/test/java/net/ktnx/mobileledger/model/MobileLedgerProfileTest.java
deleted file mode 100644 (file)
index edbd5a7..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright © 2020 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 <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.model;
-
-import org.junit.Test;
-import org.junit.internal.ArrayComparisonFailure;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThrows;
-
-public class MobileLedgerProfileTest {
-    private List<LedgerAccount> listFromArray(LedgerAccount[] array) {
-        ArrayList<LedgerAccount> result = new ArrayList<>();
-        Collections.addAll(result, array);
-
-        return result;
-    }
-    private void aTest(LedgerAccount[] oldList, LedgerAccount[] newList,
-                       LedgerAccount[] expectedResult) {
-        List<LedgerAccount> result =
-                MobileLedgerProfile.mergeAccountListsFromWeb(listFromArray(oldList),
-                        listFromArray(newList));
-        assertArrayEquals(expectedResult, result.toArray());
-    }
-    private void negTest(LedgerAccount[] oldList, LedgerAccount[] newList,
-                         LedgerAccount[] expectedResult) {
-        List<LedgerAccount> result =
-                MobileLedgerProfile.mergeAccountListsFromWeb(listFromArray(oldList),
-                        listFromArray(newList));
-        assertThrows(ArrayComparisonFailure.class,
-                () -> assertArrayEquals(expectedResult, result.toArray()));
-    }
-    private LedgerAccount[] emptyArray() {
-        return new LedgerAccount[]{};
-    }
-    @Test
-    public void mergeEmptyLists() {
-        aTest(emptyArray(), emptyArray(), emptyArray());
-    }
-    @Test
-    public void mergeIntoEmptyLists() {
-        LedgerAccount acc1 = new LedgerAccount(null, "Acc1", null);
-        aTest(emptyArray(), new LedgerAccount[]{acc1}, new LedgerAccount[]{acc1});
-    }
-    @Test
-    public void mergeEmptyList() {
-        LedgerAccount acc1 = new LedgerAccount(null, "Acc1", null);
-        aTest(new LedgerAccount[]{acc1}, emptyArray(), emptyArray());
-    }
-    @Test
-    public void mergeEqualLists() {
-        LedgerAccount acc1 = new LedgerAccount(null, "Acc1", null);
-        aTest(new LedgerAccount[]{acc1}, new LedgerAccount[]{acc1}, new LedgerAccount[]{acc1});
-    }
-    @Test
-    public void mergeFlags() {
-        LedgerAccount acc1a = new LedgerAccount(null, "Acc1", null);
-        LedgerAccount acc1b = new LedgerAccount(null, "Acc1", null);
-        acc1b.setExpanded(true);
-        acc1b.setAmountsExpanded(true);
-        List<LedgerAccount> merged = MobileLedgerProfile.mergeAccountListsFromWeb(
-                listFromArray(new LedgerAccount[]{acc1a}),
-                listFromArray(new LedgerAccount[]{acc1b}));
-        assertArrayEquals(new LedgerAccount[]{acc1b}, merged.toArray());
-        assertSame(merged.get(0), acc1a);
-        // restore original values, modified by the merge
-        acc1a.setExpanded(false);
-        acc1a.setAmountsExpanded(false);
-        negTest(new LedgerAccount[]{acc1a}, new LedgerAccount[]{acc1b},
-                new LedgerAccount[]{new LedgerAccount(null, "Acc1", null)});
-    }
-}
\ No newline at end of file