WIP: more db work

master
cpfeiffer 2016-06-14 20:13:08 +02:00
parent ae548d0806
commit 61957d6cb0
6 changed files with 134 additions and 25 deletions

View File

@ -60,7 +60,7 @@ public class GBApplication extends Application {
private static LimitedQueue mIDSenderLookup = new LimitedQueue(16);
private static Prefs prefs;
private static GBPrefs gbPrefs;
private static DBHandler lockHandler;
private static LockHandler lockHandler;
/**
* Note: is null on Lollipop and Kitkat
*/
@ -156,7 +156,10 @@ public class GBApplication extends Application {
DBOpenHelper helper = new DBOpenHelper(context, "test-db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
lockHandler = new LockHandler(daoMaster, helper);
if (lockHandler == null) {
lockHandler = new LockHandler();
}
lockHandler.init(daoMaster, helper);
}
public static Context getContext() {
@ -179,6 +182,9 @@ public class GBApplication extends Application {
* If acquiring was successful, callers must call #releaseDB when they
* are done (from the same thread that acquired the lock!
*
* 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()

View File

@ -9,23 +9,48 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
/**
* A dummy DBHandler that does nothing more than implementing the release() method.
* It is solely used for locking concurrent access to the database session.
* Provides lowlevel access to the database.
*/
public class LockHandler implements DBHandler {
private final DaoMaster daoMaster;
private DaoSession session;
private final SQLiteOpenHelper helper;
private DaoMaster daoMaster = null;
private DaoSession session = null;
private SQLiteOpenHelper helper = null;
public LockHandler(DaoMaster daoMaster, DBOpenHelper helper) {
public LockHandler() {
}
public void init(DaoMaster daoMaster, DBOpenHelper 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");
}
this.daoMaster = daoMaster;
this.helper = helper;
session = daoMaster.newSession();
if (session == null) {
throw new RuntimeException("Unable to create database session");
}
}
private boolean isValid() {
return daoMaster != null;
}
private void ensureValid() {
if (!isValid()) {
throw new IllegalStateException("LockHandler is not in a valid state");
}
}
@Override
public void close() {
ensureValid();
GBApplication.releaseDB();
}
@ -34,7 +59,7 @@ public class LockHandler implements DBHandler {
if (session != null) {
throw new IllegalStateException("session must be null");
}
// this will create completely new db instances. This handler will be dead
// this will create completely new db instances and in turn update this handler through #init()
GBApplication.setupDatabase(GBApplication.getContext());
}
@ -46,20 +71,25 @@ public class LockHandler implements DBHandler {
session.clear();
session.getDatabase().close();
session = null;
helper = null;
daoMaster = null;
}
@Override
public SQLiteOpenHelper getHelper() {
ensureValid();
return helper;
}
@Override
public DaoSession getDaoSession() {
ensureValid();
return session;
}
@Override
public SQLiteDatabase getDatabase() {
ensureValid();
return daoMaster.getDatabase();
}
}

View File

@ -4,20 +4,29 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
/**
* Provides lowlevel 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 access may be performed after calling this method.
* Same as calling {@link GBApplication#releaseDB()}
* Releases the DB handler. No DB access will be possible before
* #openDb() will be called.
*/
void close() throws Exception;

View File

@ -31,6 +31,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
/**
* Provides utiliy access to some common entities, so you won't need to use
* their DAO classes.
*
* Maybe this code should actually be in the DAO classes themselves, but then
* these should be under revision control instead of 100% generated at build time.
*/
public class DBHelper {
private final Context context;
@ -134,9 +141,10 @@ public class DBHelper {
}
public static User getUser(DaoSession session) {
UserDao userDao = session.getUserDao();
List<User> users = userDao.loadAll();
ActivityUser prefsUser = new ActivityUser();
UserDao userDao = session.getUserDao();
Query<User> query = userDao.queryBuilder().where(UserDao.Properties.Name.eq(prefsUser.getName())).build();
List<User> users = query.list();
User user;
if (users.isEmpty()) {
user = createUser(prefsUser, session);
@ -155,8 +163,6 @@ public class DBHelper {
user.setGender(prefsUser.getGender());
session.getUserDao().insert(user);
ensureUserAttributes(user, prefsUser, session);
return user;
}
@ -187,6 +193,7 @@ public class DBHelper {
return false;
}
// TODO: move this into db queries?
private static boolean isValidNow(ValidByDate element) {
Calendar cal = DateTimeUtils.getCalendarUTC();
Date nowUTC = cal.getTime();
@ -221,15 +228,39 @@ public class DBHelper {
return true;
}
private static boolean isEqual(DeviceAttributes attr, GBDevice gbDevice) {
if (!isEqual(attr.getFirmwareVersion1(), gbDevice.getFirmwareVersion())) {
return false;
}
if (!isEqual(attr.getFirmwareVersion2(), gbDevice.getFirmwareVersion2())) {
return false;
}
return true;
}
private static boolean isEqual(String s1, String s2) {
if (s1 == s2) {
return true;
}
if (s1 != null) {
return s1.equals(s2);
}
return false;
}
public static Device getDevice(GBDevice gbDevice, DaoSession session) {
DeviceDao deviceDao = session.getDeviceDao();
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build();
List<Device> devices = query.list();
Device device;
if (devices.isEmpty()) {
Device device = createDevice(session, gbDevice);
return device;
device = createDevice(session, gbDevice);
} else {
device = devices.get(0);
}
return devices.get(0);
ensureDeviceAttributes(device, gbDevice, session);
return device;
}
private static Device createDevice(DaoSession session, GBDevice gbDevice) {
@ -239,18 +270,36 @@ public class DBHelper {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
device.setManufacturer(coordinator.getManufacturer());
session.getDeviceDao().insert(device);
List<DeviceAttributes> deviceAttributes = device.getDeviceAttributesList();
return device;
}
private static void ensureDeviceAttributes(Device device, GBDevice gbDevice, DaoSession session) {
List<DeviceAttributes> deviceAttributes = device.getDeviceAttributesList();
if (hasUpToDateDeviceAttributes(deviceAttributes, gbDevice)) {
return;
}
DeviceAttributes attributes = new DeviceAttributes();
attributes.setDeviceId(device.getId());
attributes.setValidFromUTC(DateTimeUtils.todayUTC());
attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion());
// TODO: firmware version2? generically or through DeviceCoordinator?
attributes.setFirmwareVersion2(gbDevice.getFirmwareVersion2());
DeviceAttributesDao attributesDao = session.getDeviceAttributesDao();
attributesDao.insert(attributes);
deviceAttributes.add(attributes);
}
return device;
private static boolean hasUpToDateDeviceAttributes(List<DeviceAttributes> deviceAttributes, GBDevice gbDevice) {
for (DeviceAttributes attr : deviceAttributes) {
if (!isValidNow(attr)) {
return false;
}
if (isEqual(attr, gbDevice)) {
return true;
}
}
return false;
}
}

View File

@ -10,6 +10,11 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
/**
* Base class for all sample providers. A Sample provider is device specific and provides
* access to the device specific samples. There are both read and write operations.
* @param <T>
*/
public abstract class AbstractSampleProvider<T extends AbstractActivitySample> implements SampleProvider<T> {
private static final WhereCondition[] NO_CONDITIONS = new WhereCondition[0];
private final DaoSession mSession;
@ -51,12 +56,12 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
@Override
public void addGBActivitySample(T activitySample) {
getSampleDao().insert(activitySample);
getSampleDao().insertOrReplace(activitySample);
}
@Override
public void addGBActivitySamples(T[] activitySamples) {
getSampleDao().insertInTx(activitySamples);
getSampleDao().insertOrReplaceInTx(activitySamples);
}
// @Override

View File

@ -46,8 +46,9 @@ public class GBDevice implements Parcelable {
private final String mName;
private final String mAddress;
private final DeviceType mDeviceType;
private String mFirmwareVersion = null;
private String mHardwareVersion = null;
private String mFirmwareVersion;
private String mFirmwareVersion2;
private String mHardwareVersion;
private State mState = State.NOT_CONNECTED;
private short mBatteryLevel = BATTERY_UNKNOWN;
private short mBatteryThresholdPercent = BATTERY_THRESHOLD_PERCENT;
@ -68,6 +69,7 @@ public class GBDevice implements Parcelable {
mAddress = in.readString();
mDeviceType = DeviceType.values()[in.readInt()];
mFirmwareVersion = in.readString();
mFirmwareVersion2 = in.readString();
mHardwareVersion = in.readString();
mState = State.values()[in.readInt()];
mBatteryLevel = (short) in.readInt();
@ -86,6 +88,7 @@ public class GBDevice implements Parcelable {
dest.writeString(mAddress);
dest.writeInt(mDeviceType.ordinal());
dest.writeString(mFirmwareVersion);
dest.writeString(mFirmwareVersion2);
dest.writeString(mHardwareVersion);
dest.writeInt(mState.ordinal());
dest.writeInt(mBatteryLevel);
@ -113,11 +116,18 @@ public class GBDevice implements Parcelable {
public String getFirmwareVersion() {
return mFirmwareVersion;
}
public String getFirmwareVersion2() {
return mFirmwareVersion2;
}
public void setFirmwareVersion(String firmwareVersion) {
mFirmwareVersion = firmwareVersion;
}
public void setFirmwareVersion2(String firmwareVersion2) {
mFirmwareVersion2 = firmwareVersion2;
}
public String getHardwareVersion() {
return mHardwareVersion;
}