From 9cb019f5ca8959b84dc2e77b7af60216ddb4744c Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Thu, 4 Jan 2018 22:22:08 +0100 Subject: [PATCH 1/9] #557 - showoff of possible refactorings - see REFACTOR.md for additonal details --- .../androidaps/Services/DataService.java | 9 +- .../androidaps/db/DatabaseHelper.java | 4 +- .../info/nightscout/androidaps/db/Food.java | 28 ++- .../nightscout/androidaps/db/FoodDao.java | 161 ++++++++++++++ .../nightscout/androidaps/db/FoodHelper.java | 208 ------------------ .../androidaps/plugins/Food/FoodFragment.java | 6 +- .../info/nightscout/utils/JsonHelper.java | 49 +++++ 7 files changed, 249 insertions(+), 216 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/db/FoodDao.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/db/FoodHelper.java create mode 100644 app/src/main/java/info/nightscout/utils/JsonHelper.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 b242c4c099..570aab6ffc 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -12,6 +12,8 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.sql.SQLException; + import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -518,7 +520,12 @@ public class DataService extends IntentService { } private void handleRemovedFoodRecord(String _id) { - MainApp.getDbHelper().foodHelper.deleteFoodById(_id); + + try { + MainApp.getDbHelper().foodHelper.getDao().deleteByNSId(_id); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } } public void handleAddChangeFoodRecord(JSONObject trJson) throws JSONException { 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 082f872d20..79b06e5977 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -68,7 +68,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public static final String DATABASE_DBREQUESTS = "DBRequests"; public static final String DATABASE_CAREPORTALEVENTS = "CareportalEvents"; public static final String DATABASE_PROFILESWITCHES = "ProfileSwitches"; - public static final String DATABASE_FOODS = "Foods"; private static final int DATABASE_VERSION = 8; @@ -95,11 +94,12 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledProfileSwitchEventPost = null; - public FoodHelper foodHelper = new FoodHelper(this); + 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); } diff --git a/app/src/main/java/info/nightscout/androidaps/db/Food.java b/app/src/main/java/info/nightscout/androidaps/db/Food.java index cb856df073..f3f7071cb5 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Food.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Food.java @@ -3,20 +3,25 @@ package info.nightscout.androidaps.db; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Objects; +import info.nightscout.utils.JsonHelper; + /** * Created by mike on 20.09.2017. */ - -@DatabaseTable(tableName = DatabaseHelper.DATABASE_FOODS) +@DatabaseTable(tableName = Food.TABLE_FOODS) public class Food { private static Logger log = LoggerFactory.getLogger(Food.class); + public static final String TABLE_FOODS = "Foods"; + @DatabaseField(id = true) public long key; @@ -64,6 +69,25 @@ public class Food { key = System.currentTimeMillis(); } + public static Food createFromJson(JSONObject json) throws JSONException { + Food food = new Food(); + if ("food".equals(JsonHelper.safeGetString(json, "type"))) { + food._id = JsonHelper.safeGetString(json, "_id"); + food.category = JsonHelper.safeGetString(json, "category"); + food.subcategory = JsonHelper.safeGetString(json, "subcategory"); + food.name = JsonHelper.safeGetString(json, "name"); + food.units = JsonHelper.safeGetString(json, "unit"); + food.portion = JsonHelper.safeGetDouble(json, "portion"); + food.carbs = JsonHelper.safeGetInt(json, "carbs"); + food.gi = JsonHelper.safeGetInt(json, "gi"); + food.energy = JsonHelper.safeGetInt(json, "energy"); + food.protein = JsonHelper.safeGetInt(json, "protein"); + food.fat = JsonHelper.safeGetInt(json, "fat"); + } + + return food; + } + public boolean isEqual(Food other) { if (portion != other.portion) return false; diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java b/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java new file mode 100644 index 0000000000..18e543149c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java @@ -0,0 +1,161 @@ +package info.nightscout.androidaps.db; + +import android.content.Context; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.BaseDaoImpl; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.support.ConnectionSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by triplem on 04.01.18. + */ + +public class FoodDao extends BaseDaoImpl { + + private static final Logger log = LoggerFactory.getLogger(FoodDao.class); + + public FoodDao(ConnectionSource source) throws SQLException { + super(source, Food.class); + } + + /** + * Static instantiation methods. The database connection is accessed via + * the OpenHelperManager which keeps a count of the number of objects + * using the connection. Thus every call to connect() must be matched by + * a call to release() once the session is done. + */ + public static FoodDao connect(Context context) { + return with(OpenHelperManager.getHelper(context, DatabaseHelper.class) + .getConnectionSource()); + } + + public static FoodDao with(ConnectionSource connection) { + try { + return (FoodDao) DaoManager.createDao(connection, Food.class); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Releasing the DAO flags the connection manager that the DAO is no + * longer using the connection. When the connection count is zero, the + * connection manager will close the database. + */ + public void release() { + OpenHelperManager.releaseHelper(); + } + + /** + * + * @return + * + * @deprecated should use queryForAll instead, which is a standard method of the ORMLite DAO + */ + public List getFoodData() { + try { + QueryBuilder queryBuilder = this.queryBuilder(); + PreparedQuery preparedQuery = queryBuilder.prepare(); + return this.query(preparedQuery); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + + return new ArrayList<>(); + } + + public boolean createOrUpdateByNS(Food food) { + try { + // find by NS _id + if (food._id != null) { + Food old = this.findByNSId(food._id); + + if (old != null) { + if (!old.isEqual(food)) { + this.delete(old); // need to delete/create because date may change too + old.copyFrom(food); + this.create(old); + log.debug("FOOD: Updating record by _id: " + old.toString()); + FoodHelper.scheduleFoodChange(); + return true; + } else { + return false; + } + } + } + this.createOrUpdate(food); + log.debug("FOOD: New record: " + food.toString()); + FoodHelper.scheduleFoodChange(); + return true; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + 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. + * + * @param _id + * @return + */ + public Food findByNSId(String _id) { + try { + List list = this.queryForEq("_id", _id); + + if (list.size() == 1) { // really? if there are more then one result, then we do not return anything... + return list.get(0); + } + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return null; + } + + + +} + diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodHelper.java b/app/src/main/java/info/nightscout/androidaps/db/FoodHelper.java deleted file mode 100644 index a933c2166e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/db/FoodHelper.java +++ /dev/null @@ -1,208 +0,0 @@ -package info.nightscout.androidaps.db; - -import com.j256.ormlite.android.AndroidConnectionSource; -import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.stmt.PreparedQuery; -import com.j256.ormlite.stmt.QueryBuilder; -import com.j256.ormlite.stmt.Where; -import com.j256.ormlite.table.TableUtils; - -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.SQLException; -import java.util.ArrayList; -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.EventFoodDatabaseChanged; - -/** - * Created by mike on 24.09.2017. - */ - -public class FoodHelper { - private static Logger log = LoggerFactory.getLogger(FoodHelper.class); - - DatabaseHelper databaseHelper; - - private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledFoodEventPost = null; - - public FoodHelper(DatabaseHelper databaseHelper) { - this.databaseHelper = databaseHelper; - } - - private Dao getDaoFood() throws SQLException { - return databaseHelper.getDao(Food.class); - } - - public void resetFood() { - try { - TableUtils.dropTable(databaseHelper.getConnectionSource(), Food.class, true); - TableUtils.createTableIfNotExists(databaseHelper.getConnectionSource(), Food.class); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - scheduleFoodChange(); - } - - public List getFoodData() { - try { - Dao daoFood = getDaoFood(); - List foods; - QueryBuilder queryBuilder = daoFood.queryBuilder(); - PreparedQuery preparedQuery = queryBuilder.prepare(); - foods = daoFood.query(preparedQuery); - return foods; - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public boolean createOrUpdate(Food food) { - try { - // find by NS _id - if (food._id != null && !food._id.equals("")) { - Food old; - - QueryBuilder queryBuilder = getDaoFood().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", food._id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List found = getDaoFood().query(preparedQuery); - if (found.size() > 0) { - old = found.get(0); - if (!old.isEqual(food)) { - getDaoFood().delete(old); // need to delete/create because date may change too - old.copyFrom(food); - getDaoFood().create(old); - log.debug("FOOD: Updating record by _id: " + old.toString()); - scheduleFoodChange(); - return true; - } else { - return false; - } - } else { - getDaoFood().createOrUpdate(food); - log.debug("FOOD: New record: " + food.toString()); - scheduleFoodChange(); - return true; - } - } - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - return false; - } - - public void delete(Food food) { - try { - getDaoFood().delete(food); - scheduleFoodChange(); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - } - - public static void scheduleFoodChange() { - class PostRunnable implements Runnable { - public void run() { - log.debug("Firing EventFoodChange"); - MainApp.bus().post(new EventFoodDatabaseChanged()); - scheduledFoodEventPost = null; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledFoodEventPost != null) - scheduledFoodEventPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - scheduledFoodEventPost = foodEventWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - /* - { - "_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 = new Food(); - if (trJson.has("type") && trJson.getString("type").equals("food")) { - if (trJson.has("_id")) - food._id = trJson.getString("_id"); - if (trJson.has("category")) - food.category = trJson.getString("category"); - if (trJson.has("subcategory")) - food.subcategory = trJson.getString("subcategory"); - if (trJson.has("name")) - food.name = trJson.getString("name"); - if (trJson.has("unit")) - food.units = trJson.getString("unit"); - if (trJson.has("portion")) - food.portion = trJson.getDouble("portion"); - if (trJson.has("carbs")) - food.carbs = trJson.getInt("carbs"); - if (trJson.has("gi")) - food.gi = trJson.getInt("gi"); - if (trJson.has("energy")) - food.energy = trJson.getInt("energy"); - if (trJson.has("protein")) - food.protein = trJson.getInt("protein"); - if (trJson.has("fat")) - food.fat = trJson.getInt("fat"); - } - createOrUpdate(food); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - - public void deleteFoodById(String _id) { - Food stored = findFoodById(_id); - if (stored != null) { - log.debug("FOOD: Removing Food record from database: " + stored.toString()); - delete(stored); - scheduleFoodChange(); - } - } - - public Food findFoodById(String _id) { - try { - QueryBuilder queryBuilder = getDaoFood().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", _id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List list = getDaoFood().query(preparedQuery); - - if (list.size() == 1) { - return list.get(0); - } else { - return null; - } - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - return null; - } - -} 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 a5c00aab7f..3f11663c43 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 @@ -121,7 +121,7 @@ public class FoodFragment extends SubscriberFragment { } }); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().foodHelper.getFoodData()); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().foodHelper.getDao().getFoodData()); recyclerView.setAdapter(adapter); loadData(); @@ -144,7 +144,7 @@ public class FoodFragment extends SubscriberFragment { } void loadData() { - unfiltered = MainApp.getDbHelper().foodHelper.getFoodData(); + unfiltered = MainApp.getDbHelper().foodHelper.getDao().getFoodData(); } void fillCategories() { @@ -299,7 +299,7 @@ public class FoodFragment extends SubscriberFragment { if (_id != null && !_id.equals("")) { NSUpload.removeFoodFromNS(_id); } - MainApp.getDbHelper().foodHelper.delete(food); + MainApp.getDbHelper().foodHelper.getDao().deleteFood(food); } }); builder.setNegativeButton(MainApp.sResources.getString(R.string.cancel), null); diff --git a/app/src/main/java/info/nightscout/utils/JsonHelper.java b/app/src/main/java/info/nightscout/utils/JsonHelper.java new file mode 100644 index 0000000000..5068b08671 --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/JsonHelper.java @@ -0,0 +1,49 @@ +package info.nightscout.utils; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JSonHelper is a Helper class which contains several methods to safely get data from the ggiven JSONObject. + * + * Created by triplem on 04.01.18. + */ + +public class JsonHelper { + + private static final Logger log = LoggerFactory.getLogger(JsonHelper.class); + + private JsonHelper() {}; + + public static String safeGetString(JSONObject json, String fieldName) throws JSONException { + String result = null; + + if (json.has(fieldName)) { + result = json.getString(fieldName); + } + + return result; + } + + public static double safeGetDouble(JSONObject json, String fieldName) throws JSONException { + double result = 0d; + + if (json.has(fieldName)) { + result = json.getDouble(fieldName); + } + + return result; + } + + public static int safeGetInt(JSONObject json, String fieldName) throws JSONException { + int result = 0; + + if (json.has(fieldName)) { + result = json.getInt(fieldName); + } + + return result; + } +} From f31eabcefe96f4183c0894805badde18e5479c8c Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Fri, 5 Jan 2018 22:51:18 +0100 Subject: [PATCH 2/9] 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); From a78652f737c8deb257cde162f90cca11406d23b6 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Sat, 6 Jan 2018 20:22:13 +0100 Subject: [PATCH 3/9] This needs to get reverted, but for testing purposes... --- .../androidaps/plugins/Food/FoodFragment.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 7d55e66d64..f8042f3de5 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 @@ -19,8 +19,10 @@ import android.widget.ImageView; import android.widget.TextView; import com.crashlytics.android.Crashlytics; +import com.google.common.util.concurrent.ServiceManager; import com.squareup.otto.Subscribe; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,10 +59,27 @@ public class FoodFragment extends SubscriberFragment { final String EMPTY = MainApp.sResources.getString(R.string.none); + private static final String json = " {\n" + + " \"_id\": \"551ee3ad368e06e80856e6a9\",\n" + + " \"type\": \"food\",\n" + + " \"category\": \"Category\",\n" + + " \"subcategory\": \"Subcatgory\",\n" + + " \"name\": \"Name\",\n" + + " \"portion\": 250,\n" + + " \"carbs\": 12,\n" + + " \"gi\": 1,\n" + + " \"created_at\": \"2015-04-14T06:59:16.500Z\",\n" + + " \"unit\": \"ml\"\n" + + " }"; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { try { + log.info("Strartup..."); + JSONObject food = new JSONObject(json); + DataServiceManager.getInstance().getFoodService().createFoodFromJsonIfNotExists(food); + View view = inflater.inflate(R.layout.food_fragment, container, false); filter = (EditText) view.findViewById(R.id.food_filter); clearFilter = (ImageView) view.findViewById(R.id.food_clearfilter); From 7d669fbeb8089fda873fea73e5fc7987ff96bd27 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Sat, 6 Jan 2018 21:32:10 +0100 Subject: [PATCH 4/9] Make food working, adopt category selection slightly --- .../info/nightscout/androidaps/db/Food.java | 2 +- .../nightscout/androidaps/db/FoodService.java | 4 +++ .../androidaps/plugins/Food/FoodFragment.java | 33 +++++-------------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/Food.java b/app/src/main/java/info/nightscout/androidaps/db/Food.java index f3f7071cb5..9824d1964b 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Food.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Food.java @@ -16,7 +16,7 @@ import info.nightscout.utils.JsonHelper; * Created by mike on 20.09.2017. */ -@DatabaseTable(tableName = Food.TABLE_FOODS) +@DatabaseTable(tableName = Food.TABLE_FOODS, daoClass=FoodDao.class) public class Food { private static Logger log = LoggerFactory.getLogger(Food.class); diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodService.java b/app/src/main/java/info/nightscout/androidaps/db/FoodService.java index b753764341..56fd4b3a4f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/FoodService.java +++ b/app/src/main/java/info/nightscout/androidaps/db/FoodService.java @@ -32,6 +32,10 @@ public class FoodService extends OrmLiteBaseService { private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledFoodEventPost = null; + public FoodService() { + this.onCreate(); + } + public FoodDao getDao() { return FoodDao.with(this.getConnectionSource()); } 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 f8042f3de5..bd3cb9fe6c 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 @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Set; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -59,27 +60,10 @@ public class FoodFragment extends SubscriberFragment { final String EMPTY = MainApp.sResources.getString(R.string.none); - private static final String json = " {\n" + - " \"_id\": \"551ee3ad368e06e80856e6a9\",\n" + - " \"type\": \"food\",\n" + - " \"category\": \"Category\",\n" + - " \"subcategory\": \"Subcatgory\",\n" + - " \"name\": \"Name\",\n" + - " \"portion\": 250,\n" + - " \"carbs\": 12,\n" + - " \"gi\": 1,\n" + - " \"created_at\": \"2015-04-14T06:59:16.500Z\",\n" + - " \"unit\": \"ml\"\n" + - " }"; - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { try { - log.info("Strartup..."); - JSONObject food = new JSONObject(json); - DataServiceManager.getInstance().getFoodService().createFoodFromJsonIfNotExists(food); - View view = inflater.inflate(R.layout.food_fragment, container, false); filter = (EditText) view.findViewById(R.id.food_filter); clearFilter = (ImageView) view.findViewById(R.id.food_clearfilter); @@ -168,16 +152,15 @@ public class FoodFragment extends SubscriberFragment { } void fillCategories() { - categories = new ArrayList<>(); + Set catSet = new HashSet<>(); for (Food f : unfiltered) { if (f.category != null && !f.category.equals("")) - categories.add(f.category); + catSet.add(f.category); } // make it unique - categories = new ArrayList<>(new HashSet<>(categories)); - + categories = new ArrayList<>(catSet); categories.add(0, MainApp.sResources.getString(R.string.none)); ArrayAdapter adapterCategories = new ArrayAdapter<>(getContext(), @@ -187,19 +170,19 @@ public class FoodFragment extends SubscriberFragment { void fillSubcategories() { String categoryFilter = category.getSelectedItem().toString(); - subcategories = new ArrayList<>(); + + Set subCatSet = new HashSet<>(); if (!categoryFilter.equals(EMPTY)) { for (Food f : unfiltered) { if (f.category != null && f.category.equals(categoryFilter)) if (f.subcategory != null && !f.subcategory.equals("")) - subcategories.add(f.subcategory); + subCatSet.add(f.subcategory); } } // make it unique - subcategories = new ArrayList<>(new HashSet<>(subcategories)); - + subcategories = new ArrayList<>(subCatSet); subcategories.add(0, MainApp.sResources.getString(R.string.none)); ArrayAdapter adapterSubcategories = new ArrayAdapter<>(getContext(), From 147448afca654b31be46c053b0888944384900b3 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Sun, 7 Jan 2018 20:24:42 +0100 Subject: [PATCH 5/9] #557 - Encapsulate food plugin --- .../androidaps/Services/DataService.java | 61 +-- .../androidaps/db/DataServiceManager.java | 25 -- .../androidaps/db/DatabaseHelper.java | 29 +- .../nightscout/androidaps/db/FoodDao.java | 126 ------ .../nightscout/androidaps/db/FoodService.java | 179 --------- .../androidaps/events/NsFoodEvent.java | 35 ++ .../androidaps/{db => plugins/Food}/Food.java | 4 +- .../androidaps/plugins/Food/FoodFragment.java | 10 +- .../androidaps/plugins/Food/FoodPlugin.java | 9 + .../androidaps/plugins/Food/FoodService.java | 360 ++++++++++++++++++ 10 files changed, 433 insertions(+), 405 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/db/FoodDao.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/db/FoodService.java create mode 100644 app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java rename app/src/main/java/info/nightscout/androidaps/{db => plugins/Food}/Food.java (97%) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.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 f6787283c8..ab0b6414fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -12,16 +12,14 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.sql.SQLException; - import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; 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.events.NsFoodEvent; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; @@ -477,62 +475,19 @@ public class DataService extends IntentService { } } - if (intent.getAction().equals(Intents.ACTION_NEW_FOOD) || intent.getAction().equals(Intents.ACTION_CHANGED_FOOD)) { - try { - if (bundles.containsKey("food")) { - String trstring = bundles.getString("food"); - handleAddChangeFoodRecord(new JSONObject(trstring)); - } - if (bundles.containsKey("foods")) { - String trstring = bundles.getString("foods"); - JSONArray jsonArray = new JSONArray(trstring); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject trJson = jsonArray.getJSONObject(i); - handleAddChangeFoodRecord(trJson); - } - } - } catch (Exception e) { - log.error("Unhandled exception", e); - } + if (intent.getAction().equals(Intents.ACTION_NEW_FOOD) + || intent.getAction().equals(Intents.ACTION_CHANGED_FOOD)) { + int mode = Intents.ACTION_NEW_FOOD.equals(intent.getAction()) ? NsFoodEvent.ADD : NsFoodEvent.UPDATE; + NsFoodEvent evt = new NsFoodEvent(mode, bundles); + MainApp.bus().post(evt); } if (intent.getAction().equals(Intents.ACTION_REMOVED_FOOD)) { - try { - if (bundles.containsKey("food")) { - String trstring = bundles.getString("food"); - JSONObject trJson = new JSONObject(trstring); - String _id = trJson.getString("_id"); - handleRemovedFoodRecord(_id); - } - - if (bundles.containsKey("foods")) { - String trstring = bundles.getString("foods"); - JSONArray jsonArray = new JSONArray(trstring); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject trJson = jsonArray.getJSONObject(i); - String _id = trJson.getString("_id"); - handleRemovedFoodRecord(_id); - } - } - } catch (Exception e) { - log.error("Unhandled exception", e); - } + NsFoodEvent evt = new NsFoodEvent(NsFoodEvent.REMOVE, bundles); + MainApp.bus().post(evt); } } - private void handleRemovedFoodRecord(String _id) { - - try { - DataServiceManager.getInstance().getFoodService().deleteByNSId(_id); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - } - - public void handleAddChangeFoodRecord(JSONObject trJson) throws JSONException { - DataServiceManager.getInstance().getFoodService().createFoodFromJsonIfNotExists(trJson); - } - private void handleRemovedRecordFromNS(String _id) { MainApp.getDbHelper().deleteTreatmentById(_id); MainApp.getDbHelper().deleteTempTargetById(_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 deleted file mode 100644 index dc4d71b98d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/db/DataServiceManager.java +++ /dev/null @@ -1,25 +0,0 @@ -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 871c37f66d..aabefb1307 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -8,6 +8,7 @@ import android.support.annotation.Nullable; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.CloseableIterator; import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.DaoManager; import com.j256.ormlite.stmt.PreparedQuery; import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.Where; @@ -101,6 +102,9 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledProfileSwitchEventPost = null; + private int oldVersion = 0; + private int newVersion = 0; + public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); onCreate(getWritableDatabase(), getConnectionSource()); @@ -120,7 +124,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - TableUtils.createTableIfNotExists(connectionSource, Food.class); } catch (SQLException e) { log.error("Can't create database", e); throw new RuntimeException(e); @@ -130,6 +133,9 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @Override public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { try { + this.oldVersion = oldVersion; + this.newVersion = newVersion; + if (oldVersion == 7 && newVersion == 8) { log.debug("Upgrading database from v7 to v8"); TableUtils.dropTable(connectionSource, Treatment.class, true); @@ -145,7 +151,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); TableUtils.dropTable(connectionSource, CareportalEvent.class, true); TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); - TableUtils.dropTable(connectionSource, Food.class, true); onCreate(database, connectionSource); } } catch (SQLException e) { @@ -154,6 +159,14 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } + public int getOldVersion() { + return oldVersion; + } + + public int getNewVersion() { + return newVersion; + } + /** * Close the database connections and clear any cached DAOs. */ @@ -223,7 +236,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - resetFood(); +// resetFood(); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); @@ -311,16 +324,6 @@ 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 ------------------------------------------- diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java b/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java deleted file mode 100644 index b3cefaa417..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/db/FoodDao.java +++ /dev/null @@ -1,126 +0,0 @@ -package info.nightscout.androidaps.db; - -import android.content.Context; - -import com.j256.ormlite.android.apptools.OpenHelperManager; -import com.j256.ormlite.dao.BaseDaoImpl; -import com.j256.ormlite.dao.DaoManager; -import com.j256.ormlite.stmt.PreparedQuery; -import com.j256.ormlite.stmt.QueryBuilder; -import com.j256.ormlite.support.ConnectionSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by triplem on 04.01.18. - */ - -public class FoodDao extends BaseDaoImpl { - - private static final Logger log = LoggerFactory.getLogger(FoodDao.class); - - public FoodDao(ConnectionSource source) throws SQLException { - super(source, Food.class); - } - - /** - * Static instantiation methods. The database connection is accessed via - * the OpenHelperManager which keeps a count of the number of objects - * using the connection. Thus every call to connect() must be matched by - * a call to release() once the session is done. - */ - public static FoodDao connect(Context context) { - return with(OpenHelperManager.getHelper(context, DatabaseHelper.class) - .getConnectionSource()); - } - - public static FoodDao with(ConnectionSource connection) { - try { - return (FoodDao) DaoManager.createDao(connection, Food.class); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - /** - * Releasing the DAO flags the connection manager that the DAO is no - * longer using the connection. When the connection count is zero, the - * connection manager will close the database. - */ - public void release() { - OpenHelperManager.releaseHelper(); - } - - /** - * - * @return - * - * @deprecated should use queryForAll instead, which is a standard method of the ORMLite DAO - */ - public List getFoodData() { - try { - QueryBuilder queryBuilder = this.queryBuilder(); - PreparedQuery preparedQuery = queryBuilder.prepare(); - return this.query(preparedQuery); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - - return new ArrayList<>(); - } - - public boolean createOrUpdateByNS(Food food) { - try { - // find by NS _id - if (food._id != null) { - Food old = this.findByNSId(food._id); - - if (old != null) { - if (!old.isEqual(food)) { - this.delete(old); // need to delete/create because date may change too - old.copyFrom(food); - this.create(old); - log.debug("FOOD: Updating record by _id: " + old.toString()); - return true; - } else { - return false; - } - } - } - this.createOrUpdate(food); - log.debug("FOOD: New record: " + food.toString()); - return true; - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - return false; - } - - /** - * finds food by its NS Id. - * - * @param _id - * @return - */ - public Food findByNSId(String _id) { - try { - List list = this.queryForEq("_id", _id); - - if (list.size() == 1) { // really? if there are more then one result, then we do not return anything... - return list.get(0); - } - } catch (SQLException e) { - log.error("Unhandled exception", e); - } - return null; - } - - - -} - diff --git a/app/src/main/java/info/nightscout/androidaps/db/FoodService.java b/app/src/main/java/info/nightscout/androidaps/db/FoodService.java deleted file mode 100644 index 56fd4b3a4f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/db/FoodService.java +++ /dev/null @@ -1,179 +0,0 @@ -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 FoodService() { - this.onCreate(); - } - - 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/events/NsFoodEvent.java b/app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java new file mode 100644 index 0000000000..ff3b1e78a7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java @@ -0,0 +1,35 @@ +package info.nightscout.androidaps.events; + +import android.os.Bundle; + +/** + * Event which is published with data fetched from NightScout specific for the + * Food-class. + * + * Payload is the from NS retrieved JSON-String which should be handled by all + * subscriber. + */ + +public class NsFoodEvent extends Event { + + public static final int ADD = 0; + public static final int UPDATE = 1; + public static final int REMOVE = 2; + + private final int mode; + + private final Bundle payload; + + public NsFoodEvent(int mode, Bundle payload) { + this.mode = mode; + this.payload = payload; + } + + public int getMode() { + return mode; + } + + public Bundle getPayload() { + return payload; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/Food.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java similarity index 97% rename from app/src/main/java/info/nightscout/androidaps/db/Food.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java index 9824d1964b..9b38dbcbe9 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Food.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.db; +package info.nightscout.androidaps.plugins.Food; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @@ -16,7 +16,7 @@ import info.nightscout.utils.JsonHelper; * Created by mike on 20.09.2017. */ -@DatabaseTable(tableName = Food.TABLE_FOODS, daoClass=FoodDao.class) +@DatabaseTable(tableName = Food.TABLE_FOODS) public class Food { private static Logger log = LoggerFactory.getLogger(Food.class); 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 bd3cb9fe6c..2a3bf0788a 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 @@ -19,10 +19,8 @@ import android.widget.ImageView; import android.widget.TextView; import com.crashlytics.android.Crashlytics; -import com.google.common.util.concurrent.ServiceManager; import com.squareup.otto.Subscribe; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,8 +31,6 @@ import java.util.Set; 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; import info.nightscout.utils.NSUpload; @@ -125,7 +121,7 @@ public class FoodFragment extends SubscriberFragment { } }); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(DataServiceManager.getInstance().getFoodService().getFoodData()); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getSpecificPlugin(FoodPlugin.class).getService().getFoodData()); recyclerView.setAdapter(adapter); loadData(); @@ -148,7 +144,7 @@ public class FoodFragment extends SubscriberFragment { } void loadData() { - unfiltered = DataServiceManager.getInstance().getFoodService().getFoodData(); + unfiltered = MainApp.getSpecificPlugin(FoodPlugin.class).getService().getFoodData(); } void fillCategories() { @@ -302,7 +298,7 @@ public class FoodFragment extends SubscriberFragment { if (_id != null && !_id.equals("")) { NSUpload.removeFoodFromNS(_id); } - DataServiceManager.getInstance().getFoodService().delete(food); + MainApp.getSpecificPlugin(FoodPlugin.class).getService().delete(food); } }); builder.setNegativeButton(MainApp.sResources.getString(R.string.cancel), null); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java index 255b4fd951..64517a19a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java @@ -13,6 +13,12 @@ public class FoodPlugin implements PluginBase { private static FoodPlugin plugin = null; + private FoodService service; + + private FoodPlugin() { + this.service = new FoodService(); + } + public static FoodPlugin getPlugin() { if (plugin == null) plugin = new FoodPlugin(); @@ -81,5 +87,8 @@ public class FoodPlugin implements PluginBase { return -1; } + public FoodService getService() { + return this.service; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java new file mode 100644 index 0000000000..038363f972 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java @@ -0,0 +1,360 @@ +package info.nightscout.androidaps.plugins.Food; + +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.support.annotation.Nullable; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.android.apptools.OrmLiteBaseService; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; +import com.squareup.otto.Subscribe; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.ArrayList; +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.db.DatabaseHelper; +import info.nightscout.androidaps.db.ICallback; +import info.nightscout.androidaps.events.Event; +import info.nightscout.androidaps.events.EventFoodDatabaseChanged; +import info.nightscout.androidaps.events.NsFoodEvent; + +/** + * 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 FoodService() { + onCreate(); + dbInitialize(); + MainApp.bus().register(this); + } + + /** + * This method is a simple re-implementation of the database create and up/downgrade functionality + * in SQLiteOpenHelper#getDatabaseLocked method. + * + * It is implemented to be able to late initialize separate plugins of the application. + */ + protected void dbInitialize() { + DatabaseHelper helper = OpenHelperManager.getHelper(this, DatabaseHelper.class); + int newVersion = helper.getNewVersion(); + int oldVersion = helper.getOldVersion(); + + if (oldVersion > newVersion) { + onDowngrade(this.getConnectionSource(), oldVersion, newVersion); + } else { + onUpgrade(this.getConnectionSource(), oldVersion, newVersion); + } + } + + public Dao getDao() { + try { + return DaoManager.createDao(this.getConnectionSource(), Food.class); + } catch (SQLException e) { + log.error("Cannot create Dao for Food.class"); + } + + return null; + } + + @Subscribe + public void handleNsEvent(NsFoodEvent event) { + int mode = event.getMode(); + Bundle payload = event.getPayload(); + + try { + if (payload.containsKey("food")) { + JSONObject json = new JSONObject(payload.getString("food")); + if (mode == NsFoodEvent.ADD || mode == NsFoodEvent.UPDATE) { + this.createFoodFromJsonIfNotExists(json); + } else { + this.deleteNS(json); + } + } + + if (payload.containsKey("foods")) { + JSONArray array = new JSONArray(payload.getString("foods")); + if (mode == NsFoodEvent.ADD || mode == NsFoodEvent.UPDATE) { + this.createFoodFromJsonIfNotExists(array); + } else { + this.deleteNS(array); + } + } + } catch (JSONException e) { + log.error("Unhandled Exception", e); + } + } + + @Override + public void onCreate() { + super.onCreate(); + try { + log.info("onCreate"); + TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class); + } catch (SQLException e) { + log.error("Can't create database", e); + throw new RuntimeException(e); + } + } + + public void onUpgrade(ConnectionSource connectionSource, int oldVersion, int newVersion) { + if (oldVersion == 7 && newVersion == 8) { + log.debug("Upgrading database from v7 to v8"); + } else { + log.info("onUpgrade"); +// this.resetFood(); + } + } + + public void onDowngrade(ConnectionSource connectionSource, int oldVersion, int newVersion) { + // this method is not supported right now + } + + 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); + } + 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() { + try { + return this.getDao().queryForAll(); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + + return new ArrayList<>(); + } + + /* + { + "_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 json) { + try { + Food food = Food.createFromJson(json); + this.createFoodFromJsonIfNotExists(food); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } + + public void createFoodFromJsonIfNotExists(JSONArray array) { + try { + for(int n = 0; n < array.length(); n++) + { + JSONObject json = array.getJSONObject(n); + Food food = Food.createFromJson(json); + this.createFoodFromJsonIfNotExists(food); + } + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } + + public void createFoodFromJsonIfNotExists(Food food) { + try { + this.getDao().createOrUpdate(food); + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + } + + public void deleteNS(JSONObject json) { + try { + String _id = json.getString("_id"); + this.deleteByNSId(_id); + } catch (JSONException | SQLException e) { + log.error("Unhandled exception", e); + } + } + + public void deleteNS(JSONArray array) { + try { + for(int n = 0; n < array.length(); n++) + { + JSONObject json = array.getJSONObject(n); + this.deleteNS(json); + } + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } + + /** + * 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.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); + } + } + + /** + * Create of update a food record by the NS (Nightscout) Id. + * + * @param food + * @return + */ + public boolean createOrUpdateByNS(Food food) { + try { + // find by NS _id + if (food._id != null) { + Food old = this.findByNSId(food._id); + + if (old != null) { + if (!old.isEqual(food)) { + this.delete(old); // need to delete/create because date may change too + old.copyFrom(food); + this.getDao().create(old); + log.debug("FOOD: Updating record by _id: " + old.toString()); + this.scheduleFoodChange(); + return true; + } else { + return false; + } + } + } + this.getDao().createOrUpdate(food); + log.debug("FOOD: New record: " + food.toString()); + this.scheduleFoodChange(); + return true; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return false; + } + + /** + * finds food by its NS Id. + * + * @param _id + * @return + */ + public Food findByNSId(String _id) { + try { + List list = this.getDao().queryForEq("_id", _id); + + if (list.size() == 1) { // really? if there are more then one result, then we do not return anything... + return list.get(0); + } + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return null; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} From 9a7e57f0a0668c56f1b1e48e29293c2c4839c22f Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Mon, 29 Jan 2018 21:57:50 +0100 Subject: [PATCH 6/9] rename event to stay in compliance to naming conventions --- .../info/nightscout/androidaps/Services/DataService.java | 8 ++++---- .../events/{NsFoodEvent.java => EventNsFood.java} | 4 ++-- .../nightscout/androidaps/plugins/Food/FoodService.java | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) rename app/src/main/java/info/nightscout/androidaps/events/{NsFoodEvent.java => EventNsFood.java} (87%) 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 ab0b6414fb..f89b85b8f4 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -19,7 +19,7 @@ import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.events.EventNewBasalProfile; -import info.nightscout.androidaps.events.NsFoodEvent; +import info.nightscout.androidaps.events.EventNsFood; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; @@ -477,13 +477,13 @@ public class DataService extends IntentService { if (intent.getAction().equals(Intents.ACTION_NEW_FOOD) || intent.getAction().equals(Intents.ACTION_CHANGED_FOOD)) { - int mode = Intents.ACTION_NEW_FOOD.equals(intent.getAction()) ? NsFoodEvent.ADD : NsFoodEvent.UPDATE; - NsFoodEvent evt = new NsFoodEvent(mode, bundles); + int mode = Intents.ACTION_NEW_FOOD.equals(intent.getAction()) ? EventNsFood.ADD : EventNsFood.UPDATE; + EventNsFood evt = new EventNsFood(mode, bundles); MainApp.bus().post(evt); } if (intent.getAction().equals(Intents.ACTION_REMOVED_FOOD)) { - NsFoodEvent evt = new NsFoodEvent(NsFoodEvent.REMOVE, bundles); + EventNsFood evt = new EventNsFood(EventNsFood.REMOVE, bundles); MainApp.bus().post(evt); } } diff --git a/app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java similarity index 87% rename from app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java rename to app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java index ff3b1e78a7..90b6f5681b 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/NsFoodEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java @@ -10,7 +10,7 @@ import android.os.Bundle; * subscriber. */ -public class NsFoodEvent extends Event { +public class EventNsFood extends Event { public static final int ADD = 0; public static final int UPDATE = 1; @@ -20,7 +20,7 @@ public class NsFoodEvent extends Event { private final Bundle payload; - public NsFoodEvent(int mode, Bundle payload) { + public EventNsFood(int mode, Bundle payload) { this.mode = mode; this.payload = payload; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java index 038363f972..b6d8b89582 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java @@ -32,7 +32,7 @@ import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.ICallback; import info.nightscout.androidaps.events.Event; import info.nightscout.androidaps.events.EventFoodDatabaseChanged; -import info.nightscout.androidaps.events.NsFoodEvent; +import info.nightscout.androidaps.events.EventNsFood; /** * Created by mike on 24.09.2017. @@ -79,14 +79,14 @@ public class FoodService extends OrmLiteBaseService { } @Subscribe - public void handleNsEvent(NsFoodEvent event) { + public void handleNsEvent(EventNsFood event) { int mode = event.getMode(); Bundle payload = event.getPayload(); try { if (payload.containsKey("food")) { JSONObject json = new JSONObject(payload.getString("food")); - if (mode == NsFoodEvent.ADD || mode == NsFoodEvent.UPDATE) { + if (mode == EventNsFood.ADD || mode == EventNsFood.UPDATE) { this.createFoodFromJsonIfNotExists(json); } else { this.deleteNS(json); @@ -95,7 +95,7 @@ public class FoodService extends OrmLiteBaseService { if (payload.containsKey("foods")) { JSONArray array = new JSONArray(payload.getString("foods")); - if (mode == NsFoodEvent.ADD || mode == NsFoodEvent.UPDATE) { + if (mode == EventNsFood.ADD || mode == EventNsFood.UPDATE) { this.createFoodFromJsonIfNotExists(array); } else { this.deleteNS(array); From 2a1bfe1f6c5f8659ae5f35da9bc7a68c0352f054 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Mon, 29 Jan 2018 22:23:20 +0100 Subject: [PATCH 7/9] merge changes on foodhelper --- .../androidaps/plugins/Food/FoodService.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java index b6d8b89582..bb0487dc53 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java @@ -307,7 +307,7 @@ public class FoodService extends OrmLiteBaseService { public boolean createOrUpdateByNS(Food food) { try { // find by NS _id - if (food._id != null) { + if (food._id != null && !food._id.equals("")) { Food old = this.findByNSId(food._id); if (old != null) { @@ -322,11 +322,12 @@ public class FoodService extends OrmLiteBaseService { return false; } } + } else { + this.getDao().createOrUpdate(food); + log.debug("FOOD: New record: " + food.toString()); + this.scheduleFoodChange(); + return true; } - this.getDao().createOrUpdate(food); - log.debug("FOOD: New record: " + food.toString()); - this.scheduleFoodChange(); - return true; } catch (SQLException e) { log.error("Unhandled exception", e); } From f68d8f1626e7e11c0ab3675494268a86f4d987c8 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Tue, 30 Jan 2018 19:33:10 +0100 Subject: [PATCH 8/9] Cleanup Refactoring --- .../nightscout/androidaps/MainActivity.java | 4 ++ .../androidaps/db/DatabaseHelper.java | 1 - .../androidaps/plugins/Food/FoodFragment.java | 3 +- .../androidaps/plugins/Food/FoodService.java | 63 +++++++++++-------- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index e77be36ba6..ef09a9c7b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -42,6 +42,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.Food.FoodPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.tabs.SlidingTabLayout; import info.nightscout.androidaps.tabs.TabPageAdapter; @@ -367,6 +368,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @Override public void onClick(DialogInterface dialog, int which) { MainApp.getDbHelper().resetDatabases(); + // should be handled by Plugin-Interface and + // additional service interface and plugin registry + MainApp.getSpecificPlugin(FoodPlugin.class).getService().resetFood(); } }) .create() 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 aabefb1307..9738192593 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -236,7 +236,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); -// resetFood(); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); 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 2a3bf0788a..c5032c5299 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 @@ -121,7 +121,8 @@ public class FoodFragment extends SubscriberFragment { } }); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getSpecificPlugin(FoodPlugin.class).getService().getFoodData()); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp + .getSpecificPlugin(FoodPlugin.class).getService().getFoodData()); recyclerView.setAdapter(adapter); loadData(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java index bb0487dc53..617c975825 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java @@ -239,11 +239,7 @@ public class FoodService extends OrmLiteBaseService { } public void createFoodFromJsonIfNotExists(Food food) { - try { - this.getDao().createOrUpdate(food); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } + this.createOrUpdateByNS(food); } public void deleteNS(JSONObject json) { @@ -305,35 +301,48 @@ public class FoodService extends OrmLiteBaseService { * @return */ public boolean createOrUpdateByNS(Food food) { - try { - // find by NS _id - if (food._id != null && !food._id.equals("")) { - Food old = this.findByNSId(food._id); + // find by NS _id + if (food._id != null && !food._id.equals("")) { + Food old = this.findByNSId(food._id); - if (old != null) { - if (!old.isEqual(food)) { - this.delete(old); // need to delete/create because date may change too - old.copyFrom(food); - this.getDao().create(old); - log.debug("FOOD: Updating record by _id: " + old.toString()); - this.scheduleFoodChange(); - return true; - } else { - return false; - } + if (old != null) { + if (!old.isEqual(food)) { + this.delete(old); // need to delete/create because date may change too + old.copyFrom(food); + this.create(old); + return true; + } else { + return false; } - } else { - this.getDao().createOrUpdate(food); - log.debug("FOOD: New record: " + food.toString()); - this.scheduleFoodChange(); - return true; } - } catch (SQLException e) { - log.error("Unhandled exception", e); + } else { + this.createOrUpdate(food); + return true; } + return false; } + public void createOrUpdate(Food food) { + try { + this.getDao().createOrUpdate(food); + log.debug("FOOD: Created or Updated: " + food.toString()); + } catch (SQLException e) { + log.error("Unable to createOrUpdate Food", e); + } + this.scheduleFoodChange(); + } + + public void create(Food food) { + try { + this.getDao().create(food); + log.debug("FOOD: New record: " + food.toString()); + } catch (SQLException e) { + log.error("Unable to create Food", e); + } + this.scheduleFoodChange(); + } + /** * finds food by its NS Id. * From 38999d6598df48cbe6bd99c4c064a4c58e9c0ce5 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Mar 2018 10:53:23 +0100 Subject: [PATCH 9/9] food code cleanup --- .../androidaps/plugins/Food/Food.java | 18 +++++++++++ .../androidaps/plugins/Food/FoodService.java | 24 +++++++-------- .../services/NSClientService.java | 30 +++++-------------- app/src/main/res/layout/food_fragment.xml | 4 +-- app/src/main/res/values-cs/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 43 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java index 9b38dbcbe9..5506d32471 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/Food.java @@ -128,4 +128,22 @@ public class Food { units = other.units; gi = other.gi; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("_id=" + _id + ";"); + sb.append("isValid=" + isValid + ";"); + sb.append("name=" + name + ";"); + sb.append("category=" + category + ";"); + sb.append("subcategory=" + subcategory + ";"); + sb.append("portion=" + portion + ";"); + sb.append("carbs=" + carbs + ";"); + sb.append("protein=" + protein + ";"); + sb.append("energy=" + energy + ";"); + sb.append("units=" + units + ";"); + sb.append("gi=" + gi + ";"); + + return sb.toString(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java index 617c975825..c3028461ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodService.java @@ -53,7 +53,7 @@ public class FoodService extends OrmLiteBaseService { /** * This method is a simple re-implementation of the database create and up/downgrade functionality * in SQLiteOpenHelper#getDatabaseLocked method. - * + *

