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 final boolean profileSpecific) {
141 hookAutocompletionAdapter(context, view, table, field, profileSpecific, null, null,
142 Data.profile.getValue());
144 @TargetApi(Build.VERSION_CODES.N)
145 public static void hookAutocompletionAdapter(final Context context,
146 final AutoCompleteTextView view,
147 final String table, final String field,
148 final boolean profileSpecific, final View nextView,
149 final DescriptionSelectedCallback callback,
150 final MobileLedgerProfile profile) {
151 String[] from = {field};
152 int[] to = {android.R.id.text1};
153 SimpleCursorAdapter adapter =
154 new SimpleCursorAdapter(context, android.R.layout.simple_dropdown_item_1line, null,
156 adapter.setStringConversionColumn(1);
158 FilterQueryProvider provider = constraint -> {
159 if (constraint == null) return null;
161 String str = constraint.toString().toUpperCase();
162 debug("autocompletion", "Looking for " + str);
163 String[] col_names = {FontsContract.Columns._ID, field};
164 MatrixCursor c = new MatrixCursor(col_names);
168 if (profileSpecific) {
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, profile.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()));