mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-13 03:07:32 +01:00
Refactoring of database access
- the db is now exclusively locked for one thread - the charts read from the db and analyze data in a background thread + some small cleanups
This commit is contained in:
parent
9ca595a5cb
commit
802f48011d
@ -201,9 +201,8 @@ public class ControlCenter extends Activity {
|
||||
case R.id.controlcenter_start_sleepmonitor:
|
||||
if (selectedDevice != null) {
|
||||
Intent startIntent;
|
||||
// startIntent = new Intent(ControlCenter.this, SleepChartActivity.class);
|
||||
startIntent = new Intent(ControlCenter.this, ChartsActivity.class);
|
||||
startIntent.putExtra("device", selectedDevice);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, selectedDevice);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
return true;
|
||||
|
@ -6,6 +6,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
@ -20,7 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
||||
@ -186,30 +187,40 @@ public class DebugActivity extends Activity {
|
||||
}
|
||||
|
||||
private void exportDB() {
|
||||
DBHandler dbHandler = null;
|
||||
try {
|
||||
ActivityDatabaseHandler dbHandler = GBApplication.getActivityDatabaseHandler();
|
||||
dbHandler = GBApplication.acquireDB();
|
||||
DBHelper helper = new DBHelper(this);
|
||||
File dir = FileUtils.getExternalFilesDir();
|
||||
File destFile = helper.exportDB(dbHandler, dir);
|
||||
Toast.makeText(this, "Exported to: " + destFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
|
||||
File destFile = helper.exportDB(dbHandler.getHelper(), dir);
|
||||
GB.toast(this, "Exported to: " + destFile.getAbsolutePath(), Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Unable to export db", ex);
|
||||
Toast.makeText(this, "Error exporting DB: " + ex.getMessage(), Toast.LENGTH_LONG).show();
|
||||
} finally {
|
||||
if (dbHandler != null) {
|
||||
dbHandler.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void importDB() {
|
||||
DBHandler dbHandler = null;
|
||||
try {
|
||||
ActivityDatabaseHandler dbHandler = GBApplication.getActivityDatabaseHandler();
|
||||
dbHandler = GBApplication.acquireDB();
|
||||
DBHelper helper = new DBHelper(this);
|
||||
File dir = FileUtils.getExternalFilesDir();
|
||||
File sourceFile = new File(dir, dbHandler.getDatabaseName());
|
||||
helper.importDB(dbHandler, sourceFile);
|
||||
helper.validateDB(dbHandler);
|
||||
Toast.makeText(this, "Import successful.", Toast.LENGTH_LONG).show();
|
||||
SQLiteOpenHelper sqLiteOpenHelper = dbHandler.getHelper();
|
||||
File sourceFile = new File(dir, sqLiteOpenHelper.getDatabaseName());
|
||||
helper.importDB(sqLiteOpenHelper, sourceFile);
|
||||
helper.validateDB(sqLiteOpenHelper);
|
||||
GB.toast(this, "Import successful.", Toast.LENGTH_LONG, GB.INFO);
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Unable to import db", ex);
|
||||
Toast.makeText(this, "Error importing DB: " + ex.getMessage(), Toast.LENGTH_LONG).show();
|
||||
GB.toast(this, "Error importing DB: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
} finally {
|
||||
if (dbHandler != null) {
|
||||
dbHandler.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,19 +7,27 @@ import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
||||
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";
|
||||
private static GBApplication context;
|
||||
private static ActivityDatabaseHandler mActivityDatabaseHandler;
|
||||
private static Lock dbLock = new ReentrantLock();
|
||||
|
||||
public GBApplication() {
|
||||
context = this;
|
||||
@ -75,8 +83,33 @@ public class GBApplication extends Application {
|
||||
return context;
|
||||
}
|
||||
|
||||
public static ActivityDatabaseHandler getActivityDatabaseHandler() {
|
||||
return mActivityDatabaseHandler;
|
||||
/**
|
||||
* 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!
|
||||
* @return the DBHandler
|
||||
* @see #releaseDB()
|
||||
* @throws GBException
|
||||
*/
|
||||
public static DBHandler acquireDB() throws GBException {
|
||||
try {
|
||||
if (dbLock.tryLock(30, TimeUnit.SECONDS)) {
|
||||
return mActivityDatabaseHandler;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Log.i(TAG, "Interrupted while waiting for DB lock");
|
||||
}
|
||||
throw new GBException("Unable to access the database.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the database lock.
|
||||
* @throws IllegalMonitorStateException if the current thread is not owning the lock
|
||||
* @see #acquireDB()
|
||||
*/
|
||||
public static void releaseDB() {
|
||||
dbLock.unlock();
|
||||
}
|
||||
|
||||
public static boolean isRunningLollipopOrLater() {
|
||||
|
@ -0,0 +1,16 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
public class GBException extends Exception {
|
||||
public GBException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
public GBException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public GBException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
public GBException() {
|
||||
super();
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import com.github.mikephil.charting.animation.Easing;
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.data.BarData;
|
||||
@ -22,11 +22,12 @@ import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.SleepUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SampleProvider;
|
||||
|
||||
@ -75,12 +76,19 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
return coordinator.getSampleProvider();
|
||||
}
|
||||
|
||||
protected List<ActivitySample> getAllSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
/**
|
||||
* Returns all kinds of samples for the given device.
|
||||
* To be called from a background thread.
|
||||
* @param device
|
||||
* @param tsFrom
|
||||
* @param tsTo
|
||||
*/
|
||||
protected List<ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
if (tsFrom == -1) {
|
||||
tsFrom = getTSLast24Hours();
|
||||
}
|
||||
SampleProvider provider = getProvider(device);
|
||||
return GBApplication.getActivityDatabaseHandler().getAllActivitySamples(tsFrom, tsTo, provider);
|
||||
return db.getAllActivitySamples(tsFrom, tsTo, provider);
|
||||
}
|
||||
|
||||
private int getTSLast24Hours() {
|
||||
@ -88,24 +96,24 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
return (int) ((now / 1000) - (24 * 60 * 60) & 0xffffffff); // -24 hours
|
||||
}
|
||||
|
||||
protected List<ActivitySample> getActivitySamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
protected List<ActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
if (tsFrom == -1) {
|
||||
tsFrom = getTSLast24Hours();
|
||||
}
|
||||
SampleProvider provider = getProvider(device);
|
||||
return GBApplication.getActivityDatabaseHandler().getActivitySamples(tsFrom, tsTo, provider);
|
||||
return db.getActivitySamples(tsFrom, tsTo, provider);
|
||||
}
|
||||
|
||||
|
||||
protected List<ActivitySample> getSleepSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
protected List<ActivitySample> getSleepSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
if (tsFrom == -1) {
|
||||
tsFrom = getTSLast24Hours();
|
||||
}
|
||||
SampleProvider provider = getProvider(device);
|
||||
return GBApplication.getActivityDatabaseHandler().getSleepSamples(tsFrom, tsTo, provider);
|
||||
return db.getSleepSamples(tsFrom, tsTo, provider);
|
||||
}
|
||||
|
||||
protected List<ActivitySample> getTestSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
protected List<ActivitySample> getTestSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.clear();
|
||||
cal.set(2015, Calendar.JUNE, 10, 6, 40);
|
||||
@ -114,7 +122,7 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
tsFrom = tsTo - (24 * 60 * 60);
|
||||
|
||||
SampleProvider provider = getProvider(device);
|
||||
return GBApplication.getActivityDatabaseHandler().getAllActivitySamples(tsFrom, tsTo, provider);
|
||||
return db.getAllActivitySamples(tsFrom, tsTo, provider);
|
||||
}
|
||||
|
||||
protected void configureChartDefaults(Chart<?> chart) {
|
||||
@ -141,6 +149,28 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
chart.setDrawGridBackground(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will invoke a background task to read the data from the
|
||||
* database, analyze it, prepare it for the charts and eventually call
|
||||
* #renderCharts
|
||||
*/
|
||||
protected void refresh() {
|
||||
createRefreshTask("Visualizing data", getActivity()).execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method reads the data from the database, analyzes and prepares it for
|
||||
* the charts. This will be called from a background task, so there must not be
|
||||
* any UI access. #renderCharts will be automatically called after this method.
|
||||
*/
|
||||
protected abstract void refreshInBackground(DBHandler db);
|
||||
|
||||
/**
|
||||
* Performs a re-rendering of the chart.
|
||||
* Always called from the UI thread.
|
||||
*/
|
||||
protected abstract void renderCharts();
|
||||
|
||||
protected void refresh(GBDevice gbDevice, BarLineChartBase chart, List<ActivitySample> samples) {
|
||||
if (gbDevice == null) {
|
||||
return;
|
||||
@ -252,12 +282,10 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
setupLegend(chart);
|
||||
|
||||
chart.setData(data);
|
||||
|
||||
chart.animateX(500, Easing.EasingOption.EaseInOutQuart);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<ActivitySample> getSamples(GBDevice device, int tsFrom, int tsTo);
|
||||
protected abstract List<ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo);
|
||||
|
||||
protected abstract void setupLegend(Chart chart);
|
||||
|
||||
@ -315,4 +343,25 @@ public abstract class AbstractChartFragment extends Fragment {
|
||||
// set1.setColor(Color.CYAN);
|
||||
return set1;
|
||||
}
|
||||
|
||||
protected RefreshTask createRefreshTask(String task, Context context) {
|
||||
return new RefreshTask(task, context);
|
||||
}
|
||||
|
||||
public class RefreshTask extends DBAccess {
|
||||
public RefreshTask(String task, Context context) {
|
||||
super(task, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground(DBHandler db) {
|
||||
refreshInBackground(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object o) {
|
||||
super.onPostExecute(o);
|
||||
renderCharts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.github.mikephil.charting.animation.Easing;
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.components.XAxis;
|
||||
@ -24,6 +25,7 @@ import java.util.List;
|
||||
import nodomain.freeyourgadget.gadgetbridge.ControlCenter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
||||
|
||||
@ -48,8 +50,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
mSmartAlarmTo = intent.getIntExtra("smartalarm_to", -1);
|
||||
mTimestampFrom = intent.getIntExtra("recording_base_timestamp", -1);
|
||||
mSmartAlarmGoneOff = intent.getIntExtra("alarm_gone_off", -1);
|
||||
List<ActivitySample> samples = getSamples(mGBDevice, -1, -1);
|
||||
refresh(mGBDevice, mChart, samples);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -108,16 +109,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawTopYLabelEntry(false);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
|
||||
List<ActivitySample> samples = getSamples(mGBDevice, -1, -1);
|
||||
refresh(mGBDevice, mChart, samples);
|
||||
|
||||
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
// mChart.getLegend().setEnabled(false);
|
||||
//
|
||||
// mChart.animateXY(2000, 2000);
|
||||
|
||||
// don't forget to refresh the drawing
|
||||
mChart.invalidate();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -126,6 +118,18 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db) {
|
||||
List<ActivitySample> samples = getSamples(db, mGBDevice, -1, -1);
|
||||
refresh(mGBDevice, mChart, samples);
|
||||
|
||||
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
}
|
||||
|
||||
protected void renderCharts() {
|
||||
mChart.animateX(500, Easing.EasingOption.EaseInOutQuart);
|
||||
}
|
||||
|
||||
protected void setupLegend(Chart chart) {
|
||||
List<Integer> legendColors = new ArrayList<>(3);
|
||||
List<String> legendLabels = new ArrayList<>(3);
|
||||
@ -140,7 +144,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ActivitySample> getSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
return getAllSamples(device, tsFrom, tsTo);
|
||||
protected List<ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
return getAllSamples(db, device, tsFrom, tsTo);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.github.mikephil.charting.animation.Easing;
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.charts.PieChart;
|
||||
@ -34,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAmount;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAmounts;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAnalysis;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
||||
|
||||
@ -64,17 +66,15 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
}
|
||||
};
|
||||
|
||||
private void refresh() {
|
||||
List<ActivitySample> samples = getSamples();
|
||||
refresh(mGBDevice, mActivityChart, getSamples());
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db) {
|
||||
List<ActivitySample> samples = getSamples(db);
|
||||
refresh(mGBDevice, mActivityChart, samples);
|
||||
refreshSleepAmounts(mGBDevice, mSleepAmountChart, samples);
|
||||
|
||||
mActivityChart.invalidate();
|
||||
mSleepAmountChart.invalidate();
|
||||
}
|
||||
|
||||
private List<ActivitySample> getSamples() {
|
||||
return getSamples(mGBDevice, -1, -1);
|
||||
private List<ActivitySample> getSamples(DBHandler db) {
|
||||
return getSamples(db, mGBDevice, -1, -1);
|
||||
}
|
||||
|
||||
private void refreshSleepAmounts(GBDevice mGBDevice, PieChart pieChart, List<ActivitySample> samples) {
|
||||
@ -105,8 +105,6 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
|
||||
pieChart.getLegend().setEnabled(false);
|
||||
//setupLegend(pieChart);
|
||||
|
||||
pieChart.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,13 +178,6 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(false);
|
||||
yAxisRight.setDrawTopYLabelEntry(false);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
|
||||
// mActivityChart.getLegend().setEnabled(false);
|
||||
//
|
||||
// mActivityChart.animateXY(2000, 2000);
|
||||
|
||||
// don't forget to refresh the drawing
|
||||
// mActivityChart.invalidate();
|
||||
}
|
||||
|
||||
protected void setupLegend(Chart chart) {
|
||||
@ -202,7 +193,12 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ActivitySample> getSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
return super.getSleepSamples(device, tsFrom, tsTo);
|
||||
protected List<ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
return super.getSleepSamples(db, device, tsFrom, tsTo);
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderCharts() {
|
||||
mActivityChart.animateX(500, Easing.EasingOption.EaseInOutQuart);
|
||||
mSleepAmountChart.invalidate();
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.ControlCenter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAnalysis;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.miband.MiBandCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
||||
@ -62,19 +63,21 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
||||
}
|
||||
};
|
||||
|
||||
private void refresh() {
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
|
||||
//NB: we could have omitted the day, but this way we can move things to the past easily
|
||||
refreshDaySteps(mTodayStepsChart, day);
|
||||
refreshWeekBeforeSteps(mWeekStepsChart, day);
|
||||
refreshDaySteps(db, mTodayStepsChart, day);
|
||||
refreshWeekBeforeSteps(db, mWeekStepsChart, day);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderCharts() {
|
||||
mWeekStepsChart.invalidate();
|
||||
mTodayStepsChart.invalidate();
|
||||
}
|
||||
|
||||
|
||||
private void refreshWeekBeforeSteps(BarLineChartBase barChart, Calendar day) {
|
||||
private void refreshWeekBeforeSteps(DBHandler db, BarLineChartBase barChart, Calendar day) {
|
||||
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
|
||||
@ -83,7 +86,7 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
||||
List<String> labels = new ArrayList<>();
|
||||
|
||||
for (int counter = 0; counter < 7; counter++) {
|
||||
entries.add(new BarEntry(analysis.calculateTotalSteps(getSamplesOfDay(day)), counter));
|
||||
entries.add(new BarEntry(analysis.calculateTotalSteps(getSamplesOfDay(db, day)), counter));
|
||||
labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale));
|
||||
day.add(Calendar.DATE, 1);
|
||||
}
|
||||
@ -102,10 +105,10 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
||||
barChart.getLegend().setEnabled(false);
|
||||
}
|
||||
|
||||
private void refreshDaySteps(PieChart pieChart, Calendar day) {
|
||||
private void refreshDaySteps(DBHandler db, PieChart pieChart, Calendar day) {
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
|
||||
int totalSteps = analysis.calculateTotalSteps(getSamplesOfDay(day));
|
||||
int totalSteps = analysis.calculateTotalSteps(getSamplesOfDay(db, day));
|
||||
|
||||
pieChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(totalSteps));
|
||||
PieData data = new PieData();
|
||||
@ -222,7 +225,7 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
||||
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
}
|
||||
|
||||
private List<ActivitySample> getSamplesOfDay(Calendar day) {
|
||||
private List<ActivitySample> getSamplesOfDay(DBHandler db, Calendar day) {
|
||||
int startTs;
|
||||
int endTs;
|
||||
|
||||
@ -236,11 +239,11 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
||||
day.set(Calendar.SECOND, 59);
|
||||
endTs = (int) (day.getTimeInMillis() / 1000);
|
||||
|
||||
return getSamples(mGBDevice, startTs, endTs);
|
||||
return getSamples(db, mGBDevice, startTs, endTs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ActivitySample> getSamples(GBDevice device, int tsFrom, int tsTo) {
|
||||
return super.getAllSamples(device, tsFrom, tsTo);
|
||||
protected List<ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
return super.getAllSamples(db, device, tsFrom, tsTo);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import java.util.ArrayList;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.schema.ActivityDBCreationScript;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
@ -27,7 +28,7 @@ import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIME
|
||||
import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TYPE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES;
|
||||
|
||||
public class ActivityDatabaseHandler extends SQLiteOpenHelper {
|
||||
public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActivityDatabaseHandler.class);
|
||||
|
||||
@ -135,6 +136,16 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper {
|
||||
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteOpenHelper getHelper() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
GBApplication.releaseDB();
|
||||
}
|
||||
|
||||
public ArrayList<ActivitySample> getAllActivitySamples(int timestamp_from, int timestamp_to, SampleProvider provider) {
|
||||
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL, provider);
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public abstract class DBAccess extends AsyncTask {
|
||||
private final String mTask;
|
||||
private final Context mContext;
|
||||
private Exception mError;
|
||||
|
||||
public DBAccess(String task, Context context) {
|
||||
mTask = task;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
protected abstract void doInBackground(DBHandler handler);
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(Object[] params) {
|
||||
DBHandler handler = null;
|
||||
try {
|
||||
handler = GBApplication.acquireDB();
|
||||
doInBackground(handler);
|
||||
} catch (Exception e) {
|
||||
mError = e;
|
||||
} finally {
|
||||
if (handler != null) {
|
||||
handler.release();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object o) {
|
||||
if (mError != null) {
|
||||
displayError(mError);
|
||||
}
|
||||
}
|
||||
|
||||
protected void displayError(Throwable error) {
|
||||
GB.toast(getContext(), getContext().getString(R.string.dbaccess_error_executing, error.getMessage()), Toast.LENGTH_LONG, GB.ERROR, error);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.database;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SampleProvider;
|
||||
|
||||
public interface DBHandler {
|
||||
public SQLiteOpenHelper getHelper();
|
||||
|
||||
/**
|
||||
* Releases the DB handler. No access may be performed after calling this method.
|
||||
* Same as calling {@link GBApplication#releaseDB()}
|
||||
*/
|
||||
void release();
|
||||
|
||||
List<ActivitySample> getAllActivitySamples(int tsFrom, int tsTo, SampleProvider provider);
|
||||
|
||||
List<ActivitySample> getActivitySamples(int tsFrom, int tsTo, SampleProvider provider);
|
||||
|
||||
List<ActivitySample> getSleepSamples(int tsFrom, int tsTo, SampleProvider provider);
|
||||
|
||||
void addGBActivitySample(int timestamp, byte provider, short intensity, byte steps, byte kind);
|
||||
|
||||
SQLiteDatabase getWritableDatabase();
|
||||
}
|
@ -23,8 +23,8 @@ public class DBHelper {
|
||||
private String getClosedDBPath(SQLiteOpenHelper dbHandler) throws IllegalStateException {
|
||||
SQLiteDatabase db = dbHandler.getReadableDatabase();
|
||||
String path = db.getPath();
|
||||
db.close(); // reference counted, so may still be open
|
||||
if (db.isOpen()) {
|
||||
db.close();
|
||||
if (db.isOpen()) { // reference counted, so may still be open
|
||||
throw new IllegalStateException("Database must be closed");
|
||||
}
|
||||
return path;
|
||||
@ -82,6 +82,4 @@ public class DBHelper {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.btle.SetDeviceBusyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.btle.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SampleProvider;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR;
|
||||
@ -840,24 +840,32 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
LOG.debug("flushing activity data holder");
|
||||
byte category, intensity, steps;
|
||||
|
||||
ActivityDatabaseHandler dbHandler = GBApplication.getActivityDatabaseHandler();
|
||||
DBHandler dbHandler = null;
|
||||
try {
|
||||
dbHandler = GBApplication.acquireDB();
|
||||
try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples
|
||||
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += 3) { //TODO: check if multiple of 3, if not something is wrong
|
||||
category = activityStruct.activityDataHolder[i];
|
||||
intensity = activityStruct.activityDataHolder[i + 1];
|
||||
steps = activityStruct.activityDataHolder[i + 2];
|
||||
|
||||
try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples
|
||||
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += 3) { //TODO: check if multiple of 3, if not something is wrong
|
||||
category = activityStruct.activityDataHolder[i];
|
||||
intensity = activityStruct.activityDataHolder[i + 1];
|
||||
steps = activityStruct.activityDataHolder[i + 2];
|
||||
|
||||
dbHandler.addGBActivitySample(
|
||||
(int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000),
|
||||
SampleProvider.PROVIDER_MIBAND,
|
||||
intensity,
|
||||
steps,
|
||||
category);
|
||||
activityStruct.activityDataTimestampProgress.add(Calendar.MINUTE, 1);
|
||||
dbHandler.addGBActivitySample(
|
||||
(int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000),
|
||||
SampleProvider.PROVIDER_MIBAND,
|
||||
intensity,
|
||||
steps,
|
||||
category);
|
||||
activityStruct.activityDataTimestampProgress.add(Calendar.MINUTE, 1);
|
||||
}
|
||||
} finally {
|
||||
activityStruct.activityDataHolderProgress = 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
GB.toast(getContext(), ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
} finally {
|
||||
activityStruct.activityDataHolderProgress = 0;
|
||||
if (dbHandler != null) {
|
||||
dbHandler.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SampleProvider;
|
||||
@ -47,13 +49,23 @@ public class GadgetbridgePblSupport {
|
||||
int samples_remaining = samples.length / 2;
|
||||
LOG.info("got " + samples_remaining + " samples");
|
||||
int offset_seconds = 0;
|
||||
while (samples_remaining-- > 0) {
|
||||
short sample = samplesBuffer.getShort();
|
||||
byte type = (byte) ((sample & 0xe000) >>> 13);
|
||||
byte intensity = (byte) ((sample & 0x1f80) >>> 7);
|
||||
byte steps = (byte) (sample & 0x007f);
|
||||
GBApplication.getActivityDatabaseHandler().addGBActivitySample(timestamp + offset_seconds, SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE, intensity, steps, type);
|
||||
offset_seconds += 60;
|
||||
DBHandler db = null;
|
||||
try {
|
||||
db = GBApplication.acquireDB();
|
||||
while (samples_remaining-- > 0) {
|
||||
short sample = samplesBuffer.getShort();
|
||||
byte type = (byte) ((sample & 0xe000) >>> 13);
|
||||
byte intensity = (byte) ((sample & 0x1f80) >>> 7);
|
||||
byte steps = (byte) (sample & 0x007f);
|
||||
db.addGBActivitySample(timestamp + offset_seconds, SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE, intensity, steps, type);
|
||||
offset_seconds += 60;
|
||||
}
|
||||
} catch (GBException e) {
|
||||
LOG.error("Error acquiring database", e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -12,6 +12,8 @@ import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonitorResult;
|
||||
@ -108,7 +110,17 @@ public class MorpheuzSupport {
|
||||
type = MorpheuzSampleProvider.TYPE_LIGHT_SLEEP;
|
||||
}
|
||||
if (index >= 0) {
|
||||
GBApplication.getActivityDatabaseHandler().addGBActivitySample(recording_base_timestamp + index * 600, SampleProvider.PROVIDER_PEBBLE_MORPHEUZ, intensity, (byte) 0, type);
|
||||
DBHandler db = null;
|
||||
try {
|
||||
db = GBApplication.acquireDB();
|
||||
db.addGBActivitySample(recording_base_timestamp + index * 600, SampleProvider.PROVIDER_PEBBLE_MORPHEUZ, intensity, (byte) 0, type);
|
||||
} catch (GBException e) {
|
||||
LOG.error("Error acquiring database", e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctrl_message = MorpheuzSupport.CTRL_VERSION_DONE | MorpheuzSupport.CTRL_SET_LAST_SENT | MorpheuzSupport.CTRL_DO_NEXT;
|
||||
|
@ -5,7 +5,7 @@
|
||||
android:title="@string/controlcenter_fetch_activity_data"/>
|
||||
<item
|
||||
android:id="@+id/controlcenter_start_sleepmonitor"
|
||||
android:title="@string/controlcenter_start_sleepmonitor"/>
|
||||
android:title="@string/controlcenter_start_activitymonitor"/>
|
||||
<item
|
||||
android:id="@+id/controlcenter_configure_alarms"
|
||||
android:title="@string/controlcenter_start_configure_alarms"/>
|
||||
|
@ -158,4 +158,6 @@
|
||||
<string name="chart_no_data_synchronize">No data. Synchronize device?</string>
|
||||
<string name="user_feedback_miband_activity_data_transfer">About to transfer %1$s of data starting from %2$s</string>
|
||||
<string name="miband_prefs_fitness_goal">Target steps for each day</string>
|
||||
<string name="dbaccess_error_executing">Error executing \'%1$s\'</string>
|
||||
<string name="controlcenter_start_activitymonitor">Your Activity (ALPHA)</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user