]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/utils/MLDB.java
move DB access routines to the application class
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / utils / MLDB.java
1 /*
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.ktnx.mobileledger.utils;
19
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;
32
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;
38
39 import org.jetbrains.annotations.NonNls;
40
41 import java.util.Locale;
42
43 import static net.ktnx.mobileledger.utils.Logger.debug;
44
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";
49     @NonNls
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));
55         try {
56             return Integer.parseInt(s);
57         }
58         catch (Exception e) {
59             debug("db", "returning default int value of " + name, e);
60             return default_value;
61         }
62     }
63     @SuppressWarnings("unused")
64     static public long getLongOption(String name, long default_value) {
65         String s = getOption(name, String.valueOf(default_value));
66         try {
67             return Long.parseLong(s);
68         }
69         catch (Exception e) {
70             debug("db", "returning default long value of " + name, e);
71             return default_value;
72         }
73     }
74     static public void getOption(String name, String defaultValue, GetOptCallback cb) {
75         AsyncTask<Void, Void, String> t = new AsyncTask<Void, Void, String>() {
76             @Override
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}))
82                 {
83                     if (cursor.moveToFirst()) {
84                         String result = cursor.getString(0);
85
86                         if (result == null) result = defaultValue;
87
88                         debug("async-db", "option " + name + "=" + result);
89                         return result;
90                     }
91                     else return defaultValue;
92                 }
93                 catch (Exception e) {
94                     debug("db", "returning default value for " + name, e);
95                     return defaultValue;
96                 }
97             }
98             @Override
99             protected void onPostExecute(String result) {
100                 cb.onResult(result);
101             }
102         };
103
104         t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
105     }
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}))
111         {
112             if (cursor.moveToFirst()) {
113                 String result = cursor.getString(0);
114
115                 if (result == null) result = default_value;
116
117                 debug("db", "option " + name + "=" + result);
118                 return result;
119             }
120             else return default_value;
121         }
122         catch (Exception e) {
123             debug("db", "returning default value for " + name, e);
124             return default_value;
125         }
126     }
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});
131     }
132     @SuppressWarnings("unused")
133     static public void setLongOption(String name, long value) {
134         setOption(name, String.valueOf(value));
135     }
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());
143     }
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,
155                         from, to, 0);
156         adapter.setStringConversionColumn(1);
157
158         FilterQueryProvider provider = constraint -> {
159             if (constraint == null) return null;
160
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);
165
166             String sql;
167             String[] params;
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 " +
172                                     "FROM %s " +
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};
176             }
177             else {
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};
184             }
185             debug("autocompletion", sql);
186             SQLiteDatabase db = App.getDatabase();
187
188             try (Cursor matches = db.rawQuery(sql, params)) {
189                 int i = 0;
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);
196                 }
197             }
198
199             return c;
200
201         };
202
203         adapter.setFilterQueryProvider(provider);
204
205         view.setAdapter(adapter);
206
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()));
212                 }
213             });
214         }
215     }
216 }
217