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

View File

@ -9,23 +9,48 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
/** /**
* A dummy DBHandler that does nothing more than implementing the release() method. * Provides lowlevel access to the database.
* It is solely used for locking concurrent access to the database session.
*/ */
public class LockHandler implements DBHandler { public class LockHandler implements DBHandler {
private final DaoMaster daoMaster; private DaoMaster daoMaster = null;
private DaoSession session; private DaoSession session = null;
private final SQLiteOpenHelper helper; 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.daoMaster = daoMaster;
this.helper = helper; this.helper = helper;
session = daoMaster.newSession(); 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 @Override
public void close() { public void close() {
ensureValid();
GBApplication.releaseDB(); GBApplication.releaseDB();
} }
@ -34,7 +59,7 @@ public class LockHandler implements DBHandler {
if (session != null) { if (session != null) {
throw new IllegalStateException("session must be 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()); GBApplication.setupDatabase(GBApplication.getContext());
} }
@ -46,20 +71,25 @@ public class LockHandler implements DBHandler {
session.clear(); session.clear();
session.getDatabase().close(); session.getDatabase().close();
session = null; session = null;
helper = null;
daoMaster = null;
} }
@Override @Override
public SQLiteOpenHelper getHelper() { public SQLiteOpenHelper getHelper() {
ensureValid();
return helper; return helper;
} }
@Override @Override
public DaoSession getDaoSession() { public DaoSession getDaoSession() {
ensureValid();
return session; return session;
} }
@Override @Override
public SQLiteDatabase getDatabase() { public SQLiteDatabase getDatabase() {
ensureValid();
return daoMaster.getDatabase(); return daoMaster.getDatabase();
} }
} }

View File

@ -4,20 +4,29 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
/**
* Provides lowlevel access to the database.
*/
public interface DBHandler extends AutoCloseable { public interface DBHandler extends AutoCloseable {
/** /**
* Closes the database. * Closes the database.
*/ */
void closeDb(); void closeDb();
/**
* Opens the database. Note that this is only possible after an explicit
* #closeDb(). Initially the db is implicitly open.
*/
void openDb(); void openDb();
SQLiteOpenHelper getHelper(); SQLiteOpenHelper getHelper();
/** /**
* Releases the DB handler. No access may be performed after calling this method. * Releases the DB handler. No DB access will be possible before
* Same as calling {@link GBApplication#releaseDB()} * #openDb() will be called.
*/ */
void close() throws Exception; 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.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; 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 { public class DBHelper {
private final Context context; private final Context context;
@ -134,9 +141,10 @@ public class DBHelper {
} }
public static User getUser(DaoSession session) { public static User getUser(DaoSession session) {
UserDao userDao = session.getUserDao();
List<User> users = userDao.loadAll();
ActivityUser prefsUser = new ActivityUser(); 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; User user;
if (users.isEmpty()) { if (users.isEmpty()) {
user = createUser(prefsUser, session); user = createUser(prefsUser, session);
@ -155,8 +163,6 @@ public class DBHelper {
user.setGender(prefsUser.getGender()); user.setGender(prefsUser.getGender());
session.getUserDao().insert(user); session.getUserDao().insert(user);
ensureUserAttributes(user, prefsUser, session);
return user; return user;
} }
@ -187,6 +193,7 @@ public class DBHelper {
return false; return false;
} }
// TODO: move this into db queries?
private static boolean isValidNow(ValidByDate element) { private static boolean isValidNow(ValidByDate element) {
Calendar cal = DateTimeUtils.getCalendarUTC(); Calendar cal = DateTimeUtils.getCalendarUTC();
Date nowUTC = cal.getTime(); Date nowUTC = cal.getTime();
@ -221,15 +228,39 @@ public class DBHelper {
return true; 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) { public static Device getDevice(GBDevice gbDevice, DaoSession session) {
DeviceDao deviceDao = session.getDeviceDao(); DeviceDao deviceDao = session.getDeviceDao();
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build(); Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build();
List<Device> devices = query.list(); List<Device> devices = query.list();
Device device;
if (devices.isEmpty()) { if (devices.isEmpty()) {
Device device = createDevice(session, gbDevice); device = createDevice(session, gbDevice);
return device; } else {
device = devices.get(0);
} }
return devices.get(0); ensureDeviceAttributes(device, gbDevice, session);
return device;
} }
private static Device createDevice(DaoSession session, GBDevice gbDevice) { private static Device createDevice(DaoSession session, GBDevice gbDevice) {
@ -239,18 +270,36 @@ public class DBHelper {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
device.setManufacturer(coordinator.getManufacturer()); device.setManufacturer(coordinator.getManufacturer());
session.getDeviceDao().insert(device); 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(); DeviceAttributes attributes = new DeviceAttributes();
attributes.setDeviceId(device.getId()); attributes.setDeviceId(device.getId());
attributes.setValidFromUTC(DateTimeUtils.todayUTC()); attributes.setValidFromUTC(DateTimeUtils.todayUTC());
attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion()); attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion());
// TODO: firmware version2? generically or through DeviceCoordinator? attributes.setFirmwareVersion2(gbDevice.getFirmwareVersion2());
DeviceAttributesDao attributesDao = session.getDeviceAttributesDao(); DeviceAttributesDao attributesDao = session.getDeviceAttributesDao();
attributesDao.insert(attributes); attributesDao.insert(attributes);
deviceAttributes.add(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.entities.MiBandActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; 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> { public abstract class AbstractSampleProvider<T extends AbstractActivitySample> implements SampleProvider<T> {
private static final WhereCondition[] NO_CONDITIONS = new WhereCondition[0]; private static final WhereCondition[] NO_CONDITIONS = new WhereCondition[0];
private final DaoSession mSession; private final DaoSession mSession;
@ -51,12 +56,12 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
@Override @Override
public void addGBActivitySample(T activitySample) { public void addGBActivitySample(T activitySample) {
getSampleDao().insert(activitySample); getSampleDao().insertOrReplace(activitySample);
} }
@Override @Override
public void addGBActivitySamples(T[] activitySamples) { public void addGBActivitySamples(T[] activitySamples) {
getSampleDao().insertInTx(activitySamples); getSampleDao().insertOrReplaceInTx(activitySamples);
} }
// @Override // @Override

View File

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