Merge branch 'foodrefactor' into dev

This commit is contained in:
Milos Kozak 2018-03-12 10:56:14 +01:00
commit a5830cc452
15 changed files with 582 additions and 299 deletions

View file

@ -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;
@ -371,6 +372,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()

View file

@ -19,6 +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.EventNsFood;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus;
@ -474,57 +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()) ? EventNsFood.ADD : EventNsFood.UPDATE;
EventNsFood evt = new EventNsFood(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);
}
EventNsFood evt = new EventNsFood(EventNsFood.REMOVE, bundles);
MainApp.bus().post(evt);
}
}
private void handleRemovedFoodRecord(String _id) {
MainApp.getDbHelper().foodHelper.deleteFoodById(_id);
}
public void handleAddChangeFoodRecord(JSONObject trJson) throws JSONException {
MainApp.getDbHelper().foodHelper.createFoodFromJsonIfNotExists(trJson);
}
private void handleRemovedRecordFromNS(String _id) {
MainApp.getDbHelper().deleteTreatmentById(_id);
MainApp.getDbHelper().deleteTempTargetById(_id);

View file

@ -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;
@ -35,7 +36,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 +55,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);
@ -68,7 +76,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,7 +102,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledProfileSwitchEventPost = null;
public FoodHelper foodHelper = new FoodHelper(this);
private int oldVersion = 0;
private int newVersion = 0;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@ -116,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);
@ -126,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);
@ -141,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) {
@ -150,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.
*/
@ -219,7 +236,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class);
TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class);
TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class);
foodHelper.resetFood();
updateEarliestDataChange(0);
} catch (SQLException e) {
log.error("Unhandled exception", e);
@ -232,7 +248,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
scheduleTemporaryTargetChange();
scheduleCareportalEventChange();
scheduleProfileSwitchChange();
foodHelper.scheduleFoodChange();
new java.util.Timer().schedule(
new java.util.TimerTask() {
@Override
@ -308,6 +323,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
scheduleProfileSwitchChange();
}
// ------------------ getDao -------------------------------------------
private Dao<TempTarget, Long> getDaoTempTargets() throws SQLException {

View file

@ -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<Food, Long> 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<Food> getFoodData() {
try {
Dao<Food, Long> daoFood = getDaoFood();
List<Food> foods;
QueryBuilder<Food, Long> queryBuilder = daoFood.queryBuilder();
PreparedQuery<Food> 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<Food, Long> queryBuilder = getDaoFood().queryBuilder();
Where where = queryBuilder.where();
where.eq("_id", food._id);
PreparedQuery<Food> preparedQuery = queryBuilder.prepare();
List<Food> 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<Food, Long> queryBuilder = getDaoFood().queryBuilder();
Where where = queryBuilder.where();
where.eq("_id", _id);
PreparedQuery<Food> preparedQuery = queryBuilder.prepare();
List<Food> 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;
}
}

View file

@ -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();
}

View file

@ -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 EventNsFood 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 EventNsFood(int mode, Bundle payload) {
this.mode = mode;
this.payload = payload;
}
public int getMode() {
return mode;
}
public Bundle getPayload() {
return payload;
}
}

View file

@ -1,22 +1,27 @@
package info.nightscout.androidaps.db;
package info.nightscout.androidaps.plugins.Food;
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;
@ -104,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();
}
}

View file

@ -26,10 +26,10 @@ 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;
import info.nightscout.androidaps.db.Food;
import info.nightscout.androidaps.events.EventFoodDatabaseChanged;
import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
import info.nightscout.utils.FabricPrivacy;
@ -121,7 +121,8 @@ public class FoodFragment extends SubscriberFragment {
}
});
RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().foodHelper.getFoodData());
RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp
.getSpecificPlugin(FoodPlugin.class).getService().getFoodData());
recyclerView.setAdapter(adapter);
loadData();
@ -144,20 +145,19 @@ public class FoodFragment extends SubscriberFragment {
}
void loadData() {
unfiltered = MainApp.getDbHelper().foodHelper.getFoodData();
unfiltered = MainApp.getSpecificPlugin(FoodPlugin.class).getService().getFoodData();
}
void fillCategories() {
categories = new ArrayList<>();
Set<CharSequence> 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<CharSequence> adapterCategories = new ArrayAdapter<>(getContext(),
@ -167,19 +167,19 @@ public class FoodFragment extends SubscriberFragment {
void fillSubcategories() {
String categoryFilter = category.getSelectedItem().toString();
subcategories = new ArrayList<>();
Set<CharSequence> 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<CharSequence> adapterSubcategories = new ArrayAdapter<>(getContext(),
@ -299,7 +299,7 @@ public class FoodFragment extends SubscriberFragment {
if (_id != null && !_id.equals("")) {
NSUpload.removeFoodFromNS(_id);
}
MainApp.getDbHelper().foodHelper.delete(food);
MainApp.getSpecificPlugin(FoodPlugin.class).getService().delete(food);
}
});
builder.setNegativeButton(MainApp.sResources.getString(R.string.cancel), null);

View file

@ -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;
}
}

View file

@ -0,0 +1,368 @@
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.EventNsFood;
/**
* Created by mike on 24.09.2017.
*/
public class FoodService extends OrmLiteBaseService<DatabaseHelper> {
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.
* <p>
* 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<Food, Long> 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(EventNsFood event) {
int mode = event.getMode();
Bundle payload = event.getPayload();
try {
if (payload.containsKey("food")) {
JSONObject json = new JSONObject(payload.getString("food"));
if (mode == EventNsFood.ADD || mode == EventNsFood.UPDATE) {
this.createFoodFromJsonIfNotExists(json);
} else {
this.deleteNS(json);
}
}
if (payload.containsKey("foods")) {
JSONArray array = new JSONArray(payload.getString("foods"));
if (mode == EventNsFood.ADD || mode == EventNsFood.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.
* <p>
* We do need to make sure, that ICallback is extended to be able to handle multiple
* events, or handle a list of events.
* <p>
* 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<Food> 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) {
this.createOrUpdateByNS(food);
}
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.
* <p>
* 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
* <p>
* 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) {
// 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.create(old);
return true;
} else {
return false;
}
} 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.
*
* @param _id
* @return
*/
public Food findByNSId(String _id) {
try {
List<Food> 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;
}
}

View file

@ -573,22 +573,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) {
@ -601,18 +599,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)

View file

@ -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;
}
}

View file

@ -50,7 +50,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Category" />
android:text="@string/category" />
<Spinner
android:id="@+id/food_category"
@ -67,7 +67,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Subcategory" />
android:text="@string/subcategory" />
<Spinner
android:id="@+id/food_subcategory"

View file

@ -869,4 +869,6 @@
<string name="temptargetshort">DoCíl</string>
<string name="insight_min">min</string>
<string name="don_t_bolus_record_only">Nepouštět bolus, jen zaznamenat</string>
<string name="subcategory">Podkategorie</string>
<string name="category">Kategorie</string>
</resources>

View file

@ -976,5 +976,7 @@
<string name="start_eating_soon_tt">Start Eating soon TT</string>
<string name="temptargetshort">TT</string>
<string name="don_t_bolus_record_only">Don\'t bolus, record only</string>
<string name="category">Category</string>
<string name="subcategory">Subcategory</string>
</resources>