From: Damyan Ivanov Date: Fri, 3 May 2019 16:06:32 +0000 (+0300) Subject: move DB access routines to the application class X-Git-Tag: v0.10.0~44 X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=commitdiff_plain;h=90383a155ec16a9f13b1e6ac94a118033e09b3aa move DB access routines to the application class it is a natural context "source" for the DB creation and a singleton global instance --- diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 25d55d22..35f37475 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ . + */ + +package net.ktnx.mobileledger; + +import android.app.Application; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.sqlite.SQLiteDatabase; +import android.preference.PreferenceManager; + +import net.ktnx.mobileledger.model.Data; +import net.ktnx.mobileledger.utils.Globals; +import net.ktnx.mobileledger.utils.MobileLedgerDatabase; + +import static net.ktnx.mobileledger.ui.activity.SettingsActivity.PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS; + +public class App extends Application { + public static App instance; + private MobileLedgerDatabase dbHelper; + @Override + public void onCreate() { + instance = this; + super.onCreate(); + updateMonthNames(); + SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); + Data.optShowOnlyStarred.set(p.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false)); + SharedPreferences.OnSharedPreferenceChangeListener handler = + (preference, value) -> Data.optShowOnlyStarred + .set(preference.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false)); + p.registerOnSharedPreferenceChangeListener(handler); + } + private void updateMonthNames() { + Resources rm = getResources(); + Globals.monthNames = rm.getStringArray(R.array.month_names); + } + @Override + public void onTerminate() { + if (dbHelper != null) dbHelper.close(); + super.onTerminate(); + } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateMonthNames(); + } + public static SQLiteDatabase getDatabase() { + if (instance == null) throw new RuntimeException("Application not created yet"); + + return instance.getDB(); + } + public SQLiteDatabase getDB() { + if (dbHelper == null) initDb(); + + final SQLiteDatabase db = dbHelper.getWritableDatabase(); + db.execSQL("pragma case_sensitive_like=ON;"); + + return db; + } + private synchronized void initDb() { + if (dbHelper != null) return; + + dbHelper = new MobileLedgerDatabase(this); + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/MobileLedgerApplication.java b/app/src/main/java/net/ktnx/mobileledger/MobileLedgerApplication.java deleted file mode 100644 index 836ea3a1..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/MobileLedgerApplication.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2019 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 . - */ - -package net.ktnx.mobileledger; - -import android.app.Application; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.preference.PreferenceManager; - -import net.ktnx.mobileledger.model.Data; -import net.ktnx.mobileledger.utils.Globals; -import net.ktnx.mobileledger.utils.MLDB; - -import static net.ktnx.mobileledger.ui.activity.SettingsActivity.PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS; - -public class MobileLedgerApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - updateMonthNames(); - MLDB.init(this); - SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); - Data.optShowOnlyStarred.set(p.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false)); - SharedPreferences.OnSharedPreferenceChangeListener handler = - (preference, value) -> Data.optShowOnlyStarred - .set(preference.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false)); - p.registerOnSharedPreferenceChangeListener(handler); - } - private void updateMonthNames() { - Resources rm = getResources(); - Globals.monthNames = rm.getStringArray(R.array.month_names); - } - @Override - public void onTerminate() { - MLDB.done(); - super.onTerminate(); - } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - updateMonthNames(); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/async/CommitAccountsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/CommitAccountsTask.java index cb13d6db..bde0156f 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/CommitAccountsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/CommitAccountsTask.java @@ -20,10 +20,10 @@ package net.ktnx.mobileledger.async; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.LedgerAccount; import net.ktnx.mobileledger.utils.LockHolder; -import net.ktnx.mobileledger.utils.MLDB; import java.util.ArrayList; @@ -37,7 +37,7 @@ public class CommitAccountsTask String profile = Data.profile.getValue().getUuid(); try { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); db.beginTransaction(); try { try (LockHolder lh = params[0].accountList.lockForWriting()) { diff --git a/app/src/main/java/net/ktnx/mobileledger/async/DbOpRunner.java b/app/src/main/java/net/ktnx/mobileledger/async/DbOpRunner.java index 417f5b55..3a81614d 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/DbOpRunner.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/DbOpRunner.java @@ -19,7 +19,7 @@ package net.ktnx.mobileledger.async; import android.database.sqlite.SQLiteDatabase; -import net.ktnx.mobileledger.utils.MLDB; +import net.ktnx.mobileledger.App; import java.util.concurrent.BlockingQueue; @@ -36,7 +36,7 @@ class DbOpRunner extends Thread { try { DbOpItem item = queue.take(); debug("opQrunner", "Got "+item.sql); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); debug("opQrunner", "Executing "+item.sql); db.execSQL(item.sql, item.params); } diff --git a/app/src/main/java/net/ktnx/mobileledger/async/RefreshDescriptionsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/RefreshDescriptionsTask.java index 1f854371..62885c1b 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/RefreshDescriptionsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/RefreshDescriptionsTask.java @@ -21,8 +21,8 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.model.Data; -import net.ktnx.mobileledger.utils.MLDB; import java.util.HashMap; import java.util.Map; @@ -35,7 +35,7 @@ public class RefreshDescriptionsTask extends AsyncTask { Map unique = new HashMap<>(); debug("descriptions", "Starting refresh"); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); Data.backgroundTaskStarted(); try { diff --git a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java index 14d700c2..1962173c 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java @@ -22,6 +22,7 @@ import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.OperationCanceledException; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.err.HTTPException; import net.ktnx.mobileledger.json.AccountListParser; import net.ktnx.mobileledger.json.ParsedBalance; @@ -34,7 +35,6 @@ import net.ktnx.mobileledger.model.LedgerTransaction; import net.ktnx.mobileledger.model.LedgerTransactionAccount; import net.ktnx.mobileledger.model.MobileLedgerProfile; import net.ktnx.mobileledger.ui.activity.MainActivity; -import net.ktnx.mobileledger.utils.MLDB; import net.ktnx.mobileledger.utils.NetworkUtil; import java.io.BufferedReader; @@ -129,7 +129,8 @@ public class RetrieveTransactionsTask default: throw new HTTPException(http.getResponseCode(), http.getResponseMessage()); } - try (SQLiteDatabase db = MLDB.getDatabase()) { + // FIXME: why the resource block here? that would close the global DB connection + try (SQLiteDatabase db = App.getDatabase()) { try (InputStream resp = http.getInputStream()) { if (http.getResponseCode() != 200) throw new IOException(String.format("HTTP error %d", http.getResponseCode())); @@ -412,7 +413,7 @@ public class RetrieveTransactionsTask throw new HTTPException(http.getResponseCode(), http.getResponseMessage()); } publishProgress(progress); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); ArrayList accountList = new ArrayList<>(); boolean listFilledOK = false; try (InputStream resp = http.getInputStream()) { @@ -501,7 +502,7 @@ public class RetrieveTransactionsTask default: throw new HTTPException(http.getResponseCode(), http.getResponseMessage()); } - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (InputStream resp = http.getInputStream()) { if (http.getResponseCode() != 200) throw new IOException(String.format("HTTP error %d", http.getResponseCode())); diff --git a/app/src/main/java/net/ktnx/mobileledger/async/UpdateAccountsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/UpdateAccountsTask.java index 88ff97b8..328eff45 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/UpdateAccountsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/UpdateAccountsTask.java @@ -21,10 +21,10 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.LedgerAccount; import net.ktnx.mobileledger.model.MobileLedgerProfile; -import net.ktnx.mobileledger.utils.MLDB; import java.util.ArrayList; @@ -44,7 +44,7 @@ public class UpdateAccountsTask extends AsyncTask { } debug("UTT", sql); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); String lastDateString = Globals.formatLedgerDate(new Date()); Date lastDate = Globals.parseLedgerDate(lastDateString); boolean odd = true; diff --git a/app/src/main/java/net/ktnx/mobileledger/model/Data.java b/app/src/main/java/net/ktnx/mobileledger/model/Data.java index cc35638e..2a6f0d5f 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/Data.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/Data.java @@ -21,6 +21,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.async.RetrieveTransactionsTask; import net.ktnx.mobileledger.ui.activity.MainActivity; import net.ktnx.mobileledger.utils.LockHolder; @@ -103,7 +104,7 @@ public final class Data { String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null); if (profileUUID == null) return -1; - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor c = db .rawQuery("SELECT theme from profiles where uuid=?", new String[]{profileUUID})) { diff --git a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java index 873b0d68..d7fae411 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java @@ -20,6 +20,7 @@ package net.ktnx.mobileledger.model; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.async.DbOpQueue; import net.ktnx.mobileledger.utils.Globals; import net.ktnx.mobileledger.utils.Logger; @@ -70,7 +71,7 @@ public final class MobileLedgerProfile { public static MobileLedgerProfile loadAllFromDB(String currentProfileUUID) { MobileLedgerProfile result = null; ArrayList list = new ArrayList<>(); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor cursor = db.rawQuery("SELECT uuid, name, url, use_authentication, auth_user, " + "auth_password, permit_posting, theme, order_no, " + "preferred_accounts_filter FROM " + @@ -95,7 +96,7 @@ public final class MobileLedgerProfile { return result; } public static void storeProfilesOrder() { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); db.beginTransaction(); try { int orderNo = 0; @@ -172,7 +173,7 @@ public final class MobileLedgerProfile { this.authPassword = authPassword; } public void storeInDB() { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); db.beginTransaction(); try { // debug("profiles", String.format("Storing profile in DB: uuid=%s, name=%s, " + @@ -238,7 +239,7 @@ public final class MobileLedgerProfile { // debug("profile", String.format("Transaction %d stored", tr.getId())); } public String getOption(String name, String default_value) { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor cursor = db.rawQuery("select value from options where profile = ? and name=?", new String[]{uuid, name})) { @@ -289,7 +290,7 @@ public final class MobileLedgerProfile { setOption(name, String.valueOf(value)); } public void removeFromDB() { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); debug("db", String.format("removing profile %s from DB", uuid)); db.beginTransaction(); try { @@ -307,12 +308,12 @@ public final class MobileLedgerProfile { } @NonNull public LedgerAccount loadAccount(String name) { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); return loadAccount(db, name); } @Nullable public LedgerAccount tryLoadAccount(String acct_name) { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); return tryLoadAccount(db, acct_name); } @NonNull @@ -352,7 +353,7 @@ public final class MobileLedgerProfile { } public LedgerTransaction loadTransaction(int transactionId) { LedgerTransaction tr = new LedgerTransaction(transactionId, this.uuid); - tr.loadData(MLDB.getDatabase()); + tr.loadData(App.getDatabase()); return tr; } @@ -403,7 +404,7 @@ public final class MobileLedgerProfile { } public List loadChildAccountsOf(LedgerAccount acc) { List result = new ArrayList<>(); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor c = db.rawQuery( "SELECT a.name FROM accounts a WHERE a.profile = ? and a.name like ?||':%'", new String[]{uuid, acc.getName()})) @@ -421,7 +422,7 @@ public final class MobileLedgerProfile { ArrayList visibleList = new ArrayList<>(); visibleList.add(acc); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor c = db.rawQuery( "SELECT a.name FROM accounts a WHERE a.profile = ? and a.name like ?||':%'", new String[]{uuid, acc.getName()})) @@ -438,7 +439,7 @@ public final class MobileLedgerProfile { return result; } public void wipeAllData() { - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); db.beginTransaction(); try { String[] pUuid = new String[]{uuid}; diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionActivity.java index cf9a6e46..d163aa28 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionActivity.java @@ -43,6 +43,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.BuildConfig; import net.ktnx.mobileledger.R; import net.ktnx.mobileledger.async.DescriptionSelectedCallback; @@ -531,7 +532,7 @@ public class NewTransactionActivity extends ProfileThemedActivity debug("descr", sql); debug("descr", params.toString()); - try (Cursor c = MLDB.getDatabase().rawQuery(sql, params.toArray(new String[]{}))) { + try (Cursor c = App.getDatabase().rawQuery(sql, params.toArray(new String[]{}))) { if (!c.moveToNext()) return; String profileUUID = c.getString(0); diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java index 49249686..2b66f819 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java @@ -31,6 +31,7 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.R; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.LedgerTransaction; @@ -38,7 +39,6 @@ import net.ktnx.mobileledger.model.LedgerTransactionAccount; import net.ktnx.mobileledger.model.TransactionListItem; import net.ktnx.mobileledger.utils.Colors; import net.ktnx.mobileledger.utils.Globals; -import net.ktnx.mobileledger.utils.MLDB; import java.text.DateFormat; import java.util.Date; @@ -128,7 +128,7 @@ public class TransactionListAdapter extends RecyclerView.Adapter t = new AsyncTask() { @Override protected String doInBackground(Void... params) { - SQLiteDatabase db = getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor cursor = db .rawQuery("select value from options where profile = ? and name=?", new String[]{NO_PROFILE, name})) @@ -129,7 +105,7 @@ public final class MLDB { } static public String getOption(String name, String default_value) { debug("db", "about to fetch option " + name); - SQLiteDatabase db = getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor cursor = db.rawQuery("select value from options where profile = ? and name=?", new String[]{NO_PROFILE, name})) { @@ -207,7 +183,7 @@ public final class MLDB { params = new String[]{str, str, str, str}; } debug("autocompletion", sql); - SQLiteDatabase db = MLDB.getDatabase(); + SQLiteDatabase db = App.getDatabase(); try (Cursor matches = db.rawQuery(sql, params)) { int i = 0; @@ -237,92 +213,5 @@ public final class MLDB { }); } } - public static synchronized void init(Application context) { - MLDB.context = context; - if (dbHelper != null) - throw new IllegalStateException("It appears init() was already called"); - dbHelper = new MobileLedgerDatabase(context); - } - public static synchronized void done() { - if (dbHelper != null) { - debug("db", "Closing DB helper"); - dbHelper.close(); - dbHelper = null; - } - } } -class MobileLedgerDatabase extends SQLiteOpenHelper { - private static final String DB_NAME = "MoLe.db"; - private static final int LATEST_REVISION = 22; - private static final String CREATE_DB_SQL = "create_db"; - - private final Application mContext; - - MobileLedgerDatabase(Application context) { - super(context, DB_NAME, null, LATEST_REVISION); - debug("db", "creating helper instance"); - mContext = context; - super.setWriteAheadLoggingEnabled(true); - } - - @Override - public void onCreate(SQLiteDatabase db) { - debug("db", "onCreate called"); - applyRevisionFile(db, CREATE_DB_SQL); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - debug("db", "onUpgrade called"); - for (int i = oldVersion + 1; i <= newVersion; i++) applyRevision(db, i); - } - - private void applyRevision(SQLiteDatabase db, int rev_no) { - String rev_file = String.format(Locale.US, "sql_%d", rev_no); - - applyRevisionFile(db, rev_file); - } - private void applyRevisionFile(SQLiteDatabase db, String rev_file) { - final Resources rm = mContext.getResources(); - int res_id = rm.getIdentifier(rev_file, "raw", mContext.getPackageName()); - if (res_id == 0) - throw new SQLException(String.format(Locale.US, "No resource for %s", rev_file)); - db.beginTransaction(); - try (InputStream res = rm.openRawResource(res_id)) { - debug("db", "Applying " + rev_file); - InputStreamReader isr = new InputStreamReader(res); - BufferedReader reader = new BufferedReader(isr); - - String line; - int line_no = 1; - while ((line = reader.readLine()) != null) { - if (line.startsWith("--")) { - line_no++; - continue; - } - if (line.isEmpty()) { - line_no++; - continue; - } - try { - db.execSQL(line); - } - catch (Exception e) { - throw new RuntimeException( - String.format("Error applying %s, line %d", rev_file, line_no), e); - } - line_no++; - } - - db.setTransactionSuccessful(); - } - catch (IOException e) { - Log.e("db", String.format("Error opening raw resource for %s", rev_file)); - e.printStackTrace(); - } - finally { - db.endTransaction(); - } - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/MobileLedgerDatabase.java b/app/src/main/java/net/ktnx/mobileledger/utils/MobileLedgerDatabase.java new file mode 100644 index 00000000..abf11984 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/utils/MobileLedgerDatabase.java @@ -0,0 +1,108 @@ +/* + * Copyright © 2019 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 . + */ + +package net.ktnx.mobileledger.utils; + +import android.app.Application; +import android.content.res.Resources; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Locale; + +import static net.ktnx.mobileledger.utils.Logger.debug; + +public class MobileLedgerDatabase extends SQLiteOpenHelper { + private static final String DB_NAME = "MoLe.db"; + private static final int LATEST_REVISION = 22; + private static final String CREATE_DB_SQL = "create_db"; + + private final Application mContext; + + public MobileLedgerDatabase(Application context) { + super(context, DB_NAME, null, LATEST_REVISION); + debug("db", "creating helper instance"); + mContext = context; + super.setWriteAheadLoggingEnabled(true); + } + + @Override + public void onCreate(SQLiteDatabase db) { + debug("db", "onCreate called"); + applyRevisionFile(db, CREATE_DB_SQL); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + debug("db", "onUpgrade called"); + for (int i = oldVersion + 1; i <= newVersion; i++) applyRevision(db, i); + } + + private void applyRevision(SQLiteDatabase db, int rev_no) { + String rev_file = String.format(Locale.US, "sql_%d", rev_no); + + applyRevisionFile(db, rev_file); + } + private void applyRevisionFile(SQLiteDatabase db, String rev_file) { + final Resources rm = mContext.getResources(); + int res_id = rm.getIdentifier(rev_file, "raw", mContext.getPackageName()); + if (res_id == 0) + throw new SQLException(String.format(Locale.US, "No resource for %s", rev_file)); + db.beginTransaction(); + try (InputStream res = rm.openRawResource(res_id)) { + debug("db", "Applying " + rev_file); + InputStreamReader isr = new InputStreamReader(res); + BufferedReader reader = new BufferedReader(isr); + + String line; + int line_no = 1; + while ((line = reader.readLine()) != null) { + if (line.startsWith("--")) { + line_no++; + continue; + } + if (line.isEmpty()) { + line_no++; + continue; + } + try { + db.execSQL(line); + } + catch (Exception e) { + throw new RuntimeException( + String.format("Error applying %s, line %d", rev_file, line_no), e); + } + line_no++; + } + + db.setTransactionSuccessful(); + } + catch (IOException e) { + Log.e("db", String.format("Error opening raw resource for %s", rev_file)); + e.printStackTrace(); + } + finally { + db.endTransaction(); + } + } +}