]> git.ktnx.net Git - mobile-ledger.git/commitdiff
replace custom autocompletion adapter hooking with specialised adapters
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 11 Mar 2021 15:01:09 +0000 (17:01 +0200)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Wed, 24 Mar 2021 19:46:24 +0000 (19:46 +0000)
... which use Room

app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/db/DB.java
app/src/main/java/net/ktnx/mobileledger/db/TransactionDescriptionAutocompleteAdapter.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionHeaderItemHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListFragment.java
app/src/main/java/net/ktnx/mobileledger/utils/MLDB.java

diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java
new file mode 100644 (file)
index 0000000..0bee771
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.dao;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.room.ColumnInfo;
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.Update;
+
+import net.ktnx.mobileledger.db.Transaction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Dao
+public abstract class TransactionDAO extends BaseDAO<Transaction> {
+    static public List<String> unbox(List<DescriptionContainer> list) {
+        ArrayList<String> result = new ArrayList<>(list.size());
+        for (DescriptionContainer item : list) {
+            result.add(item.description);
+        }
+
+        return result;
+    }
+    @Insert
+    public abstract void insertSync(Transaction item);
+
+    @Update
+    public abstract void updateSync(Transaction item);
+
+    @Delete
+    public abstract void deleteSync(Transaction item);
+
+    @Query("SELECT * FROM transactions")
+    public abstract LiveData<List<Transaction>> getAll();
+
+    //    not useful for now
+//    @Transaction
+//    @Query("SELECT * FROM patterns")
+//    List<PatternWithAccounts> getPatternsWithAccounts();
+    @Query("SELECT * FROM transactions WHERE profile = :profileUUID AND id = :id")
+    public abstract LiveData<Transaction> getById(@NonNull String profileUUID, long id);
+
+    @Query("SELECT DISTINCT description, CASE WHEN description_upper LIKE :term||'%%' THEN 1 " +
+           "               WHEN description_upper LIKE '%%:'||:term||'%%' THEN 2 " +
+           "               WHEN description_upper LIKE '%% '||:term||'%%' THEN 3 " +
+           "               ELSE 9 END AS ordering " + "FROM description_history " +
+           "WHERE description_upper LIKE '%%'||:term||'%%' " +
+           "ORDER BY ordering, description_upper, rowid ")
+    public abstract List<DescriptionContainer> lookupDescriptionSync(@NonNull String term);
+
+    static public class DescriptionContainer {
+        @ColumnInfo
+        public String description;
+        @ColumnInfo
+        public int ordering;
+    }
+}
index 173d730ec99286eff98a75152bc0ea2ae503ebdf..32e8b216a47dac5d0f73a10b8395a4015db4e585 100644 (file)
@@ -32,6 +32,7 @@ import net.ktnx.mobileledger.dao.AccountDAO;
 import net.ktnx.mobileledger.dao.CurrencyDAO;
 import net.ktnx.mobileledger.dao.TemplateAccountDAO;
 import net.ktnx.mobileledger.dao.TemplateHeaderDAO;
+import net.ktnx.mobileledger.dao.TransactionDAO;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -165,4 +166,6 @@ abstract public class DB extends RoomDatabase {
     public abstract CurrencyDAO getCurrencyDAO();
 
     public abstract AccountDAO getAccountDAO();
+
+    public abstract TransactionDAO getTransactionDAO();
 }
diff --git a/app/src/main/java/net/ktnx/mobileledger/db/TransactionDescriptionAutocompleteAdapter.java b/app/src/main/java/net/ktnx/mobileledger/db/TransactionDescriptionAutocompleteAdapter.java
new file mode 100644 (file)
index 0000000..f5c10ca
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.db;
+
+import android.content.Context;
+import android.widget.ArrayAdapter;
+import android.widget.Filter;
+
+import androidx.annotation.NonNull;
+
+import net.ktnx.mobileledger.dao.TransactionDAO;
+import net.ktnx.mobileledger.utils.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TransactionDescriptionAutocompleteAdapter extends ArrayAdapter<String> {
+    private final TransactionFilter filter = new TransactionFilter();
+    private final TransactionDAO dao = DB.get()
+                                         .getTransactionDAO();
+    public TransactionDescriptionAutocompleteAdapter(Context context) {
+        super(context, android.R.layout.simple_dropdown_item_1line, new ArrayList<>());
+    }
+    @NonNull
+    @Override
+    public Filter getFilter() {
+        return filter;
+    }
+    class TransactionFilter extends Filter {
+        @Override
+        protected FilterResults performFiltering(CharSequence constraint) {
+            FilterResults results = new FilterResults();
+            if (constraint == null) {
+                results.count = 0;
+                return results;
+            }
+
+            Logger.debug("acc", String.format("Looking for account '%s'", constraint));
+            final List<String> matches = TransactionDAO.unbox(dao.lookupDescriptionSync(
+                    String.valueOf(constraint)
+                          .toUpperCase()));
+            results.values = matches;
+            results.count = matches.size();
+
+            return results;
+        }
+        @Override
+        @SuppressWarnings("unchecked")
+        protected void publishResults(CharSequence constraint, FilterResults results) {
+            if (results.values == null) {
+                notifyDataSetInvalidated();
+            }
+            else {
+                setNotifyOnChange(false);
+                clear();
+                addAll((List<String>) results.values);
+                notifyDataSetChanged();
+            }
+        }
+    }
+}
index 90c4718fc01c13582edb18febd4f4036db806ec0..81afa05dc69fbe938c22ee4229f0fe8e592d43bd 100644 (file)
@@ -24,7 +24,7 @@ import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.View;
 import android.widget.EditText;