* It is implemented to be able to late initialize separate plugins of the application. */ protected void dbInitialize() { @@ -145,10 +145,10 @@ public class FoodService extends OrmLiteBaseService { /** * 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). * @@ -227,8 +227,7 @@ public class FoodService extends OrmLiteBaseService { public void createFoodFromJsonIfNotExists(JSONArray array) { try { - for(int n = 0; n < array.length(); n++) - { + for (int n = 0; n < array.length(); n++) { JSONObject json = array.getJSONObject(n); Food food = Food.createFromJson(json); this.createFoodFromJsonIfNotExists(food); @@ -252,9 +251,8 @@ public class FoodService extends OrmLiteBaseService { } public void deleteNS(JSONArray array) { - try { - for(int n = 0; n < array.length(); n++) - { + try { + for (int n = 0; n < array.length(); n++) { JSONObject json = array.getJSONObject(n); this.deleteNS(json); } @@ -265,7 +263,7 @@ public class FoodService extends OrmLiteBaseService { /** * deletes an entry by its NS Id. - * + *

* Basically a convenience method for findByNSId and delete. * * @param _id @@ -280,7 +278,7 @@ public class FoodService extends OrmLiteBaseService { /** * deletes the food and sends the foodChange Event - * + *

* should be moved ot a Service * * @param food @@ -314,10 +312,10 @@ public class FoodService extends OrmLiteBaseService { } else { return false; } + } else { + this.createOrUpdate(food); + return true; } - } else { - this.createOrUpdate(food); - return true; } return false; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java index 1ef71c2f75..fa96fdc967 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java @@ -572,22 +572,20 @@ public class NSClientService extends Service { MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); for (Integer index = 0; index < foods.length(); index++) { JSONObject jsonFood = foods.getJSONObject(index); - NSTreatment treatment = new NSTreatment(jsonFood); // remove from upload queue if Ack is failing UploadQueue.removeID(jsonFood); - //Find latest date in treatment - if (treatment.getMills() != null && treatment.getMills() < System.currentTimeMillis()) - if (treatment.getMills() > latestDateInReceivedData) - latestDateInReceivedData = treatment.getMills(); - if (treatment.getAction() == null) { + String action = null; + if (jsonFood.has("action")) + action = jsonFood.getString("action"); + + if (action == null) { addedFoods.put(jsonFood); - } else if (treatment.getAction().equals("update")) { + } else if (action.equals("update")) { updatedFoods.put(jsonFood); - } else if (treatment.getAction().equals("remove")) { - if (treatment.getMills() != null && treatment.getMills() > System.currentTimeMillis() - 24 * 60 * 60 * 1000L) // handle 1 day old deletions only - removedFoods.put(jsonFood); + } else if (action.equals("remove")) { + removedFoods.put(jsonFood); } } if (removedFoods.length() > 0) { @@ -600,18 +598,6 @@ public class NSClientService extends Service { BroadcastFood.handleNewFood(addedFoods, MainApp.instance().getApplicationContext(), isDelta); } } - if (data.has("")) { - JSONArray foods = data.getJSONArray("food"); - if (foods.length() > 0) { - MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); - for (Integer index = 0; index < foods.length(); index++) { - JSONObject jsonFood = foods.getJSONObject(index); - // remove from upload queue if Ack is failing - UploadQueue.removeID(jsonFood); - } - BroadcastDeviceStatus.handleNewFoods(foods, MainApp.instance().getApplicationContext(), isDelta); - } - } if (data.has("mbgs")) { JSONArray mbgs = data.getJSONArray("mbgs"); if (mbgs.length() > 0) diff --git a/app/src/main/res/layout/food_fragment.xml b/app/src/main/res/layout/food_fragment.xml index 7b42eff11f..f204677968 100644 --- a/app/src/main/res/layout/food_fragment.xml +++ b/app/src/main/res/layout/food_fragment.xml @@ -50,7 +50,7 @@ + android:text="@string/category" /> + android:text="@string/subcategory" /> Opravdu chcete přečíst historii z pumpy a nést důsledky z toho vyplývající? Nedostatek inzulínu pro takovýto bolus Chyba spuštění extended bolusu + Podkategorie + Kategorie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 554b7f759f..4378d9a632 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -861,5 +861,7 @@ Recovering from connection loss Not enough insulin for bolus left in reservoir Extended bolus delivery error + Category + Subcategory