mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Database: Split read and write locks
This commit is contained in:
@@ -41,7 +41,6 @@ import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
@@ -70,18 +69,13 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import ch.qos.logback.core.spi.LifeCycle;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.PeriodicDbExporter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothStateChangeReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksContentObserver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -111,17 +105,14 @@ import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
|
||||
public class GBApplication extends Application {
|
||||
// Since this class must not log to slf4j, we use plain android.util.Log
|
||||
private static final String TAG = "GBApplication";
|
||||
public static final String DATABASE_NAME = "Gadgetbridge";
|
||||
private static volatile ShutdownHook SHUTDOWN_HOOK;
|
||||
|
||||
private static GBApplication context;
|
||||
private static final Lock dbLock = new ReentrantLock();
|
||||
private static DeviceService deviceService;
|
||||
private static SharedPreferences sharedPrefs;
|
||||
|
||||
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
|
||||
private static GBPrefs prefs;
|
||||
private static LockHandler lockHandler;
|
||||
/**
|
||||
* Note: is null on Lollipop
|
||||
*/
|
||||
@@ -178,6 +169,7 @@ public class GBApplication extends Application {
|
||||
Intent quitIntent = new Intent(GBApplication.ACTION_QUIT);
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent);
|
||||
GBApplication.deviceService().quit();
|
||||
GBDatabaseManager.closeDatabase();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -188,13 +180,7 @@ public class GBApplication extends Application {
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent);
|
||||
GBApplication.deviceService().quit();
|
||||
|
||||
if (lockHandler != null) {
|
||||
try {
|
||||
lockHandler.closeDb();
|
||||
} catch (final Exception e) {
|
||||
GB.log("Failed to close DB before restart", GB.ERROR, e);
|
||||
}
|
||||
}
|
||||
GBDatabaseManager.closeDatabase();
|
||||
|
||||
final Intent startActivity = new Intent(context, ControlCenterv2.class);
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
@@ -268,15 +254,16 @@ public class GBApplication extends Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
app = this;
|
||||
super.onCreate();
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleObserver());
|
||||
|
||||
if (lockHandler != null) {
|
||||
if (app != null) {
|
||||
super.onCreate();
|
||||
// guard against multiple invocations (robolectric)
|
||||
return;
|
||||
}
|
||||
|
||||
app = this;
|
||||
super.onCreate();
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleObserver());
|
||||
|
||||
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
prefs = new GBPrefs(sharedPrefs);
|
||||
|
||||
@@ -284,7 +271,7 @@ public class GBApplication extends Application {
|
||||
GBEnvironment.setupEnvironment(GBEnvironment.createDeviceEnvironment());
|
||||
// setup db after the environment is set up, but don't do it in test mode
|
||||
// in test mode, it's done individually, see TestBase
|
||||
setupDatabase();
|
||||
GBDatabaseManager.setupDatabase(this);
|
||||
}
|
||||
|
||||
// don't do anything here before we set up logging, otherwise
|
||||
@@ -430,22 +417,6 @@ public class GBApplication extends Application {
|
||||
return prefs.getBoolean("minimize_priority", false);
|
||||
}
|
||||
|
||||
public void setupDatabase() {
|
||||
DaoMaster.OpenHelper helper;
|
||||
GBEnvironment env = GBEnvironment.env();
|
||||
if (env.isTest()) {
|
||||
helper = new DaoMaster.DevOpenHelper(this, null, null);
|
||||
} else {
|
||||
helper = new DBOpenHelper(this, DATABASE_NAME, null);
|
||||
}
|
||||
SQLiteDatabase db = helper.getWritableDatabase();
|
||||
DaoMaster daoMaster = new DaoMaster(db);
|
||||
if (lockHandler == null) {
|
||||
lockHandler = new LockHandler();
|
||||
}
|
||||
lockHandler.init(daoMaster, helper);
|
||||
}
|
||||
|
||||
public static Context getContext() {
|
||||
return context;
|
||||
}
|
||||
@@ -471,37 +442,17 @@ public class GBApplication extends Application {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DBHandler instance for reading/writing or throws GBException
|
||||
* when that was not successful
|
||||
* If acquiring was successful, callers must call #releaseDB when they
|
||||
* are done (from the same thread that acquired the lock!
|
||||
* <p>
|
||||
* Callers must not hold a reference to the returned instance because it
|
||||
* will be invalidated at some point.
|
||||
*
|
||||
* @return the DBHandler
|
||||
* @throws GBException
|
||||
* @see #releaseDB()
|
||||
* @see GBDatabaseManager#acquireWrite()
|
||||
*/
|
||||
public static DBHandler acquireDB() throws GBException {
|
||||
try {
|
||||
if (dbLock.tryLock(30, TimeUnit.SECONDS)) {
|
||||
return lockHandler;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Log.i(TAG, "Interrupted while waiting for DB lock");
|
||||
}
|
||||
throw new GBException("Unable to access the database.");
|
||||
return GBDatabaseManager.acquireWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the database lock.
|
||||
*
|
||||
* @throws IllegalMonitorStateException if the current thread is not owning the lock
|
||||
* @see #acquireDB()
|
||||
* @see GBDatabaseManager#acquireReadOnly()
|
||||
*/
|
||||
public static void releaseDB() {
|
||||
dbLock.unlock();
|
||||
public static DBHandler acquireDbReadOnly() throws GBException {
|
||||
return GBDatabaseManager.acquireReadOnly();
|
||||
}
|
||||
|
||||
public static boolean isRunningNougatOrLater() {
|
||||
@@ -708,35 +659,6 @@ public class GBApplication extends Application {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes both the old Activity database and the new one recreates it with empty tables.
|
||||
*
|
||||
* @return true on successful deletion
|
||||
*/
|
||||
public static synchronized boolean deleteActivityDatabase(Context context) {
|
||||
// TODO: flush, close, reopen db
|
||||
if (lockHandler != null) {
|
||||
lockHandler.closeDb();
|
||||
}
|
||||
boolean result = deleteOldActivityDatabase(context);
|
||||
result &= getContext().deleteDatabase(DATABASE_NAME);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the legacy (pre 0.12) Activity database
|
||||
*
|
||||
* @return true on successful deletion
|
||||
*/
|
||||
public static synchronized boolean deleteOldActivityDatabase(Context context) {
|
||||
DBHelper dbHelper = new DBHelper(context);
|
||||
boolean result = true;
|
||||
if (dbHelper.existsDB("ActivityDatabase")) {
|
||||
result = getContext().deleteDatabase("ActivityDatabase");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void migratePrefsIfNeeded() {
|
||||
GBPrefsMigrator.migratePrefsIfNeeded(sharedPrefs);
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe, all calls should be guarded by the upstream write lock.
|
||||
*/
|
||||
public class GBDatabase {
|
||||
private static final String TAG = "GBDatabase";
|
||||
public static final String DATABASE_NAME = "Gadgetbridge";
|
||||
|
||||
private DaoMaster daoMaster = null;
|
||||
private SQLiteOpenHelper helper = null;
|
||||
private DaoSession session = null;
|
||||
|
||||
public DaoMaster getDaoMaster() {
|
||||
return daoMaster;
|
||||
}
|
||||
|
||||
public SQLiteOpenHelper getHelper() {
|
||||
return helper;
|
||||
}
|
||||
|
||||
public DaoSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
void closeDatabase() {
|
||||
if (session == null) {
|
||||
Log.w(TAG, "Database was already closed");
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Trying to close database from " + Thread.currentThread().getName());
|
||||
session.clear();
|
||||
session.getDatabase().close();
|
||||
session = null;
|
||||
daoMaster = null;
|
||||
helper = null;
|
||||
Log.d(TAG, "Database closed");
|
||||
}
|
||||
|
||||
void setupDatabase(final Context context) {
|
||||
if (session != null) {
|
||||
Log.w(TAG, "Database already setup");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(TAG, "Setting up database from " + Thread.currentThread().getName());
|
||||
|
||||
if (GBEnvironment.env().isTest()) {
|
||||
helper = new DaoMaster.DevOpenHelper(context, null, null);
|
||||
} else {
|
||||
helper = new DBOpenHelper(context, DATABASE_NAME, null);
|
||||
}
|
||||
final SQLiteDatabase db = helper.getWritableDatabase();
|
||||
daoMaster = new DaoMaster(db);
|
||||
session = daoMaster.newSession();
|
||||
if (session == null) {
|
||||
throw new RuntimeException("Unable to create database session");
|
||||
}
|
||||
Log.d(TAG, "Database setup finished");
|
||||
}
|
||||
|
||||
void importDB(final InputStream inputStream) throws IllegalStateException, IOException {
|
||||
final String dbPath = getClosedDBPath();
|
||||
try {
|
||||
final File toFile = new File(dbPath);
|
||||
FileUtils.copyStreamToFile(inputStream, toFile);
|
||||
} finally {
|
||||
setupDatabase(GBApplication.app());
|
||||
}
|
||||
|
||||
// Validate database - must be AFTER setupDatabase so that helper is not null
|
||||
if (!helper.getReadableDatabase().isDatabaseIntegrityOk()) {
|
||||
throw new IOException("Database integrity is not OK");
|
||||
}
|
||||
}
|
||||
|
||||
void exportDB(final File destFile) throws IllegalStateException, IOException {
|
||||
try {
|
||||
final String dbPath = getClosedDBPath();
|
||||
final File sourceFile = new File(dbPath);
|
||||
FileUtils.copyFile(sourceFile, destFile);
|
||||
} finally {
|
||||
setupDatabase(GBApplication.app());
|
||||
}
|
||||
}
|
||||
|
||||
void exportDB(final OutputStream dest) throws IOException {
|
||||
try {
|
||||
final String dbPath = getClosedDBPath();
|
||||
final File source = new File(dbPath);
|
||||
FileUtils.copyFileToStream(source, dest);
|
||||
} finally {
|
||||
setupDatabase(GBApplication.app());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database and returns its name.
|
||||
* Important: after calling this, you have set the database up again.
|
||||
*/
|
||||
String getClosedDBPath() throws IllegalStateException {
|
||||
final SQLiteDatabase db = daoMaster.getDatabase();
|
||||
final String path = db.getPath();
|
||||
closeDatabase();
|
||||
if (db.isOpen()) { // reference counted, so may still be open
|
||||
throw new IllegalStateException("Database must be closed");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
|
||||
public class GBDatabaseManager {
|
||||
private static final String TAG = "GBDatabaseManager";
|
||||
|
||||
private static final ReentrantReadWriteLock DB_LOCK = new ReentrantReadWriteLock(true);
|
||||
private static final GBDatabase GB_DATABASE = new GBDatabase();
|
||||
|
||||
private GBDatabaseManager() {
|
||||
}
|
||||
|
||||
public static void closeDatabase() {
|
||||
Log.v(TAG, "Trying to close database from " + Thread.currentThread().getName());
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.closeDatabase();
|
||||
} finally {
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupDatabase(final Context context) {
|
||||
Log.v(TAG, "Setting up database from " + Thread.currentThread().getName());
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.setupDatabase(context);
|
||||
} finally {
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DBHandler instance for reading/writing or throws GBException
|
||||
* when that was not successful
|
||||
* If acquiring was successful, callers must call close when they
|
||||
* are done (from the same thread that acquired the lock!
|
||||
* <p>
|
||||
* Callers must not hold a reference to the returned instance because it
|
||||
* will be invalidated at some point.
|
||||
*
|
||||
* @return the DBHandler
|
||||
*/
|
||||
public static DBHandler acquireWrite() throws GBException {
|
||||
try {
|
||||
Log.v(TAG, "Trying to acquire write lock from " + Thread.currentThread().getName());
|
||||
if (DB_LOCK.writeLock().tryLock(30, TimeUnit.SECONDS)) {
|
||||
Log.v(TAG, "Acquired write lock from " + Thread.currentThread().getName());
|
||||
return new LockHandler(DB_LOCK.writeLock(), GB_DATABASE.getDaoMaster(), GB_DATABASE.getSession());
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for DB lock");
|
||||
}
|
||||
throw new GBException("Failed to acquire database write lock");
|
||||
}
|
||||
|
||||
public static DBHandler acquireReadOnly() throws GBException {
|
||||
try {
|
||||
Log.v(TAG, "Trying to acquire read lock from " + Thread.currentThread().getName());
|
||||
if (DB_LOCK.readLock().tryLock(30, TimeUnit.SECONDS)) {
|
||||
Log.v(TAG, "Acquired read lock from " + Thread.currentThread().getName());
|
||||
return new LockHandler(DB_LOCK.readLock(), GB_DATABASE.getDaoMaster(), GB_DATABASE.getSession());
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for DB lock");
|
||||
}
|
||||
throw new GBException("Failed to acquire database read lock");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes both the old Activity database and the new one recreates it with empty tables.
|
||||
*
|
||||
* @return true on successful deletion
|
||||
*/
|
||||
public static boolean deleteActivityDatabase(final Context context) {
|
||||
Log.v(TAG, "Deleting activity database from " + Thread.currentThread().getName());
|
||||
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.closeDatabase();
|
||||
boolean result = deleteOldActivityDatabase(context);
|
||||
result &= context.deleteDatabase(GBDatabase.DATABASE_NAME);
|
||||
return result;
|
||||
} finally {
|
||||
GB_DATABASE.setupDatabase(context);
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportDB(final File destFile) throws IOException {
|
||||
Log.v(TAG, "Exporting database to file from " + Thread.currentThread().getName());
|
||||
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.exportDB(destFile);
|
||||
} finally {
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportDB(final OutputStream dest) throws IOException {
|
||||
Log.v(TAG, "Exporting database to OutputStream from " + Thread.currentThread().getName());
|
||||
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.exportDB(dest);
|
||||
} finally {
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the legacy (pre 0.12) Activity database
|
||||
*
|
||||
* @return true on successful deletion
|
||||
*/
|
||||
public static boolean deleteOldActivityDatabase(final Context context) {
|
||||
Log.v(TAG, "Deleting old activity database from " + Thread.currentThread().getName());
|
||||
|
||||
final DBHelper dbHelper = new DBHelper(context);
|
||||
boolean result = true;
|
||||
if (dbHelper.existsDB("ActivityDatabase")) {
|
||||
result = context.deleteDatabase("ActivityDatabase");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void importDB(final File fromFile) throws IllegalStateException, IOException {
|
||||
importDB(new FileInputStream(fromFile));
|
||||
}
|
||||
|
||||
public static void importDB(final InputStream inputStream) throws IllegalStateException, IOException {
|
||||
Log.v(TAG, "Importing database from " + Thread.currentThread().getName());
|
||||
|
||||
DB_LOCK.writeLock().lock();
|
||||
try {
|
||||
GB_DATABASE.importDB(inputStream);
|
||||
} finally {
|
||||
DB_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
@@ -27,90 +29,49 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
* Provides low-level access to the database.
|
||||
*/
|
||||
public class LockHandler implements DBHandler {
|
||||
private static final String TAG = "LockHandler";
|
||||
|
||||
private DaoMaster daoMaster = null;
|
||||
private DaoSession session = null;
|
||||
private SQLiteOpenHelper helper = null;
|
||||
private final Lock lock;
|
||||
|
||||
public LockHandler() {
|
||||
}
|
||||
private final DaoMaster daoMaster;
|
||||
private final DaoSession session;
|
||||
|
||||
public void init(DaoMaster daoMaster, DaoMaster.OpenHelper helper) {
|
||||
if (isValid()) {
|
||||
throw new IllegalStateException("DB must be closed before initializing it again");
|
||||
}
|
||||
if (daoMaster == null) {
|
||||
throw new IllegalArgumentException("daoMaster must not be null");
|
||||
}
|
||||
if (helper == null) {
|
||||
throw new IllegalArgumentException("helper must not be null");
|
||||
}
|
||||
private boolean closed = false;
|
||||
|
||||
public LockHandler(final Lock lock,
|
||||
final DaoMaster daoMaster,
|
||||
final DaoSession session) {
|
||||
this.lock = lock;
|
||||
this.daoMaster = daoMaster;
|
||||
this.helper = helper;
|
||||
|
||||
session = daoMaster.newSession();
|
||||
if (session == null) {
|
||||
throw new RuntimeException("Unable to create database session");
|
||||
}
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaoMaster getDaoMaster() {
|
||||
return daoMaster;
|
||||
}
|
||||
|
||||
private boolean isValid() {
|
||||
return daoMaster != null;
|
||||
}
|
||||
|
||||
private void ensureValid() {
|
||||
if (!isValid()) {
|
||||
private void ensureNotClosed() {
|
||||
if (closed) {
|
||||
throw new IllegalStateException("LockHandler is not in a valid state");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ensureValid();
|
||||
GBApplication.releaseDB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void openDb() {
|
||||
if (session != null) {
|
||||
throw new IllegalStateException("session must be null");
|
||||
if (closed) {
|
||||
Log.w(TAG, lock.getClass().getSimpleName() + " was already closed (" + Thread.currentThread().getName() + ")");
|
||||
return;
|
||||
}
|
||||
// this will create completely new db instances and in turn update this handler through #init()
|
||||
GBApplication.app().setupDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void closeDb() {
|
||||
if (session == null) {
|
||||
throw new IllegalStateException("session must not be null");
|
||||
}
|
||||
session.clear();
|
||||
session.getDatabase().close();
|
||||
session = null;
|
||||
helper = null;
|
||||
daoMaster = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteOpenHelper getHelper() {
|
||||
ensureValid();
|
||||
return helper;
|
||||
closed = true;
|
||||
Log.d(TAG, "Releasing " + lock.getClass().getSimpleName() + " from " + Thread.currentThread().getName());
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaoSession getDaoSession() {
|
||||
ensureValid();
|
||||
ensureNotClosed();
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteDatabase getDatabase() {
|
||||
ensureValid();
|
||||
ensureNotClosed();
|
||||
return daoMaster.getDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
+35
-50
@@ -19,11 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -45,15 +43,14 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.files.FileManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.ImportExportSharedPreferences;
|
||||
|
||||
|
||||
@@ -122,19 +119,9 @@ public class DataManagementActivity extends AbstractGBActivity {
|
||||
dbPath.setText(getExternalPath());
|
||||
|
||||
Button exportDBButton = findViewById(R.id.exportDataButton);
|
||||
exportDBButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
exportDB();
|
||||
}
|
||||
});
|
||||
exportDBButton.setOnClickListener(v -> exportDB());
|
||||
Button importDBButton = findViewById(R.id.importDataButton);
|
||||
importDBButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
importDB();
|
||||
}
|
||||
});
|
||||
importDBButton.setOnClickListener(v -> importDB());
|
||||
|
||||
Button showContentDataButton = findViewById(R.id.showContentDataButton);
|
||||
showContentDataButton.setOnClickListener(v -> {
|
||||
@@ -223,24 +210,31 @@ public class DataManagementActivity extends AbstractGBActivity {
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setTitle(R.string.dbmanagementactivity_export_data_title)
|
||||
.setMessage(R.string.dbmanagementactivity_export_confirmation)
|
||||
.setPositiveButton(R.string.activity_DB_ExportButton, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
exportShared();
|
||||
DBHelper helper = new DBHelper(DataManagementActivity.this);
|
||||
File dir = FileUtils.getExternalFilesDir();
|
||||
File destFile = helper.exportDB(dbHandler, dir);
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_exported_to, destFile.getAbsolutePath()), Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_error_exporting_db, ex.getLocalizedMessage()), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
.setPositiveButton(R.string.activity_DB_ExportButton, (dialog, which) -> {
|
||||
exportShared();
|
||||
|
||||
try {
|
||||
final File dir = FileUtils.getExternalFilesDir();
|
||||
final File destFile = new File(dir, "Gadgetbridge");
|
||||
if (destFile.exists()) {
|
||||
final String date = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date());
|
||||
File backup = new File(dir, destFile.getName() + "_" + date);
|
||||
if (!destFile.renameTo(backup)) {
|
||||
throw new IOException("Unable to rename existing database file");
|
||||
}
|
||||
} else if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
throw new IOException("Unable to create directory: " + dir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
GBDatabaseManager.exportDB(destFile);
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_exported_to, destFile.getAbsolutePath()), Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_error_exporting_db, ex.getLocalizedMessage()), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
.setNegativeButton(R.string.Cancel, (dialog, which) -> {
|
||||
})
|
||||
.show();
|
||||
}
|
||||
@@ -251,27 +245,18 @@ public class DataManagementActivity extends AbstractGBActivity {
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setTitle(R.string.dbmanagementactivity_import_data_title)
|
||||
.setMessage(R.string.dbmanagementactivity_overwrite_database_confirmation)
|
||||
.setPositiveButton(R.string.dbmanagementactivity_overwrite, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
DBHelper helper = new DBHelper(DataManagementActivity.this);
|
||||
File dir = FileUtils.getExternalFilesDir();
|
||||
SQLiteOpenHelper sqLiteOpenHelper = dbHandler.getHelper();
|
||||
File sourceFile = new File(dir, sqLiteOpenHelper.getDatabaseName());
|
||||
helper.importDB(dbHandler, sourceFile);
|
||||
helper.validateDB(sqLiteOpenHelper);
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_import_successful), Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_error_importing_db, ex.getLocalizedMessage()), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
importShared();
|
||||
.setPositiveButton(R.string.dbmanagementactivity_overwrite, (dialog, which) -> {
|
||||
try {
|
||||
final File dir = FileUtils.getExternalFilesDir();
|
||||
final File sourceFile = new File(dir, "Gadgetbridge");
|
||||
GBDatabaseManager.importDB(sourceFile);
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_import_successful), Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
GB.toast(DataManagementActivity.this, getString(R.string.dbmanagementactivity_error_importing_db, ex.getLocalizedMessage()), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
importShared();
|
||||
})
|
||||
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
.setNegativeButton(R.string.Cancel, (dialog, which) -> {
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@ import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB
|
||||
@@ -83,7 +84,7 @@ class DatabaseDebugFragment : AbstractDebugFragment() {
|
||||
.setTitle(R.string.dbmanagementactivity_delete_activity_data_title)
|
||||
.setMessage(R.string.dbmanagementactivity_really_delete_entire_db)
|
||||
.setPositiveButton(R.string.Delete) { _, _ ->
|
||||
if (GBApplication.deleteActivityDatabase(requireContext())) {
|
||||
if (GBDatabaseManager.deleteActivityDatabase(requireContext())) {
|
||||
GB.toast(
|
||||
requireContext(),
|
||||
getString(R.string.dbmanagementactivity_database_successfully_deleted),
|
||||
@@ -112,7 +113,7 @@ class DatabaseDebugFragment : AbstractDebugFragment() {
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setMessage(R.string.dbmanagementactivity_delete_old_activitydb_confirmation)
|
||||
.setPositiveButton(R.string.Delete) { _, _ ->
|
||||
if (GBApplication.deleteOldActivityDatabase(requireContext())) {
|
||||
if (GBDatabaseManager.deleteOldActivityDatabase(requireContext())) {
|
||||
GB.toast(
|
||||
requireContext(),
|
||||
getString(R.string.dbmanagementactivity_old_activity_db_successfully_deleted),
|
||||
|
||||
@@ -18,36 +18,16 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.database;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
|
||||
/**
|
||||
* Provides low-level access to the database.
|
||||
*/
|
||||
public interface DBHandler extends AutoCloseable {
|
||||
/**
|
||||
* Closes the database.
|
||||
*/
|
||||
void closeDb();
|
||||
|
||||
/**
|
||||
* Opens the database. Note that this is only possible after an explicit
|
||||
* #closeDb(). Initially the db is implicitly open.
|
||||
*/
|
||||
void openDb();
|
||||
|
||||
SQLiteOpenHelper getHelper();
|
||||
|
||||
/**
|
||||
* Releases the DB handler. No DB access will be possible before
|
||||
* #openDb() will be called.
|
||||
*/
|
||||
void close() throws Exception;
|
||||
|
||||
SQLiteDatabase getDatabase();
|
||||
|
||||
DaoMaster getDaoMaster();
|
||||
DaoSession getDaoSession();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package nodomain.freeyourgadget.gadgetbridge.database;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -30,18 +29,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.greenrobot.dao.Property;
|
||||
import de.greenrobot.dao.query.Query;
|
||||
@@ -77,7 +69,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
|
||||
|
||||
@@ -98,78 +89,6 @@ public class DBHelper {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database and returns its name.
|
||||
* Important: after calling this, you have to DBHandler#openDb() it again
|
||||
* to get it back to work.
|
||||
*/
|
||||
private String getClosedDBPath(DBHandler dbHandler) throws IllegalStateException {
|
||||
SQLiteDatabase db = dbHandler.getDatabase();
|
||||
String path = db.getPath();
|
||||
dbHandler.closeDb();
|
||||
if (db.isOpen()) { // reference counted, so may still be open
|
||||
throw new IllegalStateException("Database must be closed");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public File exportDB(DBHandler dbHandler, File toDir) throws IllegalStateException, IOException {
|
||||
String dbPath = getClosedDBPath(dbHandler);
|
||||
try {
|
||||
File sourceFile = new File(dbPath);
|
||||
File destFile = new File(toDir, sourceFile.getName());
|
||||
if (destFile.exists()) {
|
||||
File backup = new File(toDir, destFile.getName() + "_" + getDate());
|
||||
destFile.renameTo(backup);
|
||||
} else if (!toDir.exists()) {
|
||||
if (!toDir.mkdirs()) {
|
||||
throw new IOException("Unable to create directory: " + toDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
FileUtils.copyFile(sourceFile, destFile);
|
||||
return destFile;
|
||||
} finally {
|
||||
dbHandler.openDb();
|
||||
}
|
||||
}
|
||||
|
||||
public void exportDB(DBHandler dbHandler, OutputStream dest) throws IOException {
|
||||
String dbPath = getClosedDBPath(dbHandler);
|
||||
try {
|
||||
File source = new File(dbPath);
|
||||
FileUtils.copyFileToStream(source, dest);
|
||||
} finally {
|
||||
dbHandler.openDb();
|
||||
}
|
||||
}
|
||||
|
||||
private String getDate() {
|
||||
return new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date());
|
||||
}
|
||||
|
||||
public void importDB(DBHandler dbHandler, File fromFile) throws IllegalStateException, IOException {
|
||||
importDB(dbHandler, new FileInputStream(fromFile));
|
||||
}
|
||||
|
||||
public void importDB(DBHandler dbHandler, InputStream inputStream) throws IllegalStateException, IOException {
|
||||
String dbPath = getClosedDBPath(dbHandler);
|
||||
try {
|
||||
File toFile = new File(dbPath);
|
||||
FileUtils.copyStreamToFile(inputStream, toFile);
|
||||
} finally {
|
||||
dbHandler.openDb();
|
||||
}
|
||||
}
|
||||
|
||||
public void validateDB(SQLiteOpenHelper dbHandler) throws IOException {
|
||||
try (SQLiteDatabase db = dbHandler.getReadableDatabase()) {
|
||||
if (!db.isDatabaseIntegrityOk()) {
|
||||
throw new IOException("Database integrity is not OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dropTable(String tableName, SQLiteDatabase db) {
|
||||
String statement = "DROP TABLE IF EXISTS '" + tableName + "'";
|
||||
db.execSQL(statement);
|
||||
@@ -422,7 +341,6 @@ public class DBHelper {
|
||||
* exists already, it will be updated with the current preferences values. If no device exists
|
||||
* yet, it will be created in the database.
|
||||
*
|
||||
* @param session
|
||||
* @return the device entity corresponding to the given GBDevice
|
||||
*/
|
||||
public static Device getDevice(GBDevice gbDevice, DaoSession session) {
|
||||
@@ -596,6 +514,7 @@ public class DBHelper {
|
||||
return createTag(user, name, null, session);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
static Tag createTag(@NonNull User user, @NonNull String name, @Nullable String description, @NonNull DaoSession session) {
|
||||
Tag tag = new Tag();
|
||||
tag.setUserId(user.getId());
|
||||
|
||||
+4
-6
@@ -3,15 +3,16 @@ package nodomain.freeyourgadget.gadgetbridge.database
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class DatabaseExportWorker(
|
||||
private val mContext: Context,
|
||||
@@ -38,11 +39,8 @@ class DatabaseExportWorker(
|
||||
}
|
||||
|
||||
try {
|
||||
GBApplication.acquireDB().use { dbHandler ->
|
||||
val helper = DBHelper(mContext)
|
||||
mContext.contentResolver.openOutputStream(dst.toUri()).use { out ->
|
||||
helper.exportDB(dbHandler, out)
|
||||
}
|
||||
mContext.contentResolver.openOutputStream(dst.toUri()).use { out ->
|
||||
GBDatabaseManager.exportDB(out)
|
||||
}
|
||||
|
||||
GBApplication.getPrefs().preferences.edit {
|
||||
|
||||
+3
-9
@@ -61,15 +61,13 @@ public class SonySWR12HandlerThread extends GBDeviceIoThread {
|
||||
}
|
||||
|
||||
private void processRealTimeHeartRate(EventWithValue event) {
|
||||
try {
|
||||
DBHandler dbHandler = GBApplication.acquireDB();
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
int timestamp = getTimestamp();
|
||||
SonySWR12Sample sample = new SonySWR12Sample(timestamp, deviceId, userId, (int) event.value, ActivitySample.NOT_MEASURED, 0, 1);
|
||||
provider.addGBActivitySample(sample);
|
||||
GBApplication.releaseDB();
|
||||
Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
|
||||
.putExtra(GBDevice.EXTRA_DEVICE, getDevice())
|
||||
.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample)
|
||||
@@ -100,23 +98,20 @@ public class SonySWR12HandlerThread extends GBDeviceIoThread {
|
||||
}
|
||||
|
||||
private void addActivity(ActivityWithData activity) {
|
||||
try {
|
||||
DBHandler dbHandler = GBApplication.acquireDB();
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
int kind = SonySWR12Constants.TYPE_ACTIVITY;
|
||||
SonySWR12Sample sample = new SonySWR12Sample(activity.getTimeStampSec(), deviceId, userId, ActivitySample.NOT_MEASURED, activity.data, kind, 1);
|
||||
provider.addGBActivitySample(sample);
|
||||
GBApplication.releaseDB();
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSleep(ActivitySleep activity) {
|
||||
try {
|
||||
DBHandler dbHandler = GBApplication.acquireDB();
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
@@ -144,7 +139,6 @@ public class SonySWR12HandlerThread extends GBDeviceIoThread {
|
||||
sample = new SonySWR12Sample(activity.getTimeStampSec() + activity.durationMin * 60, deviceId, userId, ActivitySample.NOT_MEASURED, 0, SonySWR12Constants.TYPE_NOT_WORN, 1);
|
||||
provider.addGBActivitySample(sample);
|
||||
}
|
||||
GBApplication.releaseDB();
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -178,8 +178,7 @@ public class AlarmUtils {
|
||||
|
||||
public static List<Alarm> mergeOneshotToDeviceAlarms(GBDevice gbDevice, Alarm oneshot, int position) {
|
||||
List<Alarm> all_alarms = new ArrayList<>();
|
||||
try {
|
||||
DBHandler db = GBApplication.acquireDB();
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
DaoSession daoSession = db.getDaoSession();
|
||||
Device device = DBHelper.getDevice(gbDevice, daoSession);
|
||||
User user = DBHelper.getUser(daoSession);
|
||||
@@ -188,7 +187,6 @@ public class AlarmUtils {
|
||||
oneshot.setUserId(user.getId());
|
||||
daoSession.insertOrReplace(oneshot);
|
||||
all_alarms = DBHelper.getAlarms(gbDevice);
|
||||
GBApplication.releaseDB();
|
||||
} catch (Exception e) {
|
||||
GB.log("error storing one shot quick alarm", GB.ERROR, e);
|
||||
}
|
||||
|
||||
+2
-6
@@ -40,6 +40,7 @@ import java.util.zip.ZipOutputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
@@ -165,12 +166,7 @@ public class ZipBackupExportJob extends AbstractZipBackupJob {
|
||||
final ZipEntry zipEntry = new ZipEntry(DATABASE_FILENAME);
|
||||
zipOut.putNextEntry(zipEntry);
|
||||
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
final DBHelper helper = new DBHelper(context);
|
||||
helper.exportDB(dbHandler, zipOut);
|
||||
} catch (final Exception e) {
|
||||
throw new IOException("Failed to export database", e);
|
||||
}
|
||||
GBDatabaseManager.exportDB(zipOut);
|
||||
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
|
||||
+3
-8
@@ -18,7 +18,6 @@ package nodomain.freeyourgadget.gadgetbridge.util.backup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -39,6 +38,7 @@ import java.util.zip.ZipFile;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
@@ -140,13 +140,8 @@ public class ZipBackupImportJob extends AbstractZipBackupJob {
|
||||
// Restore database
|
||||
LOG.debug("Importing database");
|
||||
updateProgress(75, R.string.backup_restore_importing_database);
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
final DBHelper helper = new DBHelper(getContext());
|
||||
final SQLiteOpenHelper sqLiteOpenHelper = dbHandler.getHelper();
|
||||
try (InputStream databaseInputStream = zipFile.getInputStream(zipFile.getEntry(DATABASE_FILENAME))) {
|
||||
helper.importDB(dbHandler, databaseInputStream);
|
||||
helper.validateDB(sqLiteOpenHelper);
|
||||
}
|
||||
try (InputStream databaseInputStream = zipFile.getInputStream(zipFile.getEntry(DATABASE_FILENAME))) {
|
||||
GBDatabaseManager.importDB(databaseInputStream);
|
||||
}
|
||||
|
||||
if (isAborted()) return;
|
||||
|
||||
+18
-19
@@ -214,30 +214,29 @@ internal object RecordedWorkoutSyncer {
|
||||
sliceStartBoundary: Instant,
|
||||
sliceEndBoundary: Instant
|
||||
): List<BaseActivitySummary> {
|
||||
val db: DBHandler = GBApplication.acquireDB()
|
||||
try {
|
||||
val device = DBHelper.getDevice(gbDevice, db.daoSession)
|
||||
if (device == null) {
|
||||
LOG.warn("Device not found in database for '{}'", gbDevice.aliasOrName)
|
||||
return emptyList()
|
||||
GBApplication.acquireDB().use { db ->
|
||||
val device = DBHelper.getDevice(gbDevice, db.daoSession)
|
||||
if (device == null) {
|
||||
LOG.warn("Device not found in database for '{}'", gbDevice.aliasOrName)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val startDate = Date.from(sliceStartBoundary)
|
||||
val endDate = Date.from(sliceEndBoundary)
|
||||
|
||||
return db.daoSession.baseActivitySummaryDao.queryBuilder()
|
||||
.where(
|
||||
BaseActivitySummaryDao.Properties.DeviceId.eq(device.id),
|
||||
BaseActivitySummaryDao.Properties.StartTime.ge(startDate),
|
||||
BaseActivitySummaryDao.Properties.StartTime.lt(endDate)
|
||||
)
|
||||
.build()
|
||||
.list()
|
||||
}
|
||||
|
||||
val startDate = Date.from(sliceStartBoundary)
|
||||
val endDate = Date.from(sliceEndBoundary)
|
||||
|
||||
return db.daoSession.baseActivitySummaryDao.queryBuilder()
|
||||
.where(
|
||||
BaseActivitySummaryDao.Properties.DeviceId.eq(device.id),
|
||||
BaseActivitySummaryDao.Properties.StartTime.ge(startDate),
|
||||
BaseActivitySummaryDao.Properties.StartTime.lt(endDate)
|
||||
)
|
||||
.build()
|
||||
.list()
|
||||
} catch (e: Exception) {
|
||||
LOG.error("Error querying workouts from database for device '{}'", gbDevice.aliasOrName, e)
|
||||
return emptyList()
|
||||
} finally {
|
||||
GBApplication.releaseDB()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.io.File;
|
||||
|
||||
import ch.qos.logback.classic.util.ContextInitializer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
@@ -70,7 +71,7 @@ public abstract class TestBase {
|
||||
app = (GBApplication) RuntimeEnvironment.application;
|
||||
assertNotNull(app);
|
||||
assertNotNull(getContext());
|
||||
app.setupDatabase();
|
||||
GBDatabaseManager.setupDatabase(app);
|
||||
dbHandler = GBApplication.acquireDB();
|
||||
daoSession = dbHandler.getDaoSession();
|
||||
assertNotNull(daoSession);
|
||||
@@ -78,8 +79,7 @@ public abstract class TestBase {
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
dbHandler.closeDb();
|
||||
GBApplication.releaseDB();
|
||||
GBDatabaseManager.closeDatabase();
|
||||
}
|
||||
|
||||
protected GBDevice createDummyGDevice(String macAddress) {
|
||||
|
||||
Reference in New Issue
Block a user