-import android.widget.SimpleCursorAdapter;
+import android.widget.ListAdapter;
 import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
@@ -32,10 +32,10 @@ import androidx.annotation.NonNull;
 
 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.ui.DatePickerFragment;
 import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.Misc;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
@@ -96,8 +96,12 @@ class NewTransactionHeaderItemHolder extends NewTransactionItemViewHolder
         NewTransactionActivity activity = (NewTransactionActivity) b.getRoot()
                                                                     .getContext();
 
-        MLDB.hookAutocompletionAdapter(activity, b.newTransactionDescription,
-                MLDB.DESCRIPTION_HISTORY_TABLE, "description", false, activity, mProfile);
+        b.newTransactionDescription.setAdapter(
+                new TransactionDescriptionAutocompleteAdapter(activity));
+        b.newTransactionDescription.setOnItemClickListener(
+                (parent, view, position, id) -> activity.descriptionSelected(
+                        parent.getItemAtPosition(position)
+                              .toString()));
 
         decimalSeparator = "";
         Data.locale.observe(activity, locale -> decimalSeparator = String.valueOf(
@@ -299,14 +303,14 @@ class NewTransactionHeaderItemHolder extends NewTransactionItemViewHolder
                 b.newTransactionDate.setText(head.getFormattedDate());
 
                 // avoid triggering completion pop-up
-                SimpleCursorAdapter a =
-                        (SimpleCursorAdapter) b.newTransactionDescription.getAdapter();
+                ListAdapter a = b.newTransactionDescription.getAdapter();
                 try {
                     b.newTransactionDescription.setAdapter(null);
                     b.newTransactionDescription.setText(head.getDescription());
                 }
                 finally {
-                    b.newTransactionDescription.setAdapter(a);
+                    b.newTransactionDescription.setAdapter(
+                            (TransactionDescriptionAutocompleteAdapter) a);
                 }
 
                 b.transactionComment.setText(head.getComment());
index e6820cf86764fe00e67c999db77fc7e1c469f43e..037661f17edcdf15d07ced91f66942502ff49903 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2020 Damyan Ivanov.
+ * 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
@@ -17,7 +17,6 @@
 
 package net.ktnx.mobileledger.ui.transaction_list;
 
-import android.database.Cursor;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -37,6 +36,7 @@ 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.model.Data;
 import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.DatePickerFragment;
@@ -47,7 +47,6 @@ import net.ktnx.mobileledger.ui.activity.MainActivity;
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
 import org.jetbrains.annotations.NotNull;
@@ -130,12 +129,13 @@ public class TransactionListFragment extends MobileLedgerListFragment
         vAccountFilter = view.findViewById(R.id.transaction_list_account_name_filter);
         accNameFilter = view.findViewById(R.id.transaction_filter_account_name);
 
-        MLDB.hookAutocompletionAdapter(mainActivity, accNameFilter, "accounts", "name");
+        MobileLedgerProfile profile = Data.getProfile();
+        accNameFilter.setAdapter(new AccountAutocompleteAdapter(mainActivity, profile));
         accNameFilter.setOnItemClickListener((parent, v, position, id) -> {
 //                debug("tmp", "direct onItemClick");
-            Cursor c = (Cursor) parent.getItemAtPosition(position);
             model.getAccountFilter()
-                 .setValue(c.getString(1));
+                 .setValue(parent.getItemAtPosition(position)
+                                 .toString());
             Globals.hideSoftKeyboard(mainActivity);
         });
 
@@ -144,18 +144,17 @@ public class TransactionListFragment extends MobileLedgerListFragment
 
         model.getUpdatingFlag()
              .observe(getViewLifecycleOwner(), (flag) -> refreshLayout.setRefreshing(flag));
-        MobileLedgerProfile profile = Data.getProfile();
         model.getDisplayedTransactions()
              .observe(getViewLifecycleOwner(), list -> modelAdapter.setTransactions(list));
 
         view.findViewById(R.id.clearAccountNameFilter)
             .setOnClickListener(v -> {
-                        model.getAccountFilter()
-                             .setValue(null);
-                        vAccountFilter.setVisibility(View.GONE);
-                        menuTransactionListFilter.setVisible(true);
-                        Globals.hideSoftKeyboard(mainActivity);
-                    });
+                model.getAccountFilter()
+                     .setValue(null);
+                vAccountFilter.setVisibility(View.GONE);
+                menuTransactionListFilter.setVisible(true);
+                Globals.hideSoftKeyboard(mainActivity);
+            });
 
         model.foundTransactionItemIndex.observe(getViewLifecycleOwner(), pos -> {
             Logger.debug("go-to-date", String.format(Locale.US, "Found pos %d", pos));
index 16e46e68a6439d8b6fd6725fa928291375af18db..e11044de2e3085c8cea55e942b3d85608e6e3cf0 100644 (file)
 
 package net.ktnx.mobileledger.utils;
 
-import android.annotation.TargetApi;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.AsyncTask;
-import android.os.Build;
-import android.widget.AutoCompleteTextView;
-import android.widget.FilterQueryProvider;
-import android.widget.SimpleCursorAdapter;
 
 import androidx.annotation.NonNull;
 
 import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.async.DbOpQueue;
-import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
-import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
 
 import org.jetbrains.annotations.NonNls;
 
@@ -134,71 +125,6 @@ public final class MLDB {
     static public void setLongOption(String name, long value) {
         setOption(name, String.valueOf(value));
     }
-    @TargetApi(Build.VERSION_CODES.N)
-    public static void hookAutocompletionAdapter(final Context context,
-                                                 final AutoCompleteTextView view,
-                                                 final String table, final String field) {
-        hookAutocompletionAdapter(context, view, table, field, true, null, null);
-    }
-    @TargetApi(Build.VERSION_CODES.N)
-    public static void hookAutocompletionAdapter(final Context context,
-                                                 final AutoCompleteTextView view,
-                                                 final String table, final String field,
-                                                 final boolean profileSpecific,
-                                                 final DescriptionSelectedCallback callback,
-                                                 final MobileLedgerProfile profile) {
-        String[] from = {field};
-        int[] to = {android.R.id.text1};
-        SimpleCursorAdapter adapter =
-                new SimpleCursorAdapter(context, android.R.layout.simple_dropdown_item_1line, null,
-                        from, to, 0);
-        adapter.setStringConversionColumn(1);
-
-        FilterQueryProvider provider = constraint -> {
-            if (constraint == null)
-                return null;
-
-            String str = constraint.toString()
-                                   .toUpperCase();
-            debug("autocompletion", "Looking for " + str);
-
-            String sql;
-            String[] params;
-            if (profileSpecific) {
-                MobileLedgerProfile p = (profile == null) ? Data.getProfile() : profile;
-                sql = String.format(
-                        "SELECT rowid as _id, %s, CASE WHEN %s_upper LIKE ?||'%%' THEN 1 " +
-                        "WHEN %s_upper LIKE '%%:'||?||'%%' then 2 " +
-                        "WHEN %s_upper LIKE '%% '||?||'%%' THEN 3 " + "ELSE 9 END " + "FROM %s " +
-                        "WHERE profile=? AND %s_upper LIKE '%%'||?||'%%' " +
-                        "ORDER BY 3, %s_upper, 1;", field, field, field, field, table, field,
-                        field);
-                params = new String[]{str, str, str, p.getUuid(), str};
-            }
-            else {
-                sql = String.format(
-                        "SELECT rowid as _id, %s, CASE WHEN %s_upper LIKE ?||'%%' THEN 1 " +
-                        "WHEN %s_upper LIKE '%%:'||?||'%%' THEN 2 " +
-                        "WHEN %s_upper LIKE '%% '||?||'%%' THEN 3 " + "ELSE 9 END " + "FROM %s " +
-                        "WHERE %s_upper LIKE '%%'||?||'%%' " + "ORDER BY 3, %s_upper, 1;", field,
-                        field, field, field, table, field, field);
-                params = new String[]{str, str, str, str};
-            }
-            debug("autocompletion", sql);
-            SQLiteDatabase db = App.getDatabase();
-
-            return db.rawQuery(sql, params);
-        };
-
-        adapter.setFilterQueryProvider(provider);
-
-        view.setAdapter(adapter);
-
-        if (callback != null)
-            view.setOnItemClickListener(
-                    (parent, itemView, position, id) -> callback.descriptionSelected(
-                            String.valueOf(view.getText())));
-    }
     public static void queryInBackground(@NonNull String statement, String[] params,
                                          @NonNull final CallbackHelper callbackHelper) {
         /* All callbacks are called in the new (asynchronous) thread! */