2 * Copyright © 2019 Damyan Ivanov.
3 * This file is part of MoLe.
4 * MoLe is free software: you can distribute it and/or modify it
5 * under the term of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your opinion), any later version.
9 * MoLe is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License terms for details.
14 * You should have received a copy of the GNU General Public License
15 * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
18 package net.ktnx.mobileledger.utils;
20 import android.annotation.TargetApi;
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.database.MatrixCursor;
24 import android.database.sqlite.SQLiteDatabase;
25 import android.os.AsyncTask;
26 import android.os.Build;
27 import android.provider.FontsContract;
28 import android.view.View;
29 import android.widget.AutoCompleteTextView;
30 import android.widget.FilterQueryProvider;
31 import android.widget.SimpleCursorAdapter;
33 import net.ktnx.mobileledger.App;
34 import net.ktnx.mobileledger.async.DbOpQueue;
35 import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
36 import net.ktnx.mobileledger.model.Data;
37 import net.ktnx.mobileledger.model.MobileLedgerProfile;
39 import org.jetbrains.annotations.NonNls;
41 import java.util.Locale;
43 import static net.ktnx.mobileledger.utils.Logger.debug;
45 public final class MLDB {
46 public static final String ACCOUNTS_TABLE = "accounts";
47 public static final String DESCRIPTION_HISTORY_TABLE = "description_history";
48 public static final String OPT_LAST_SCRAPE = "last_scrape";
50 public static final String OPT_PROFILE_UUID = "profile_uuid";
51 private static final String NO_PROFILE = "-";
52 @SuppressWarnings("unused")
53 static public int getIntOption(String name, int default_value) {
54 String s = getOption(name, String.valueOf(default_value));
56 return Integer.parseInt(s);
59 debug("db", "returning default int value of " + name, e);
63 @SuppressWarnings("unused")
64 static public long getLongOption(String name, long default_value) {
65 String s = getOption(name, String.valueOf(default_value));
67 return Long.parseLong(s);
70 debug("db", "returning default long value of " + name, e);
74 static public void getOption(String name, String defaultValue, GetOptCallback cb) {
75 AsyncTask<Void, Void, String> t = new AsyncTask<Void, Void, String>() {
77 protected String doInBackground(Void... params) {
78 SQLiteDatabase db = App.getDatabase();
79 try (Cursor cursor = db
80 .rawQuery("select value from options where profile = ? and name=?",
81 new String[]{NO_PROFILE, name}))
83 if (cursor.moveToFirst()) {
84 String result = cursor.getString(0);
86 if (result == null) result = defaultValue;
88 debug("async-db", "option " + name + "=" + result);
91 else return defaultValue;
94 debug("db", "returning default value for " + name, e);
99 protected void onPostExecute(String result) {
104 t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
106 static public String getOption(String name, String default_value) {
107 debug("db", "about to fetch option " + name);
108 SQLiteDatabase db = App.getDatabase();
109 try (Cursor cursor = db.rawQuery("select value from options where profile = ? and name=?",
110 new String[]{NO_PROFILE, name}))
112 if (cursor.moveToFirst()) {
113 String result = cursor.getString(0);
115 if (result == null) result = default_value;
117 debug("db", "option " + name + "=" + result);
120 else return default_value;
122 catch (Exception e) {
123 debug("db", "returning default value for " + name, e);
124 return default_value;
127 static public void setOption(String name, String value) {
128 debug("option", String.format("%s := %s", name, value));
129 DbOpQueue.add("insert or replace into options(profile, name, value) values(?, ?, ?);",
130 new String[]{NO_PROFILE, name, value});
132 @SuppressWarnings("unused")
133 static public void setLongOption(String name, long value) {
134 setOption(name, String.valueOf(value));
136 @TargetApi(Build.VERSION_CODES.N)
137 public static void hookAutocompletionAdapter(final Context context,
138 final AutoCompleteTextView view,
139 final String table, final String field) {
140 hookAutocompletionAdapter(context, view, table, field, true, null, null, null);
142 @TargetApi(Build.VERSION_CODES.N)
143 public static void hookAutocompletionAdapter(final Context context,
144 final AutoCompleteTextView view,
145 final String table, final String field,
146 final boolean profileSpecific, final View nextView,
147 final DescriptionSelectedCallback callback,
148 final MobileLedgerProfile profile) {
149 String[] from = {field};
150 int[] to = {android.R.id.text1};
151 SimpleCursorAdapter adapter =
152 new SimpleCursorAdapter(context, android.R.layout.simple_dropdown_item_1line, null,
154 adapter.setStringConversionColumn(1);
156 FilterQueryProvider provider = constraint -> {
157 if (constraint == null) return null;
159 String str = constraint.toString().toUpperCase();
160 debug("autocompletion", "Looking for " + str);
161 String[] col_names = {FontsContract.Columns._ID, field};
162 MatrixCursor c = new MatrixCursor(col_names);
166 if (profileSpecific) {
167 MobileLedgerProfile p = (profile == null) ? Data.profile.getValue() : profile;
168 if (p == null) throw new AssertionError();
169 sql = String.format("SELECT %s as a, case when %s_upper LIKE ?||'%%' then 1 " +
170 "WHEN %s_upper LIKE '%%:'||?||'%%' then 2 " +
171 "WHEN %s_upper LIKE '%% '||?||'%%' then 3 else 9 end " +
173 "WHERE profile=? AND %s_upper LIKE '%%'||?||'%%' " +
174 "ORDER BY 2, 1;", field, field, field, field, table, field);
175 params = new String[]{str, str, str, p.getUuid(), str};
178 sql = String.format("SELECT %s as a, case when %s_upper LIKE ?||'%%' then 1 " +
179 "WHEN %s_upper LIKE '%%:'||?||'%%' then 2 " +
180 "WHEN %s_upper LIKE '%% '||?||'%%' then 3 " + "else 9 end " +
181 "FROM %s " + "WHERE %s_upper LIKE '%%'||?||'%%' " +
182 "ORDER BY 2, 1;", field, field, field, field, table, field);
183 params = new String[]{str, str, str, str};
185 debug("autocompletion", sql);
186 SQLiteDatabase db = App.getDatabase();
188 try (Cursor matches = db.rawQuery(sql, params)) {
190 while (matches.moveToNext()) {
191 String match = matches.getString(0);
192 int order = matches.getInt(1);
193 debug("autocompletion",
194 String.format(Locale.ENGLISH, "match: %s |%d", match, order));
195 c.newRow().add(i++).add(match);
203 adapter.setFilterQueryProvider(provider);
205 view.setAdapter(adapter);
207 if (nextView != null) {
208 view.setOnItemClickListener((parent, itemView, position, id) -> {
209 nextView.requestFocus(View.FOCUS_FORWARD);
210 if (callback != null) {
211 callback.descriptionSelected(String.valueOf(view.getText()));