From f31eabcefe96f4183c0894805badde18e5479c8c Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Fri, 5 Jan 2018 22:51:18 +0100 Subject: [PATCH] Minor update to show off the possiblities for the refactoring. This is just a short step, major improvements are still to come. --- .../androidaps/Services/DataService.java | 5 +- .../androidaps/db/DataServiceManager.java | 25 +++ .../androidaps/db/DatabaseHelper.java | 26 ++- .../nightscout/androidaps/db/FoodDao.java | 35 ---- .../nightscout/androidaps/db/FoodService.java | 175 ++++++++++++++++++ .../nightscout/androidaps/db/ICallback.java | 15 ++ .../androidaps/plugins/Food/FoodFragment.java | 7 +- 7 files changed, 242 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java create mode 100644 app/src/main/java/info/nightscout/androidaps/db/FoodService.java create mode 100644 app/src/main/java/info/nightscout/androidaps/db/ICallback.java diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index 570aab6ffc..f6787283c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -20,6 +20,7 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.DataServiceManager; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; @@ -522,14 +523,14 @@ public class DataService extends IntentService { private void handleRemovedFoodRecord(String _id) { try { - MainApp.getDbHelper().foodHelper.getDao().deleteByNSId(_id); + DataServiceManager.getInstance().getFoodService().deleteByNSId(_id); } catch (SQLException e) { log.error("Unhandled exception", e); } } public void handleAddChangeFoodRecord(JSONObject trJson) throws JSONException { - MainApp.getDbHelper().foodHelper.createFoodFromJsonIfNotExists(trJson); + DataServiceManager.getInstance().getFoodService().createFoodFromJsonIfNotExists(trJson); } private void handleRemovedRecordFromNS(String _id) { diff --git a/app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java b/app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java new file mode 100644 index 0000000000..dc4d71b98d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.db; + +/** + * This class should get registered in the MainApp. + */ + +public class DataServiceManager { + + private static final DataServiceManager INSTANCE = new DataServiceManager(); + + private FoodService foodService; + + public static DataServiceManager getInstance() { + return INSTANCE; + } + + public FoodService getFoodService() { + if (this.foodService == null) { + this.foodService = new FoodService(); + } + + return foodService; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 79b06e5977..871c37f66d 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -35,7 +35,6 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventFoodDatabaseChanged; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventRefreshOverview; @@ -55,6 +54,14 @@ import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; import info.nightscout.utils.PercentageSplitter; +/** + * This Helper contains all resource to provide a central DB management functionality. Only methods handling + * data-structure (and not the DB content) should be contained in here (meaning DDL and not SQL). + * + * This class can safely be called from Services, but should not call Services to avoid circular dependencies. + * One major issue with this (right now) are the scheduled events, which are put into the service. Therefor all + * direct calls to the corresponding methods (eg. resetDatabases) should be done by a central service. + */ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static Logger log = LoggerFactory.getLogger(DatabaseHelper.class); @@ -94,12 +101,9 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledProfileSwitchEventPost = null; - public FoodHelper foodHelper; - public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); onCreate(getWritableDatabase(), getConnectionSource()); - foodHelper = new FoodHelper(getConnectionSource()); //onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1); } @@ -219,7 +223,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - foodHelper.resetFood(); + resetFood(); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); @@ -232,7 +236,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { scheduleTemporaryTargetChange(); scheduleCareportalEventChange(); scheduleProfileSwitchChange(); - foodHelper.scheduleFoodChange(); new java.util.Timer().schedule( new java.util.TimerTask() { @Override @@ -308,6 +311,17 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { scheduleProfileSwitchChange(); } + public void resetFood() { + try { + TableUtils.dropTable(this.getConnectionSource(), Food.class, true); + TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + } + + + // ------------------ getDao ------------------------------------------- private Dao getDaoTempTargets() throws SQLException { diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java b/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java index 18e543149c..b3cefaa417 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java +++ b/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java @@ -86,7 +86,6 @@ public class FoodDao extends BaseDaoImpl { old.copyFrom(food); this.create(old); log.debug("FOOD: Updating record by _id: " + old.toString()); - FoodHelper.scheduleFoodChange(); return true; } else { return false; @@ -95,7 +94,6 @@ public class FoodDao extends BaseDaoImpl { } this.createOrUpdate(food); log.debug("FOOD: New record: " + food.toString()); - FoodHelper.scheduleFoodChange(); return true; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -103,39 +101,6 @@ public class FoodDao extends BaseDaoImpl { return false; } - /** - * deletes an entry by its NS Id. - * - * Basically a convenience method for findByNSId and deleteFood. - * - * should be moved to a Service - * - * @param _id - */ - public void deleteByNSId(String _id) throws SQLException { - Food stored = findByNSId(_id); - if (stored != null) { - log.debug("FOOD: Removing Food record from database: " + stored.toString()); - this.deleteFood(stored); - } - } - - /** - * deletes the food and sends the foodChange Event - * - * should be moved ot a Service - * - * @param food - */ - public void deleteFood(Food food) { - try { - this.delete(food); - FoodHelper.scheduleFoodChange(); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - } - /** * finds food by its NS Id. * diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodService.java b/app/src/main/java/info/nightscout/androidaps/db/FoodService.java new file mode 100644 index 0000000000..b753764341 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/FoodService.java @@ -0,0 +1,175 @@ +package info.nightscout.androidaps.db; + +import android.content.Intent; +import android.os.IBinder; +import android.support.annotation.Nullable; + +import com.j256.ormlite.android.apptools.OrmLiteBaseService; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.events.Event; +import info.nightscout.androidaps.events.EventFoodDatabaseChanged; + +/** + * Created by mike on 24.09.2017. + */ + +public class FoodService extends OrmLiteBaseService { + private static Logger log = LoggerFactory.getLogger(FoodService.class); + + private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor(); + private static ScheduledFuture scheduledFoodEventPost = null; + + public FoodDao getDao() { + return FoodDao.with(this.getConnectionSource()); + } + + /** + * This service method is just taking care about the Food-Table, + * a central dataService should be use for throwing events for all + * tables. + */ + public void resetFood() { + this.getHelper().resetFood(); + scheduleFoodChange(); + } + + /** + * A place to centrally register events to be posted, if any data changed. + * This should be implemented in an abstract service-class. + * + * We do need to make sure, that ICallback is extended to be able to handle multiple + * events, or handle a list of events. + * + * on some methods the earliestDataChange event is handled separatly, in that it is checked if it is + * set to null by another event already (eg. scheduleExtendedBolusChange). + * + * @param event + * @param eventWorker + * @param callback + */ + private void scheduleEvent(final Event event, ScheduledExecutorService eventWorker, + final ICallback callback) { + + class PostRunnable implements Runnable { + public void run() { + log.debug("Firing EventFoodChange"); + MainApp.bus().post(event); + callback.setPost(null); + } + } + // prepare task for execution in 1 sec + // cancel waiting task to prevent sending multiple posts + if (callback.getPost() != null) + callback.getPost().cancel(false); + Runnable task = new PostRunnable(); + final int sec = 1; + callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS)); + } + + /** + * Schedule a foodChange Event. + */ + public void scheduleFoodChange() { + this.scheduleEvent(new EventFoodDatabaseChanged(), foodEventWorker, new ICallback() { + @Override + public void setPost(ScheduledFuture post) { + scheduledFoodEventPost = post; + } + + @Override + public ScheduledFuture getPost() { + return scheduledFoodEventPost; + } + }); + } + + public List getFoodData() { + return this.getDao().getFoodData(); + } + + /* + { + "_id": "551ee3ad368e06e80856e6a9", + "type": "food", + "category": "Zakladni", + "subcategory": "Napoje", + "name": "Mleko", + "portion": 250, + "carbs": 12, + "gi": 1, + "created_at": "2015-04-14T06:59:16.500Z", + "unit": "ml" + } + */ + public void createFoodFromJsonIfNotExists(JSONObject trJson) { + try { + Food food = Food.createFromJson(trJson); + this.getDao().createOrUpdate(food); + } catch (JSONException | SQLException e) { + log.error("Unhandled exception", e); + } + } + + /** + * Create of update a food record by the NS (Nightscout) Id. + * + * @param food + * @return + */ + public boolean createOrUpdateByNS(Food food) { + boolean result = this.getDao().createOrUpdateByNS(food); + if (result) this.scheduleFoodChange(); + + return result; + } + + /** + * deletes an entry by its NS Id. + * + * Basically a convenience method for findByNSId and delete. + * + * @param _id + */ + public void deleteByNSId(String _id) throws SQLException { + Food stored = this.getDao().findByNSId(_id); + if (stored != null) { + log.debug("FOOD: Removing Food record from database: " + stored.toString()); + this.delete(stored); + } + } + + /** + * deletes the food and sends the foodChange Event + * + * should be moved ot a Service + * + * @param food + */ + public void delete(Food food) { + try { + this.getDao().delete(food); + this.scheduleFoodChange(); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/ICallback.java b/app/src/main/java/info/nightscout/androidaps/db/ICallback.java new file mode 100644 index 0000000000..7a9360035f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/ICallback.java @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.db; + +import java.util.concurrent.ScheduledFuture; + +/** + * Created by triplem on 05.01.18. + */ + +public interface ICallback { + + void setPost(ScheduledFuture post); + + ScheduledFuture getPost(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodFragment.java index 3f11663c43..7d55e66d64 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodFragment.java @@ -30,6 +30,7 @@ import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.DataServiceManager; import info.nightscout.androidaps.db.Food; import info.nightscout.androidaps.events.EventFoodDatabaseChanged; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; @@ -121,7 +122,7 @@ public class FoodFragment extends SubscriberFragment { } }); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().foodHelper.getDao().getFoodData()); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(DataServiceManager.getInstance().getFoodService().getFoodData()); recyclerView.setAdapter(adapter); loadData(); @@ -144,7 +145,7 @@ public class FoodFragment extends SubscriberFragment { } void loadData() { - unfiltered = MainApp.getDbHelper().foodHelper.getDao().getFoodData(); + unfiltered = DataServiceManager.getInstance().getFoodService().getFoodData(); } void fillCategories() { @@ -299,7 +300,7 @@ public class FoodFragment extends SubscriberFragment { if (_id != null && !_id.equals("")) { NSUpload.removeFoodFromNS(_id); } - MainApp.getDbHelper().foodHelper.getDao().deleteFood(food); + DataServiceManager.getInstance().getFoodService().delete(food); } }); builder.setNegativeButton(MainApp.sResources.getString(R.string.cancel), null);