food -> Room
This commit is contained in:
parent
3d4d27439a
commit
a2fbab02f9
34 changed files with 3437 additions and 643 deletions
|
@ -109,6 +109,7 @@ class MainApp : DaggerApplication() {
|
|||
filter.addAction(Intents.ACTION_NEW_PROFILE)
|
||||
filter.addAction(Intents.ACTION_NEW_MBG)
|
||||
filter.addAction(Intents.ACTION_NEW_CAL)
|
||||
filter.addAction(Intents.ACTION_FOOD)
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(DataReceiver(), filter)
|
||||
filter = IntentFilter()
|
||||
filter.addAction(Intent.ACTION_TIME_CHANGED)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package info.nightscout.androidaps.db
|
||||
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
import info.nightscout.androidaps.events.EventFoodDatabaseChanged
|
||||
import info.nightscout.androidaps.events.EventNewBG
|
||||
import info.nightscout.androidaps.events.EventTempTargetChange
|
||||
import info.nightscout.androidaps.events.EventTherapyEventChange
|
||||
|
@ -44,5 +46,9 @@ class CompatDBHelper @Inject constructor(
|
|||
aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange")
|
||||
rxBus.send(EventTherapyEventChange())
|
||||
}
|
||||
it.filterIsInstance<Food>().firstOrNull()?.let {
|
||||
aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged")
|
||||
rxBus.send(EventFoodDatabaseChanged())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package info.nightscout.androidaps.dependencyInjection
|
|||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import info.nightscout.androidaps.db.DatabaseHelper
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodService
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentService
|
||||
import info.nightscout.androidaps.utils.wizard.BolusWizard
|
||||
|
@ -17,7 +16,6 @@ abstract class DataClassesModule {
|
|||
|
||||
@ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper
|
||||
@ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService
|
||||
@ContributesAndroidInjector abstract fun foodServiceInjector(): FoodService
|
||||
|
||||
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
|
||||
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
|
||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.dependencyInjection
|
|||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
|
||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
|
||||
|
@ -22,4 +23,5 @@ abstract class WorkersModule {
|
|||
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker
|
||||
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker
|
||||
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientWorker
|
||||
@ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package info.nightscout.androidaps.events
|
||||
|
||||
import org.json.JSONArray
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class EventNsFood(val mode: Int, val foods: JSONArray) : Event() {
|
||||
|
||||
companion object {
|
||||
val ADD = 0
|
||||
val UPDATE = 1
|
||||
val REMOVE = 2
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.food;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import info.nightscout.androidaps.utils.JsonHelper;
|
||||
|
||||
/**
|
||||
* Created by mike on 20.09.2017.
|
||||
*/
|
||||
|
||||
@DatabaseTable(tableName = Food.TABLE_FOODS)
|
||||
public class Food {
|
||||
static final String TABLE_FOODS = "Foods";
|
||||
|
||||
@DatabaseField(id = true)
|
||||
public long key;
|
||||
|
||||
@DatabaseField
|
||||
public boolean isValid = true;
|
||||
|
||||
@DatabaseField
|
||||
public String _id; // NS _id
|
||||
|
||||
@DatabaseField
|
||||
public String name;
|
||||
|
||||
@DatabaseField
|
||||
public String category;
|
||||
|
||||
@DatabaseField
|
||||
public String subcategory;
|
||||
|
||||
// Example:
|
||||
// name="juice" portion=250 units="ml" carbs=12
|
||||
// means 250ml of juice has 12g of carbs
|
||||
|
||||
@DatabaseField
|
||||
public double portion; // common portion in "units"
|
||||
|
||||
@DatabaseField
|
||||
public int carbs; // in grams
|
||||
|
||||
@DatabaseField
|
||||
public int fat = 0; // in grams
|
||||
|
||||
@DatabaseField
|
||||
public int protein = 0; // in grams
|
||||
|
||||
@DatabaseField
|
||||
public int energy = 0; // in kJ
|
||||
|
||||
@DatabaseField
|
||||
public String units = "g";
|
||||
|
||||
@DatabaseField
|
||||
public int gi; // not used yet
|
||||
|
||||
private 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;
|
||||
if (carbs != other.carbs)
|
||||
return false;
|
||||
if (fat != other.fat)
|
||||
return false;
|
||||
if (protein != other.protein)
|
||||
return false;
|
||||
if (energy != other.energy)
|
||||
return false;
|
||||
if (gi != other.gi)
|
||||
return false;
|
||||
if (!Objects.equals(_id, other._id))
|
||||
return false;
|
||||
if (!Objects.equals(name, other.name))
|
||||
return false;
|
||||
if (!Objects.equals(category, other.category))
|
||||
return false;
|
||||
if (!Objects.equals(subcategory, other.subcategory))
|
||||
return false;
|
||||
return Objects.equals(units, other.units);
|
||||
}
|
||||
|
||||
public void copyFrom(Food other) {
|
||||
isValid = other.isValid;
|
||||
_id = other._id;
|
||||
name = other.name;
|
||||
category = other.category;
|
||||
subcategory = other.subcategory;
|
||||
portion = other.portion;
|
||||
carbs = other.carbs;
|
||||
fat = other.fat;
|
||||
protein = other.protein;
|
||||
energy = other.energy;
|
||||
units = other.units;
|
||||
gi = other.gi;
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.general.food
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Paint
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
|
@ -15,20 +14,30 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dagger.android.support.DaggerFragment
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.*
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.transactions.InvalidateFoodTransaction
|
||||
import info.nightscout.androidaps.databinding.FoodFragmentBinding
|
||||
import info.nightscout.androidaps.databinding.FoodItemBinding
|
||||
import info.nightscout.androidaps.events.EventFoodDatabaseChanged
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodFragment.RecyclerViewAdapter.FoodsViewHolder
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.extensions.toVisibility
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
@ -36,10 +45,11 @@ class FoodFragment : DaggerFragment() {
|
|||
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var foodPlugin: FoodPlugin
|
||||
@Inject lateinit var nsUpload: NSUpload
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var uel: UserEntryLogger
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
@ -62,8 +72,23 @@ class FoodFragment : DaggerFragment() {
|
|||
|
||||
binding.recyclerview.setHasFixedSize(true)
|
||||
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recyclerview.adapter = RecyclerViewAdapter(foodPlugin.service?.foodData
|
||||
?: ArrayList())
|
||||
|
||||
binding.refreshFromNightscout.setOnClickListener {
|
||||
context?.let { context ->
|
||||
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", {
|
||||
uel.log(Action.FOOD_FROM_NS)
|
||||
disposable += Completable.fromAction { repository.deleteAllFoods() }
|
||||
.subscribeOn(aapsSchedulers.io)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribeBy(
|
||||
onError = { aapsLogger.error("Error removing foods", it) },
|
||||
onComplete = { rxBus.send(EventFoodDatabaseChanged()) }
|
||||
)
|
||||
|
||||
rxBus.send(EventNSClientRestart())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
binding.clearfilter.setOnClickListener {
|
||||
binding.filter.setText("")
|
||||
|
@ -99,10 +124,6 @@ class FoodFragment : DaggerFragment() {
|
|||
|
||||
override fun afterTextChanged(s: Editable) {}
|
||||
})
|
||||
loadData()
|
||||
fillCategories()
|
||||
fillSubcategories()
|
||||
filterData()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -111,9 +132,23 @@ class FoodFragment : DaggerFragment() {
|
|||
disposable.add(rxBus
|
||||
.toObservable(EventFoodDatabaseChanged::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ updateGui() }, fabricPrivacy::logException)
|
||||
.debounce(1L, TimeUnit.SECONDS)
|
||||
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
|
||||
)
|
||||
updateGui()
|
||||
swapAdapter()
|
||||
}
|
||||
|
||||
private fun swapAdapter() {
|
||||
disposable += repository
|
||||
.getFoodData()
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe { list ->
|
||||
unfiltered = list
|
||||
fillCategories()
|
||||
fillSubcategories()
|
||||
filterData()
|
||||
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -128,14 +163,11 @@ class FoodFragment : DaggerFragment() {
|
|||
_binding = null
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
unfiltered = foodPlugin.service?.foodData ?: ArrayList()
|
||||
}
|
||||
|
||||
private fun fillCategories() {
|
||||
val catSet: MutableSet<CharSequence> = HashSet()
|
||||
for (f in unfiltered) {
|
||||
if (f.category != null && f.category != "") catSet.add(f.category)
|
||||
val category = f.category
|
||||
if (!category.isNullOrBlank()) catSet.add(category)
|
||||
}
|
||||
// make it unique
|
||||
val categories = ArrayList(catSet)
|
||||
|
@ -151,7 +183,10 @@ class FoodFragment : DaggerFragment() {
|
|||
val subCatSet: MutableSet<CharSequence> = HashSet()
|
||||
if (categoryFilter != resourceHelper.gs(R.string.none)) {
|
||||
for (f in unfiltered) {
|
||||
if (f.category != null && f.category == categoryFilter) if (f.subcategory != null && f.subcategory != "") subCatSet.add(f.subcategory)
|
||||
if (f.category != null && f.category == categoryFilter) {
|
||||
val subCategory = f.subCategory
|
||||
if (!subCategory.isNullOrEmpty()) subCatSet.add(subCategory)
|
||||
}
|
||||
}
|
||||
}
|
||||
// make it unique
|
||||
|
@ -169,21 +204,19 @@ class FoodFragment : DaggerFragment() {
|
|||
val subcategoryFilter = binding.subcategory.selectedItem.toString()
|
||||
val newFiltered = ArrayList<Food>()
|
||||
for (f in unfiltered) {
|
||||
if (f.name == null || f.category == null || f.subcategory == null) continue
|
||||
if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subcategory != subcategoryFilter) continue
|
||||
if (f.category == null || f.subCategory == null) continue
|
||||
if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subCategory != subcategoryFilter) continue
|
||||
if (categoryFilter != resourceHelper.gs(R.string.none) && f.category != categoryFilter) continue
|
||||
if (textFilter != "" && !f.name.toLowerCase(Locale.getDefault()).contains(textFilter.toLowerCase(Locale.getDefault()))) continue
|
||||
newFiltered.add(f)
|
||||
}
|
||||
filtered = newFiltered
|
||||
updateGui()
|
||||
}
|
||||
|
||||
private fun updateGui() {
|
||||
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
|
||||
}
|
||||
|
||||
inner class RecyclerViewAdapter internal constructor(var foodList: List<Food>) : RecyclerView.Adapter<FoodsViewHolder>() {
|
||||
fun Int?.isNotZero(): Boolean = this != null && this != 0
|
||||
|
||||
inner class RecyclerViewAdapter internal constructor(private var foodList: List<Food>) : RecyclerView.Adapter<RecyclerViewAdapter.FoodsViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoodsViewHolder {
|
||||
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.food_item, viewGroup, false)
|
||||
|
@ -193,16 +226,16 @@ class FoodFragment : DaggerFragment() {
|
|||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: FoodsViewHolder, position: Int) {
|
||||
val food = foodList[position]
|
||||
holder.binding.nsSign.visibility = if (food._id != null) View.VISIBLE else View.GONE
|
||||
holder.binding.nsSign.visibility = (food.interfaceIDs.nightscoutId != null).toVisibility()
|
||||
holder.binding.name.text = food.name
|
||||
holder.binding.portion.text = food.portion.toString() + food.units
|
||||
holder.binding.portion.text = food.portion.toString() + food.unit
|
||||
holder.binding.carbs.text = food.carbs.toString() + resourceHelper.gs(R.string.shortgramm)
|
||||
holder.binding.fat.text = resourceHelper.gs(R.string.shortfat) + ": " + food.fat + resourceHelper.gs(R.string.shortgramm)
|
||||
if (food.fat == 0) holder.binding.fat.visibility = View.INVISIBLE
|
||||
holder.binding.fat.visibility = food.fat.isNotZero().toVisibility()
|
||||
holder.binding.protein.text = resourceHelper.gs(R.string.shortprotein) + ": " + food.protein + resourceHelper.gs(R.string.shortgramm)
|
||||
if (food.protein == 0) holder.binding.protein.visibility = View.INVISIBLE
|
||||
holder.binding.protein.visibility = food.protein.isNotZero().toVisibility()
|
||||
holder.binding.energy.text = resourceHelper.gs(R.string.shortenergy) + ": " + food.energy + resourceHelper.gs(R.string.shortkilojoul)
|
||||
if (food.energy == 0) holder.binding.energy.visibility = View.INVISIBLE
|
||||
holder.binding.energy.visibility = food.energy.isNotZero().toVisibility()
|
||||
holder.binding.remove.tag = food
|
||||
}
|
||||
|
||||
|
@ -216,12 +249,17 @@ class FoodFragment : DaggerFragment() {
|
|||
binding.remove.setOnClickListener { v: View ->
|
||||
val food = v.tag as Food
|
||||
activity?.let { activity ->
|
||||
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
|
||||
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + "\n" + food.name, {
|
||||
uel.log(Action.FOOD_REMOVED, food.name)
|
||||
if (food._id != null && food._id != "") {
|
||||
nsUpload.removeFoodFromNS(food._id)
|
||||
}
|
||||
foodPlugin.service?.delete(food)
|
||||
disposable += repository.runTransactionForResult(InvalidateFoodTransaction(food.id))
|
||||
.subscribe({
|
||||
val id = food.interfaceIDs.nightscoutId
|
||||
if (NSUpload.isIdValid(id)) nsUpload.removeFoodFromNS(id)
|
||||
// no create at the moment
|
||||
// else uploadQueue.removeID("dbAdd", food.timestamp.toString())
|
||||
}, {
|
||||
aapsLogger.error(LTag.BGSOURCE, "Error while invalidating food", it)
|
||||
})
|
||||
}, null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,28 @@
|
|||
package info.nightscout.androidaps.plugins.general.food
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.database.transactions.SyncFoodTransaction
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.receivers.BundleStore
|
||||
import info.nightscout.androidaps.receivers.DataReceiver
|
||||
import info.nightscout.androidaps.utils.JsonHelper
|
||||
import info.nightscout.androidaps.utils.extensions.foodFromJson
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -25,10 +41,75 @@ class FoodPlugin @Inject constructor(
|
|||
aapsLogger, resourceHelper, injector
|
||||
) {
|
||||
|
||||
var service: FoodService? = null
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
service = FoodService(injector)
|
||||
// cannot be inner class because of needed injection
|
||||
class FoodWorker(
|
||||
context: Context,
|
||||
params: WorkerParameters
|
||||
) : Worker(context, params) {
|
||||
|
||||
@Inject lateinit var injector: HasAndroidInjector
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var bundleStore: BundleStore
|
||||
@Inject lateinit var foodPlugin: FoodPlugin
|
||||
|
||||
init {
|
||||
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
aapsLogger.debug(LTag.DATAFOOD, "Received Food Data: $inputData}")
|
||||
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1))
|
||||
?: return Result.failure()
|
||||
|
||||
var ret = Result.success()
|
||||
|
||||
val foodsString = bundle.getString("foods") ?: return Result.failure()
|
||||
val foods = JSONArray(foodsString)
|
||||
for (index in 0 until foods.length()) {
|
||||
val jsonFood: JSONObject = foods.getJSONObject(index)
|
||||
|
||||
if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue
|
||||
|
||||
when (JsonHelper.safeGetString(jsonFood, "action")) {
|
||||
"remove" -> {
|
||||
val delFood = Food(
|
||||
name = "",
|
||||
portion = 0.0,
|
||||
carbs = 0,
|
||||
isValid = false
|
||||
).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") }
|
||||
|
||||
foodPlugin.disposable += repository.runTransactionForResult(SyncFoodTransaction(delFood)).subscribe({ result ->
|
||||
result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food ${it.interfaceIDs.nightscoutId}") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATAFOOD, "Error while removing food", it)
|
||||
ret = Result.failure()
|
||||
})
|
||||
}
|
||||
|
||||
else -> {
|
||||
val food = foodFromJson(jsonFood)
|
||||
if (food != null) {
|
||||
foodPlugin.disposable += repository.runTransactionForResult(SyncFoodTransaction(food)).subscribe({ result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATAFOOD, "Inserted food $it") }
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Updated food $it") }
|
||||
result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATAFOOD, "Error while adding/updating food", it)
|
||||
ret = Result.failure()
|
||||
})
|
||||
} else {
|
||||
aapsLogger.error(LTag.DATAFOOD, "Error parsing food", jsonFood.toString())
|
||||
ret = Result.failure()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,359 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.food;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.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 org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
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 javax.inject.Inject;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
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;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
/**
|
||||
* Created by mike on 24.09.2017.
|
||||
*/
|
||||
|
||||
public class FoodService extends OrmLiteBaseService<DatabaseHelper> {
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject RxBusWrapper rxBus;
|
||||
@Inject FabricPrivacy fabricPrivacy;
|
||||
@Inject AapsSchedulers aapsSchedulers;
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor();
|
||||
private static ScheduledFuture<?> scheduledFoodEventPost = null;
|
||||
|
||||
public FoodService(HasAndroidInjector injector) {
|
||||
injector.androidInjector().inject(this);
|
||||
onCreate();
|
||||
dbInitialize();
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventNsFood.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(event -> {
|
||||
int mode = event.getMode();
|
||||
JSONArray array = event.getFoods();
|
||||
if (mode == EventNsFood.Companion.getADD() || mode == EventNsFood.Companion.getUPDATE())
|
||||
this.createFoodFromJsonIfNotExists(array);
|
||||
else
|
||||
this.deleteNS(array);
|
||||
}, fabricPrivacy::logException)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
aapsLogger.error("Cannot create Dao for Food.class");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
try {
|
||||
aapsLogger.info(LTag.DATAFOOD, "onCreate");
|
||||
TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Can't create database", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onUpgrade(ConnectionSource connectionSource, int oldVersion, int newVersion) {
|
||||
aapsLogger.info(LTag.DATAFOOD, "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) {
|
||||
aapsLogger.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() {
|
||||
aapsLogger.debug(LTag.DATAFOOD, "Firing EventFoodChange");
|
||||
rxBus.send(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) {
|
||||
aapsLogger.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) {
|
||||
aapsLogger.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) {
|
||||
aapsLogger.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) {
|
||||
aapsLogger.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) {
|
||||
aapsLogger.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) {
|
||||
aapsLogger.debug(LTag.DATAFOOD, "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) {
|
||||
aapsLogger.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);
|
||||
aapsLogger.debug(LTag.DATAFOOD, "Created or Updated: " + food.toString());
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unable to createOrUpdate Food", e);
|
||||
}
|
||||
this.scheduleFoodChange();
|
||||
}
|
||||
|
||||
public void create(Food food) {
|
||||
try {
|
||||
this.getDao().create(food);
|
||||
aapsLogger.debug(LTag.DATAFOOD, "New record: " + food.toString());
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unable to create Food", e);
|
||||
}
|
||||
this.scheduleFoodChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* finds food by its NS Id.
|
||||
*
|
||||
* @param _id
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
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) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -108,6 +108,7 @@ class ImportExportPrefs @Inject constructor(
|
|||
return metadata
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private fun detectUserName(context: Context): String {
|
||||
// based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c
|
||||
val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name")
|
||||
|
@ -346,7 +347,7 @@ class ImportExportPrefs @Inject constructor(
|
|||
|
||||
private fun restartAppAfterImport(context: Context) {
|
||||
sp.putBoolean(R.string.key_setupwizard_processed, true)
|
||||
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
|
||||
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp)) {
|
||||
uel.log(Action.IMPORT_SETTINGS)
|
||||
log.debug(LTag.CORE, "Exiting")
|
||||
rxBus.send(EventAppExit())
|
||||
|
@ -355,14 +356,13 @@ class ImportExportPrefs @Inject constructor(
|
|||
}
|
||||
System.runFinalization()
|
||||
exitProcess(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun exportUserEntriesCsv(activity: FragmentActivity, listEntries: Single<List<UserEntry>>) {
|
||||
val entries = listEntries.blockingGet()
|
||||
override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) {
|
||||
val entries = singleEntries.blockingGet()
|
||||
prefFileList.ensureExportDirExists()
|
||||
val newFile = prefFileList.newExportXmlFile()
|
||||
//log.debug("XXXXX " + classicPrefsFormat.UserEntriesToCsv(entries))
|
||||
|
||||
try {
|
||||
classicPrefsFormat.saveCsv(newFile, entries)
|
||||
|
|
|
@ -16,7 +16,6 @@ import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
|
|||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
|
@ -34,7 +33,6 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
|
||||
@Inject lateinit var foodPlugin: FoodPlugin
|
||||
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var repository: AppRepository
|
||||
|
@ -70,7 +68,6 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
databaseHelper.resetDatabases()
|
||||
// should be handled by Plugin-Interface and
|
||||
// additional service interface and plugin registry
|
||||
foodPlugin.service?.resetFood()
|
||||
treatmentsPlugin.service.resetTreatments()
|
||||
repository.clearDatabases()
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import info.nightscout.androidaps.R;
|
|||
import info.nightscout.androidaps.db.DbRequest;
|
||||
import info.nightscout.androidaps.events.EventAppExit;
|
||||
import info.nightscout.androidaps.events.EventConfigBuilderChange;
|
||||
import info.nightscout.androidaps.events.EventNsFood;
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange;
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
|
@ -636,39 +635,8 @@ public class NSClientService extends DaggerService {
|
|||
}
|
||||
if (data.has("food")) {
|
||||
JSONArray foods = data.getJSONArray("food");
|
||||
JSONArray removedFoods = new JSONArray();
|
||||
JSONArray updatedFoods = new JSONArray();
|
||||
JSONArray addedFoods = new JSONArray();
|
||||
if (foods.length() > 0)
|
||||
rxBus.send(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);
|
||||
|
||||
String action = JsonHelper.safeGetString(jsonFood, "action");
|
||||
|
||||
if (action == null) {
|
||||
addedFoods.put(jsonFood);
|
||||
} else if (action.equals("update")) {
|
||||
updatedFoods.put(jsonFood);
|
||||
} else if (action.equals("remove")) {
|
||||
removedFoods.put(jsonFood);
|
||||
}
|
||||
}
|
||||
if (removedFoods.length() > 0) {
|
||||
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getREMOVE(), removedFoods);
|
||||
rxBus.send(evt);
|
||||
}
|
||||
if (updatedFoods.length() > 0) {
|
||||
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getUPDATE(), updatedFoods);
|
||||
rxBus.send(evt);
|
||||
}
|
||||
if (addedFoods.length() > 0) {
|
||||
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getADD(), addedFoods);
|
||||
rxBus.send(evt);
|
||||
}
|
||||
if (foods.length() > 0) rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods"));
|
||||
handleFood(foods, isDelta);
|
||||
}
|
||||
if (data.has("mbgs")) {
|
||||
JSONArray mbgs = data.getJSONArray("mbgs");
|
||||
|
@ -881,6 +849,16 @@ public class NSClientService extends DaggerService {
|
|||
}
|
||||
}
|
||||
|
||||
public void handleFood(JSONArray foods, boolean isDelta) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("foods", foods.toString());
|
||||
bundle.putBoolean("delta", isDelta);
|
||||
Intent intent = new Intent(Intents.ACTION_FOOD);
|
||||
intent.putExtras(bundle);
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public void handleNewCal(JSONArray cals, boolean isDelta) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("cals", cals.toString());
|
||||
|
|
|
@ -12,6 +12,7 @@ import dagger.android.DaggerBroadcastReceiver
|
|||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.BundleLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
|
||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
|
||||
|
@ -21,7 +22,6 @@ import info.nightscout.androidaps.utils.extensions.copyDouble
|
|||
import info.nightscout.androidaps.utils.extensions.copyInt
|
||||
import info.nightscout.androidaps.utils.extensions.copyLong
|
||||
import info.nightscout.androidaps.utils.extensions.copyString
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
|
||||
open class DataReceiver : DaggerBroadcastReceiver() {
|
||||
|
@ -87,6 +87,9 @@ open class DataReceiver : DaggerBroadcastReceiver() {
|
|||
Intents.DEXCOM_BG ->
|
||||
OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java)
|
||||
.setInputData(bundleInputData(bundle, intent)).build()
|
||||
Intents.ACTION_FOOD ->
|
||||
OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker::class.java)
|
||||
.setInputData(bundleInputData(bundle, intent)).build()
|
||||
Intents.ACTION_NEW_TREATMENT,
|
||||
Intents.ACTION_CHANGED_TREATMENT,
|
||||
Intents.ACTION_REMOVED_TREATMENT,
|
||||
|
@ -105,6 +108,7 @@ open class DataReceiver : DaggerBroadcastReceiver() {
|
|||
Data.Builder().putLong(STORE_KEY, bundleStore.store(bundle)).putString(ACTION_KEY, intent.action).build()
|
||||
|
||||
companion object {
|
||||
|
||||
const val STORE_KEY = "storeKey"
|
||||
const val ACTION_KEY = "action"
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ interface Intents {
|
|||
const val ACTION_NEW_SGV = "info.nightscout.client.NEW_SGV"
|
||||
const val ACTION_NEW_MBG = "info.nightscout.client.NEW_MBG"
|
||||
const val ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL"
|
||||
const val ACTION_FOOD = "info.nightscout.client.FOOD"
|
||||
|
||||
// xDrip -> App
|
||||
const val ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate"
|
||||
|
|
|
@ -4,7 +4,17 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsExtendedBolusesFragment">
|
||||
tools:context="info.nightscout.androidaps.plugins.general.food.TreatmentsFoodFragment">
|
||||
|
||||
<info.nightscout.androidaps.utils.ui.SingleClickButton
|
||||
android:id="@+id/refresh_from_nightscout"
|
||||
style="?android:attr/buttonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:drawableStart="@drawable/ic_refresh"
|
||||
android:text="@string/refresheventsfromnightscout"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -48,6 +58,7 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:text="@string/category" />
|
||||
|
||||
<Spinner
|
||||
|
@ -65,6 +76,7 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:text="@string/subcategory" />
|
||||
|
||||
<Spinner
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
card_view:cardBackgroundColor="@color/cardColorBackground"
|
||||
card_view:cardCornerRadius="6dp"
|
||||
card_view:cardUseCompatPadding="true"
|
||||
card_view:contentPadding="6dp">
|
||||
card_view:cardBackgroundColor="?android:colorBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -26,21 +24,24 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Name"
|
||||
android:textStyle="bold" />
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/portion"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="Portion" />
|
||||
android:text="Portion"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/carbs"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="Carbs" />
|
||||
android:text="Carbs"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -56,21 +57,24 @@
|
|||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="Fat" />
|
||||
android:text="Fat"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/protein"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="Protein" />
|
||||
android:text="Protein"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/energy"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="Energy" />
|
||||
android:text="Energy"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ns_sign"
|
||||
|
@ -79,7 +83,8 @@
|
|||
android:width="30dp"
|
||||
android:text="NS"
|
||||
android:textAlignment="viewEnd"
|
||||
android:textColor="@color/colorSetTempButton" />
|
||||
android:textColor="@color/colorSetTempButton"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remove"
|
||||
|
@ -93,6 +98,15 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@color/list_delimiter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
|
|
@ -317,7 +317,7 @@
|
|||
<string name="openapsma_autosensdata_label">Autosens data</string>
|
||||
<string name="openapsma_scriptdebugdata_label">Script debug</string>
|
||||
<string name="openapsama_useautosens">Use Autosens feature</string>
|
||||
<string name="refresheventsfromnightscout">Refresh events from NS</string>
|
||||
<string name="refresheventsfromnightscout">Refresh from NS</string>
|
||||
<string name="deletefuturetreatments">Delete treatments in the future</string>
|
||||
<string name="actions_shortname">ACT</string>
|
||||
<string name="configbuilder_shortname">CONF</string>
|
||||
|
|
|
@ -14,5 +14,5 @@ interface ImportExportPrefsInterface {
|
|||
fun prefsFileExists(): Boolean
|
||||
fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable)
|
||||
fun exportSharedPreferences(f: Fragment)
|
||||
fun exportUserEntriesCsv(activity: FragmentActivity, entries: Single<List<UserEntry>>)
|
||||
fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>)
|
||||
}
|
|
@ -5,7 +5,6 @@ import org.json.JSONObject
|
|||
|
||||
object JsonHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetObject(json: JSONObject?, fieldName: String, defaultValue: Any): Any {
|
||||
var result = defaultValue
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -17,7 +16,6 @@ object JsonHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetJSONObject(json: JSONObject?, fieldName: String, defaultValue: JSONObject?): JSONObject? {
|
||||
var result = defaultValue
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -53,7 +51,6 @@ object JsonHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetStringAllowNull(json: JSONObject?, fieldName: String, defaultValue: String?): String? {
|
||||
var result = defaultValue
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -77,7 +74,6 @@ object JsonHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetDoubleAllowNull(json: JSONObject?, fieldName: String): Double? {
|
||||
var result: Double? = null
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -89,7 +85,6 @@ object JsonHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetDouble(json: JSONObject?, fieldName: String, defaultValue: Double): Double {
|
||||
var result = defaultValue
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -105,7 +100,6 @@ object JsonHelper {
|
|||
fun safeGetInt(json: JSONObject?, fieldName: String): Int =
|
||||
safeGetInt(json, fieldName, 0)
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetInt(json: JSONObject?, fieldName: String, defaultValue: Int): Int {
|
||||
var result = defaultValue
|
||||
if (json != null && json.has(fieldName)) {
|
||||
|
@ -117,6 +111,17 @@ object JsonHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
fun safeGetIntAllowNull(json: JSONObject?, fieldName: String): Int? {
|
||||
var result : Int? = null
|
||||
if (json != null && json.has(fieldName)) {
|
||||
try {
|
||||
result = json.getInt(fieldName)
|
||||
} catch (ignored: JSONException) {
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun safeGetLong(json: JSONObject?, fieldName: String): Long {
|
||||
var result: Long = 0
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package info.nightscout.androidaps.utils.extensions
|
||||
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
|
||||
import info.nightscout.androidaps.utils.JsonHelper
|
||||
import org.json.JSONObject
|
||||
|
||||
fun foodFromJson(jsonObject: JSONObject): Food? {
|
||||
if ("food" == JsonHelper.safeGetString(jsonObject, "type")) {
|
||||
val name = JsonHelper.safeGetStringAllowNull(jsonObject, "name", null) ?: return null
|
||||
val category = JsonHelper.safeGetStringAllowNull(jsonObject, "category", null)
|
||||
val subCategory = JsonHelper.safeGetStringAllowNull(jsonObject, "subcategory", null)
|
||||
val unit = JsonHelper.safeGetString(jsonObject, "unit", "")
|
||||
val portion = JsonHelper.safeGetDoubleAllowNull(jsonObject, "portion") ?: return null
|
||||
val carbs = JsonHelper.safeGetIntAllowNull(jsonObject, "carbs") ?: return null
|
||||
val gi = JsonHelper.safeGetIntAllowNull(jsonObject, "gi")
|
||||
val energy = JsonHelper.safeGetIntAllowNull(jsonObject, "energy")
|
||||
val protein = JsonHelper.safeGetIntAllowNull(jsonObject, "protein")
|
||||
val fat = JsonHelper.safeGetIntAllowNull(jsonObject, "fat")
|
||||
val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null
|
||||
val isValid = JsonHelper.safeGetBoolean(jsonObject, NSUpload.ISVALID, true)
|
||||
|
||||
val food = Food(
|
||||
name = name,
|
||||
category = category,
|
||||
subCategory = subCategory,
|
||||
unit = unit,
|
||||
portion = portion,
|
||||
carbs = carbs,
|
||||
gi = gi,
|
||||
energy = energy,
|
||||
protein = protein,
|
||||
fat = fat,
|
||||
isValid = isValid
|
||||
)
|
||||
food.interfaceIDs.nightscoutId = id
|
||||
return food
|
||||
}
|
||||
return null
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -6,13 +6,14 @@ import androidx.room.TypeConverters
|
|||
import info.nightscout.androidaps.database.daos.*
|
||||
import info.nightscout.androidaps.database.entities.*
|
||||
|
||||
const val DATABASE_VERSION = 6
|
||||
const val DATABASE_VERSION = 7
|
||||
|
||||
@Database(version = DATABASE_VERSION,
|
||||
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
|
||||
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
|
||||
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
|
||||
MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class],
|
||||
MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
|
||||
Food::class],
|
||||
exportSchema = true)
|
||||
@TypeConverters(Converters::class)
|
||||
internal abstract class AppDatabase : RoomDatabase() {
|
||||
|
@ -53,4 +54,6 @@ internal abstract class AppDatabase : RoomDatabase() {
|
|||
|
||||
abstract val preferenceChangeDao: PreferenceChangeDao
|
||||
|
||||
abstract val foodDao: FoodDao
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.database
|
||||
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
|
@ -170,6 +171,14 @@ class AppRepository @Inject internal constructor(
|
|||
database.therapyEventDao.compatGetTherapyEventDataFromToTime(from, to)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
// FOOD
|
||||
fun getFoodData(): Single<List<Food>> =
|
||||
database.foodDao.getFoodData()
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
fun deleteAllFoods() =
|
||||
database.foodDao.deleteAllEntries()
|
||||
|
||||
}
|
||||
|
||||
@Suppress("USELESS_CAST")
|
||||
|
|
|
@ -34,4 +34,10 @@ open class DatabaseModule {
|
|||
database.execSQL("CREATE TABLE userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `s` TEXT NOT NULL, `values` TEXT NOT NULL)")
|
||||
}
|
||||
}
|
||||
|
||||
private val migration6to7 = object : Migration(6, 7) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS foods (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,5 +24,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
|
|||
val versionChangeDao: VersionChangeDao = DelegatedVersionChangeDao(changes, database.versionChangeDao)
|
||||
val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao)
|
||||
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
|
||||
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
|
||||
fun clearAllTables() = database.clearAllTables()
|
||||
}
|
|
@ -8,6 +8,7 @@ const val TABLE_CARBS = "carbs"
|
|||
const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
|
||||
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
|
||||
const val TABLE_GLUCOSE_VALUES = "glucoseValues"
|
||||
const val TABLE_FOODS = "foods"
|
||||
const val TABLE_MEAL_LINKS = "mealLinks"
|
||||
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
|
||||
const val TABLE_PROFILE_SWITCHES = "profileSwitches"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package info.nightscout.androidaps.database.daos
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import info.nightscout.androidaps.database.TABLE_FOODS
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import io.reactivex.Single
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Dao
|
||||
internal interface FoodDao : TraceableDao<Food> {
|
||||
|
||||
@Query("SELECT * FROM $TABLE_FOODS WHERE id = :id")
|
||||
override fun findById(id: Long): Food?
|
||||
|
||||
@Query("DELETE FROM $TABLE_FOODS")
|
||||
override fun deleteAllEntries()
|
||||
|
||||
@Query("SELECT * FROM $TABLE_FOODS WHERE nightscoutId = :nsId AND referenceId IS NULL")
|
||||
fun findByNSId(nsId: String): Food?
|
||||
|
||||
@Query("SELECT * FROM $TABLE_FOODS WHERE isValid = 1 AND referenceId IS NULL ORDER BY id DESC")
|
||||
fun getFoodData(): Single<List<Food>>
|
||||
|
||||
}
|
|
@ -4,7 +4,6 @@ import androidx.room.Insert
|
|||
import androidx.room.Update
|
||||
import info.nightscout.androidaps.database.daos.workaround.TraceableDaoWorkaround
|
||||
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
|
||||
import io.reactivex.Single
|
||||
|
||||
internal interface TraceableDao<T : TraceableDBEntry> : TraceableDaoWorkaround<T> {
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package info.nightscout.androidaps.database.daos.delegated
|
||||
|
||||
import info.nightscout.androidaps.database.daos.FoodDao
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
import info.nightscout.androidaps.database.interfaces.DBEntry
|
||||
|
||||
internal class DelegatedFoodDao(changes: MutableList<DBEntry>, private val dao: FoodDao) : DelegatedDao(changes), FoodDao by dao {
|
||||
|
||||
override fun insertNewEntry(food: Food): Long {
|
||||
changes.add(food)
|
||||
return dao.insertNewEntry(food)
|
||||
}
|
||||
|
||||
override fun updateExistingEntry(food: Food): Long {
|
||||
changes.add(food)
|
||||
return dao.updateExistingEntry(food)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package info.nightscout.androidaps.database.entities
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import info.nightscout.androidaps.database.TABLE_FOODS
|
||||
import info.nightscout.androidaps.database.embedments.InterfaceIDs
|
||||
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
|
||||
|
||||
@Entity(tableName = TABLE_FOODS,
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = Food::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["referenceId"])],
|
||||
indices = [Index("referenceId")])
|
||||
data class Food(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
override var id: Long = 0,
|
||||
override var version: Int = 0,
|
||||
override var dateCreated: Long = -1,
|
||||
override var isValid: Boolean = true,
|
||||
override var referenceId: Long? = null,
|
||||
@Embedded
|
||||
override var interfaceIDs_backing: InterfaceIDs? = null,
|
||||
var name: String,
|
||||
var category: String? = null,
|
||||
var subCategory: String? = null,
|
||||
// Example:
|
||||
// name="juice" portion=250 units="ml" carbs=12
|
||||
// means 250ml of juice has 12g of carbs
|
||||
|
||||
var portion: Double, // common portion in "units"
|
||||
var carbs: Int, // in grams
|
||||
var fat: Int? = null, // in grams
|
||||
var protein: Int? = null, // in grams
|
||||
var energy: Int? = null, // in kJ
|
||||
var unit: String = "g",
|
||||
var gi: Int? = null // not used yet
|
||||
|
||||
) : TraceableDBEntry {
|
||||
|
||||
fun isEqual(other: Food): Boolean {
|
||||
if (isValid != other.isValid) return false
|
||||
if (portion != other.portion) return false
|
||||
if (carbs != other.carbs) return false
|
||||
if (fat != other.fat) return false
|
||||
if (protein != other.protein) return false
|
||||
if (energy != other.energy) return false
|
||||
if (gi != other.gi) return false
|
||||
if (name != other.name) return false
|
||||
if (category != other.category) return false
|
||||
if (subCategory != other.subCategory) return false
|
||||
return unit == other.unit
|
||||
}
|
||||
|
||||
fun copyFrom(other: Food) {
|
||||
isValid = other.isValid
|
||||
name = other.name
|
||||
category = other.category
|
||||
subCategory = other.subCategory
|
||||
portion = other.portion
|
||||
carbs = other.carbs
|
||||
fat = other.fat
|
||||
protein = other.protein
|
||||
energy = other.energy
|
||||
unit = other.unit
|
||||
gi = other.gi
|
||||
interfaceIDs.nightscoutId = other.interfaceIDs.nightscoutId
|
||||
}
|
||||
|
||||
}
|
|
@ -103,6 +103,7 @@ data class UserEntry(
|
|||
@SerializedName("TT_DELETED_FROM_NS") TT_DELETED_FROM_NS (ColorGroup.TT),
|
||||
@SerializedName("CAREPORTAL_DELETED_FROM_NS") CAREPORTAL_DELETED_FROM_NS (ColorGroup.Careportal),
|
||||
@SerializedName("CAREPORTAL_FROM_NS") CAREPORTAL_FROM_NS (ColorGroup.Careportal),
|
||||
@SerializedName("FOOD_FROM_NS") FOOD_FROM_NS (ColorGroup.Careportal),
|
||||
@SerializedName("TT_FROM_NS") TT_FROM_NS (ColorGroup.TT),
|
||||
@SerializedName("TT_CANCELED_FROM_NS") TT_CANCELED_FROM_NS (ColorGroup.TT),
|
||||
@SerializedName("EXPORT_CSV") EXPORT_CSV (ColorGroup.Aaps),
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package info.nightscout.androidaps.database.transactions
|
||||
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
|
||||
/**
|
||||
* Inserts or updates the Food
|
||||
*/
|
||||
class InsertOrUpdateFoodTransaction(private val food: Food) : Transaction<InsertOrUpdateFoodTransaction.TransactionResult>() {
|
||||
|
||||
override fun run(): TransactionResult {
|
||||
val result = TransactionResult()
|
||||
val current = database.foodDao.findById(food.id)
|
||||
if (current == null) {
|
||||
database.foodDao.insertNewEntry(food)
|
||||
result.inserted.add(food)
|
||||
} else {
|
||||
database.foodDao.updateExistingEntry(food)
|
||||
result.updated.add(food)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class TransactionResult {
|
||||
|
||||
val inserted = mutableListOf<Food>()
|
||||
val updated = mutableListOf<Food>()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.database.transactions
|
||||
|
||||
class InvalidateFoodTransaction(val id: Long) : Transaction<Unit>() {
|
||||
|
||||
override fun run() {
|
||||
val food = database.foodDao.findById(id)
|
||||
?: throw IllegalArgumentException("There is no such Food with the specified ID.")
|
||||
food.isValid = false
|
||||
database.foodDao.updateExistingEntry(food)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package info.nightscout.androidaps.database.transactions
|
||||
|
||||
import info.nightscout.androidaps.database.entities.Food
|
||||
|
||||
/**
|
||||
* Sync the TherapyEvents from NS
|
||||
*/
|
||||
class SyncFoodTransaction(private val food: Food) : Transaction<SyncFoodTransaction.TransactionResult>() {
|
||||
|
||||
override fun run(): TransactionResult {
|
||||
val result = TransactionResult()
|
||||
|
||||
val current: Food? =
|
||||
food.interfaceIDs.nightscoutId?.let {
|
||||
database.foodDao.findByNSId(it)
|
||||
}
|
||||
|
||||
if (current != null) {
|
||||
// nsId exists, update if different
|
||||
if (!current.isEqual(food)) {
|
||||
current.copyFrom(food)
|
||||
database.foodDao.updateExistingEntry(current)
|
||||
if (food.isValid && current.isValid) result.updated.add(current)
|
||||
else if (!food.isValid && current.isValid) result.invalidated.add(current)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// not known nsId, add
|
||||
database.foodDao.insertNewEntry(food)
|
||||
result.inserted.add(food)
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
class TransactionResult {
|
||||
|
||||
val updated = mutableListOf<Food>()
|
||||
val inserted = mutableListOf<Food>()
|
||||
val invalidated = mutableListOf<Food>()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue