2015-07-08 23:03:34 +02:00
|
|
|
package nodomain.freeyourgadget.gadgetbridge.database;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2016-03-04 23:19:44 +01:00
|
|
|
import android.database.Cursor;
|
2015-07-08 23:03:34 +02:00
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
|
|
import android.database.sqlite.SQLiteOpenHelper;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.text.SimpleDateFormat;
|
2016-06-06 23:18:46 +02:00
|
|
|
import java.util.Calendar;
|
2015-07-08 23:03:34 +02:00
|
|
|
import java.util.Date;
|
2016-05-13 23:47:47 +02:00
|
|
|
import java.util.List;
|
2015-07-08 23:03:34 +02:00
|
|
|
import java.util.Locale;
|
|
|
|
|
2016-05-13 23:47:47 +02:00
|
|
|
import de.greenrobot.dao.query.Query;
|
2015-07-10 00:31:45 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
2016-05-13 23:47:47 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.UserDao;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
2016-06-06 23:18:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate;
|
2016-05-13 23:47:47 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
2015-07-08 23:03:34 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|
|
|
|
2016-06-14 20:13:08 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-07-08 23:03:34 +02:00
|
|
|
public class DBHelper {
|
|
|
|
private final Context context;
|
|
|
|
|
|
|
|
public DBHelper(Context context) {
|
|
|
|
this.context = context;
|
|
|
|
}
|
|
|
|
|
2016-05-17 00:51:00 +02:00
|
|
|
/**
|
|
|
|
* Closes the database and returns its name.
|
|
|
|
* Important: after calling this, you have to DBHandler#openDb() it again
|
|
|
|
* to get it back to work.
|
|
|
|
* @param dbHandler
|
|
|
|
* @return
|
|
|
|
* @throws IllegalStateException
|
|
|
|
*/
|
|
|
|
private String getClosedDBPath(DBHandler dbHandler) throws IllegalStateException {
|
|
|
|
SQLiteDatabase db = dbHandler.getDatabase();
|
2015-07-08 23:03:34 +02:00
|
|
|
String path = db.getPath();
|
2016-05-17 00:51:00 +02:00
|
|
|
dbHandler.closeDb();
|
2015-08-03 01:17:02 +02:00
|
|
|
if (db.isOpen()) { // reference counted, so may still be open
|
2015-07-08 23:03:34 +02:00
|
|
|
throw new IllegalStateException("Database must be closed");
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2016-05-17 00:51:00 +02:00
|
|
|
public File exportDB(DBHandler dbHandler, File toDir) throws IllegalStateException, IOException {
|
2015-07-08 23:03:34 +02:00
|
|
|
String dbPath = getClosedDBPath(dbHandler);
|
2016-05-17 00:51:00 +02:00
|
|
|
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());
|
|
|
|
}
|
2015-07-08 23:03:34 +02:00
|
|
|
}
|
|
|
|
|
2016-05-17 00:51:00 +02:00
|
|
|
FileUtils.copyFile(sourceFile, destFile);
|
|
|
|
return destFile;
|
|
|
|
} finally {
|
|
|
|
dbHandler.openDb();
|
|
|
|
}
|
2015-07-08 23:03:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private String getDate() {
|
|
|
|
return new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date());
|
|
|
|
}
|
|
|
|
|
2016-05-17 00:51:00 +02:00
|
|
|
public void importDB(DBHandler dbHandler, File fromFile) throws IllegalStateException, IOException {
|
2015-07-08 23:03:34 +02:00
|
|
|
String dbPath = getClosedDBPath(dbHandler);
|
2016-05-17 00:51:00 +02:00
|
|
|
try {
|
|
|
|
File toFile = new File(dbPath);
|
|
|
|
FileUtils.copyFile(fromFile, toFile);
|
|
|
|
} finally {
|
|
|
|
dbHandler.openDb();
|
|
|
|
}
|
2015-07-08 23:03:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void validateDB(SQLiteOpenHelper dbHandler) throws IOException {
|
|
|
|
try (SQLiteDatabase db = dbHandler.getReadableDatabase()) {
|
|
|
|
if (!db.isDatabaseIntegrityOk()) {
|
|
|
|
throw new IOException("Database integrity is not OK");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-10 00:31:45 +02:00
|
|
|
|
|
|
|
public static void dropTable(String tableName, SQLiteDatabase db) {
|
2015-07-11 21:16:07 +02:00
|
|
|
String statement = "DROP TABLE IF EXISTS '" + tableName + "'";
|
|
|
|
db.execSQL(statement);
|
2015-07-10 00:31:45 +02:00
|
|
|
}
|
|
|
|
|
2016-03-04 23:19:44 +01:00
|
|
|
public static boolean existsColumn(String tableName, String columnName, SQLiteDatabase db) {
|
|
|
|
try (Cursor res = db.rawQuery("PRAGMA table_info('" + tableName + "')", null)) {
|
2016-03-07 00:36:39 +01:00
|
|
|
int index = res.getColumnIndex("name");
|
|
|
|
if (index < 1) {
|
|
|
|
return false; // something's really wrong
|
|
|
|
}
|
|
|
|
while (res.moveToNext()) {
|
|
|
|
String cn = res.getString(index);
|
|
|
|
if (columnName.equals(cn)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-03-04 23:19:44 +01:00
|
|
|
}
|
2016-03-07 00:36:39 +01:00
|
|
|
return false;
|
2016-03-04 23:19:44 +01:00
|
|
|
}
|
|
|
|
|
2015-07-10 00:31:45 +02:00
|
|
|
/**
|
|
|
|
* WITHOUT ROWID is only available with sqlite 3.8.2, which is available
|
|
|
|
* with Lollipop and later.
|
|
|
|
*
|
|
|
|
* @return the "WITHOUT ROWID" string or an empty string for pre-Lollipop devices
|
|
|
|
*/
|
|
|
|
public static String getWithoutRowId() {
|
|
|
|
if (GBApplication.isRunningLollipopOrLater()) {
|
|
|
|
return " WITHOUT ROWID;";
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
2016-05-13 23:47:47 +02:00
|
|
|
|
2016-05-16 23:00:04 +02:00
|
|
|
public static User getUser(DaoSession session) {
|
2016-05-23 23:31:22 +02:00
|
|
|
ActivityUser prefsUser = new ActivityUser();
|
2016-06-14 20:13:08 +02:00
|
|
|
UserDao userDao = session.getUserDao();
|
|
|
|
Query<User> query = userDao.queryBuilder().where(UserDao.Properties.Name.eq(prefsUser.getName())).build();
|
|
|
|
List<User> users = query.list();
|
2016-05-23 23:31:22 +02:00
|
|
|
User user;
|
2016-05-13 23:47:47 +02:00
|
|
|
if (users.isEmpty()) {
|
2016-05-23 23:31:22 +02:00
|
|
|
user = createUser(prefsUser, session);
|
|
|
|
} else {
|
|
|
|
user = users.get(0); // TODO: multiple users support?
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
2016-05-23 23:31:22 +02:00
|
|
|
ensureUserAttributes(user, prefsUser, session);
|
|
|
|
|
|
|
|
return user;
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
|
|
|
|
2016-05-23 23:31:22 +02:00
|
|
|
private static User createUser(ActivityUser prefsUser, DaoSession session) {
|
2016-05-13 23:47:47 +02:00
|
|
|
User user = new User();
|
|
|
|
user.setName(prefsUser.getName());
|
|
|
|
user.setBirthday(prefsUser.getUserBirthday());
|
|
|
|
user.setGender(prefsUser.getGender());
|
|
|
|
session.getUserDao().insert(user);
|
|
|
|
|
2016-05-23 23:31:22 +02:00
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ensureUserAttributes(User user, ActivityUser prefsUser, DaoSession session) {
|
|
|
|
List<UserAttributes> userAttributes = user.getUserAttributesList();
|
|
|
|
if (hasUpToDateUserAttributes(userAttributes, prefsUser)) {
|
|
|
|
return;
|
|
|
|
}
|
2016-05-13 23:47:47 +02:00
|
|
|
UserAttributes attributes = new UserAttributes();
|
|
|
|
attributes.setValidFromUTC(DateTimeUtils.todayUTC());
|
|
|
|
attributes.setHeightCM(prefsUser.getHeightCm());
|
|
|
|
attributes.setWeightKG(prefsUser.getWeightKg());
|
|
|
|
attributes.setUserId(user.getId());
|
|
|
|
session.getUserAttributesDao().insert(attributes);
|
|
|
|
|
|
|
|
userAttributes.add(attributes);
|
2016-05-23 23:31:22 +02:00
|
|
|
}
|
2016-05-13 23:47:47 +02:00
|
|
|
|
2016-05-23 23:31:22 +02:00
|
|
|
private static boolean hasUpToDateUserAttributes(List<UserAttributes> userAttributes, ActivityUser prefsUser) {
|
|
|
|
for (UserAttributes attr : userAttributes) {
|
2016-06-06 23:18:46 +02:00
|
|
|
if (!isValidNow(attr)) {
|
2016-05-23 23:31:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isEqual(attr, prefsUser)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-14 20:13:08 +02:00
|
|
|
// TODO: move this into db queries?
|
2016-06-06 23:18:46 +02:00
|
|
|
private static boolean isValidNow(ValidByDate element) {
|
|
|
|
Calendar cal = DateTimeUtils.getCalendarUTC();
|
|
|
|
Date nowUTC = cal.getTime();
|
|
|
|
return isValid(element, nowUTC);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean isValid(ValidByDate element, Date nowUTC) {
|
|
|
|
Date validFromUTC = element.getValidFromUTC();
|
|
|
|
Date validToUTC = element.getValidToUTC();
|
|
|
|
if (nowUTC.before(validFromUTC)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (validToUTC != null && nowUTC.after(validToUTC)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-23 23:31:22 +02:00
|
|
|
private static boolean isEqual(UserAttributes attr, ActivityUser prefsUser) {
|
2016-06-06 23:18:46 +02:00
|
|
|
if (prefsUser.getHeightCm() != attr.getHeightCM()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (prefsUser.getWeightKg() != attr.getWeightKG()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (prefsUser.getSleepDuration() != attr.getSleepGoalHPD()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (prefsUser.getStepsGoal() != attr.getStepsGoalSPD()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
|
|
|
|
2016-06-14 20:13:08 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-05-16 23:00:04 +02:00
|
|
|
public static Device getDevice(GBDevice gbDevice, DaoSession session) {
|
2016-05-13 23:47:47 +02:00
|
|
|
DeviceDao deviceDao = session.getDeviceDao();
|
|
|
|
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build();
|
|
|
|
List<Device> devices = query.list();
|
2016-06-14 20:13:08 +02:00
|
|
|
Device device;
|
2016-05-13 23:47:47 +02:00
|
|
|
if (devices.isEmpty()) {
|
2016-06-14 20:13:08 +02:00
|
|
|
device = createDevice(session, gbDevice);
|
|
|
|
} else {
|
|
|
|
device = devices.get(0);
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
2016-06-14 20:13:08 +02:00
|
|
|
ensureDeviceAttributes(device, gbDevice, session);
|
|
|
|
|
|
|
|
return device;
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static Device createDevice(DaoSession session, GBDevice gbDevice) {
|
|
|
|
Device device = new Device();
|
|
|
|
device.setIdentifier(gbDevice.getAddress());
|
|
|
|
device.setName(gbDevice.getName());
|
|
|
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
|
|
|
device.setManufacturer(coordinator.getManufacturer());
|
|
|
|
session.getDeviceDao().insert(device);
|
|
|
|
|
2016-06-14 20:13:08 +02:00
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ensureDeviceAttributes(Device device, GBDevice gbDevice, DaoSession session) {
|
|
|
|
List<DeviceAttributes> deviceAttributes = device.getDeviceAttributesList();
|
|
|
|
if (hasUpToDateDeviceAttributes(deviceAttributes, gbDevice)) {
|
|
|
|
return;
|
|
|
|
}
|
2016-05-13 23:47:47 +02:00
|
|
|
DeviceAttributes attributes = new DeviceAttributes();
|
2016-06-14 20:13:08 +02:00
|
|
|
|
2016-05-13 23:47:47 +02:00
|
|
|
attributes.setDeviceId(device.getId());
|
|
|
|
attributes.setValidFromUTC(DateTimeUtils.todayUTC());
|
|
|
|
attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion());
|
2016-06-14 20:13:08 +02:00
|
|
|
attributes.setFirmwareVersion2(gbDevice.getFirmwareVersion2());
|
2016-05-13 23:47:47 +02:00
|
|
|
DeviceAttributesDao attributesDao = session.getDeviceAttributesDao();
|
|
|
|
attributesDao.insert(attributes);
|
|
|
|
|
|
|
|
deviceAttributes.add(attributes);
|
2016-06-14 20:13:08 +02:00
|
|
|
}
|
2016-05-13 23:47:47 +02:00
|
|
|
|
2016-06-14 20:13:08 +02:00
|
|
|
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;
|
2016-05-13 23:47:47 +02:00
|
|
|
}
|
2015-07-08 23:03:34 +02:00
|
|
|
}
|