1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-22 19:02:16 +00:00

feat: Add support for Food input

This commit is contained in:
Florian Bouillon 2023-01-07 22:11:20 +01:00
parent 4bcae95d52
commit 643861bf1e
Signed by: Florian Bouillon
GPG Key ID: BEEAF3722D0EBF64
31 changed files with 1194 additions and 81 deletions

View File

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "794ed5ee15db239f9a2708b951f55552",
"identityHash": "0f92ae44f4503b964d4986959a15ef4e",
"entities": [
{
"tableName": "Weight",
@ -150,7 +150,7 @@
},
{
"tableName": "Food",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `quantity` INTEGER NOT NULL, `proteins` REAL NOT NULL, `carbohydrates` REAL NOT NULL, `fat` REAL NOT NULL, `energy` REAL NOT NULL, `timestamp` INTEGER NOT NULL)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `serving` TEXT NOT NULL, `quantity` INTEGER NOT NULL, `proteins` REAL NOT NULL, `carbohydrates` REAL NOT NULL, `fat` REAL NOT NULL, `energy` REAL NOT NULL, `timestamp` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
@ -164,6 +164,12 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serving",
"columnName": "serving",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
@ -214,7 +220,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '794ed5ee15db239f9a2708b951f55552')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0f92ae44f4503b964d4986959a15ef4e')"
]
}
}

View File

@ -0,0 +1,232 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "414712cc283c7f1d14cde8e00da277fb",
"entities": [
{
"tableName": "Weight",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `weight` REAL NOT NULL, `timestamp` INTEGER NOT NULL, `source` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "weight",
"columnName": "weight",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_Weight_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Weight_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
}
],
"foreignKeys": []
},
{
"tableName": "Water",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `value` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `source` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_Water_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Water_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
}
],
"foreignKeys": []
},
{
"tableName": "Step",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `value` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `source` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_Step_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Step_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
}
],
"foreignKeys": []
},
{
"tableName": "Food",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `serving` TEXT NOT NULL, `quantity` REAL NOT NULL, `proteins` REAL NOT NULL, `carbohydrates` REAL NOT NULL, `fat` REAL NOT NULL, `energy` REAL NOT NULL, `image` TEXT, `timestamp` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serving",
"columnName": "serving",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "proteins",
"columnName": "proteins",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "carbohydrates",
"columnName": "carbohydrates",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "fat",
"columnName": "fat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "energy",
"columnName": "energy",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "image",
"columnName": "image",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '414712cc283c7f1d14cde8e00da277fb')"
]
}
}

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View File

@ -6,8 +6,11 @@ import com.dzeio.openhealth.core.BaseAdapter
import com.dzeio.openhealth.core.BaseViewHolder
import com.dzeio.openhealth.data.food.Food
import com.dzeio.openhealth.databinding.ItemFoodBinding
import com.dzeio.openhealth.utils.DownloadImageTask
import kotlin.math.roundToInt
class FoodAdapter() : BaseAdapter<Food, ItemFoodBinding>() {
class FoodAdapter : BaseAdapter<Food, ItemFoodBinding>() {
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> ItemFoodBinding
get() = ItemFoodBinding::inflate
@ -19,8 +22,9 @@ class FoodAdapter() : BaseAdapter<Food, ItemFoodBinding>() {
item: Food,
position: Int
) {
holder.binding.foodName.text = item.name
holder.binding.foodDescription.text = item.energy.toString()
DownloadImageTask(holder.binding.productImage).execute(item.image)
holder.binding.foodName.text = "${item.name}"
holder.binding.foodDescription.text = "${item.quantity.roundToInt()}${item.serving.replace(Regex("\\d+"), "")} (${(item.energy / 100 * item.quantity).roundToInt()} kcal)"
holder.binding.edit.setOnClickListener {
onItemClick?.invoke(item)
}

View File

@ -5,6 +5,8 @@ import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.dzeio.openhealth.data.food.Food
import com.dzeio.openhealth.data.food.FoodDao
import com.dzeio.openhealth.data.step.Step
@ -21,7 +23,7 @@ import com.dzeio.openhealth.data.weight.WeightDao
Step::class,
Food::class
],
version = 2,
version = 3,
exportSchema = true,
autoMigrations = [
AutoMigration(from = 1, to = 2)
@ -54,6 +56,7 @@ abstract class AppDatabase : RoomDatabase() {
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addMigrations(MIGRATION_2_3)
// .addCallback(object : Callback() {
// override fun onCreate(db: SupportSQLiteDatabase) {
// super.onCreate(db)
@ -66,5 +69,13 @@ abstract class AppDatabase : RoomDatabase() {
// })
.build()
}
private val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Food ADD COLUMN serving TEXT NOT NULL")
database.execSQL("ALTER TABLE Food ADD COLUMN image TEXT")
database.execSQL("ALTER TABLE Food ")
}
}
}
}

View File

@ -2,6 +2,7 @@ package com.dzeio.openhealth.data.food
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.dzeio.openhealth.data.openfoodfact.OFFProduct
import java.util.Calendar
import java.util.TimeZone
@ -10,11 +11,40 @@ data class Food(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
var name: String,
var quantity: Int,
var serving: String,
var quantity: Float,
var proteins: Float,
var carbohydrates: Float,
var fat: Float,
var energy: Float,
var timestamp: Long = Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis
)
/**
* the url of the image
*/
var image: String?,
var timestamp: Long = Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis,
) {
companion object {
fun fromOpenFoodFact(food: OFFProduct, quantity: Float? = null): Food {
var eaten = quantity ?: food.servingQuantity ?: food.productQuantity ?: 0f
if (eaten == 0f) {
if (food.servingQuantity != null && food.servingQuantity != 0f) {
eaten = food.servingQuantity!!
} else if (food.productQuantity != null && food.productQuantity != 0f) {
eaten = food.productQuantity!!
}
}
return Food(
name = food.name,
serving = (food.servingSize ?: food.quantity ?: "unknown").replace(Regex(" +"), ""),
quantity = eaten,
proteins = food.nutriments.proteins,
carbohydrates = food.nutriments.carbohydrates,
fat = food.nutriments.fat,
energy = food.nutriments.energy ?: (food.nutriments.energyKJ * 0.2390057361).toFloat(),
image = food.image
)
}
}
}

View File

@ -16,4 +16,12 @@ class FoodRepository @Inject constructor(
fun getAll() = dao.getAll()
suspend fun add(food: Food) = dao.insert(food)
fun getById(id: Long) = dao.getOne(id)
suspend fun delete(food: Food) = dao.delete(food)
suspend fun update(food: Food) = dao.update(food)
}

View File

@ -6,7 +6,9 @@ data class OFFNutriments(
@SerializedName("carbohydrates_100g")
var carbohydrates: Float,
@SerializedName("energy-kcal_100g")
var energy: Float,
var energy: Float?,
@SerializedName("energy-kj_100g")
var energyKJ: Float,
@SerializedName("fat_100g")
var fat: Float,
@SerializedName("proteins_100g")

View File

@ -9,8 +9,20 @@ data class OFFProduct(
var name: String,
@SerializedName("serving_size")
var serving: String,
var servingSize: String?,
@SerializedName("serving_quantity")
var servingQuantity: Float?,
@SerializedName("quantity")
var quantity: String?,
@SerializedName("product_quantity")
var productQuantity: Float?,
@SerializedName("nutriments")
var nutriments: OFFNutriments
var nutriments: OFFNutriments,
@SerializedName("image_url")
var image: String?
)

View File

@ -1,8 +1,6 @@
package com.dzeio.openhealth.data.openfoodfact
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@ -14,25 +12,30 @@ interface OpenFoodFactService {
companion object {
fun getService(): OpenFoodFactService {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
// val interceptor = HttpLoggingInterceptor()
// interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
// val client = OkHttpClient.Builder()
// .addInterceptor(interceptor)
// .build()
val gson = GsonBuilder()
.setLenient()
.create()
val retrofit = Retrofit.Builder()
.baseUrl("https://world.openfoodfacts.org/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
// .client(client)
.build()
return retrofit.create(OpenFoodFactService::class.java)
}
}
@Headers("User-Agent: OpenHealth - Android - Version 1.0 - https://github.com/dzeiocom/OpenHealth")
@GET("/cgi/search.pl?json=true&fields=_id,nutriments,product_name,serving_quantity,serving_size,quantity,product_quantity,image_url&action=process")
suspend fun searchProducts(@Query("search_terms2") name: String): Response<OFFResult>
@Headers("User-Agent: OpenHealth - Android - Version 1.0 - https://github.com/dzeiocom/OpenHealth")
@GET("/api/v2/search?fields=_id,nutriments,product_name,serving_quantity")
suspend fun searchProducts(@Query("product_name") name: String): Response<OFFResult>
suspend fun findByCode(@Query("code") code: String): Response<OFFResult>
}

View File

@ -0,0 +1,88 @@
package com.dzeio.openhealth.ui.food
import android.util.Log
import android.view.LayoutInflater
import androidx.core.widget.addTextChangedListener
import androidx.navigation.fragment.navArgs
import com.dzeio.openhealth.R
import com.dzeio.openhealth.core.BaseDialog
import com.dzeio.openhealth.databinding.DialogFoodProductBinding
import com.dzeio.openhealth.utils.DownloadImageTask
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class FoodDialog :
BaseDialog<FoodDialogViewModel, DialogFoodProductBinding>(FoodDialogViewModel::class.java) {
private val args: FoodDialogArgs by navArgs()
private var quantity: Float? = null
override val bindingInflater: (LayoutInflater) -> DialogFoodProductBinding =
DialogFoodProductBinding::inflate
override fun onBuilderInit(builder: MaterialAlertDialogBuilder) {
super.onBuilderInit(builder)
builder.apply {
setTitle("Product")
setIcon(R.drawable.ic_outline_fastfood_24)
setPositiveButton(R.string.validate) { dialog, _ ->
if (quantity != null) {
viewModel.saveNewQuantity(quantity!!)
}
dialog.dismiss()
}
setNegativeButton(R.string.cancel) { dialog, _ ->
if (args.deleteOnCancel) {
viewModel.delete()
}
dialog.cancel()
}
setNeutralButton(R.string.delete) { _, _ ->
viewModel.delete()
}
}
}
override fun onCreated() {
super.onCreated()
Log.d("FoodDialog", args.id.toString())
viewModel.init(args.id)
viewModel.items.observe(this) {
Log.d("FoodDialog", it.toString())
updateGraphs(null)
binding.serving.text = "Serving: ${it.serving}"
DownloadImageTask(binding.image).execute(it.image)
binding.quantity.setText(it.quantity.toString())
}
binding.quantity.addTextChangedListener {
updateGraphs(binding.quantity.text.toString().toFloatOrNull())
}
}
private fun updateGraphs(newQuantity: Float?) {
quantity = newQuantity
viewModel.items.value?.let {
val transformer = newQuantity ?: it.quantity
val energy = it.energy / 100 * transformer
binding.energyTxt.text = "${energy.toInt()} / 2594kcal"
binding.energyBar.progress = (100 * energy / 2594).toInt()
val proteins = it.proteins / 100 * transformer
binding.proteinsTxt.text = "${proteins.toInt()} / 130g"
binding.proteinsBar.progress = (100 * proteins / 130).toInt()
val carbohydrates = it.carbohydrates / 100 * transformer
binding.carbsTxt.text = "${carbohydrates.toInt()} / 324g"
binding.carbsBar.progress = (100 * carbohydrates / 324).toInt()
val fat = it.fat / 100 * transformer
binding.fatTxt.text = "${fat.toInt()} / 87g"
binding.fatBar.progress = (100 * fat / 87).toInt()
}
}
}

View File

@ -0,0 +1,55 @@
package com.dzeio.openhealth.ui.food
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.food.Food
import com.dzeio.openhealth.data.food.FoodRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class FoodDialogViewModel @Inject internal constructor(
private val foodRepository: FoodRepository
) : BaseViewModel() {
val items: MutableLiveData<Food> = MutableLiveData()
fun init(productId: Long) {
viewModelScope.launch {
val res = foodRepository.getById(productId)
val food = res.first()
if (food != null) {
items.postValue(food)
}
}
}
fun delete() {
viewModelScope.launch {
val item = items.value
if (item != null) {
foodRepository.delete(item)
}
}
}
fun saveNewQuantity(quantity: Float) {
val it = items.value
if (it == null) {
return
}
val transformer = (quantity ?: it.quantity) / it.quantity
it.energy = it.energy * transformer
it.proteins = it.proteins * transformer
it.carbohydrates = it.carbohydrates * transformer
it.fat = it.fat * transformer
it.quantity = quantity
viewModelScope.launch {
foodRepository.update(it)
}
}
}

View File

@ -2,15 +2,20 @@ package com.dzeio.openhealth.ui.food
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.dzeio.openhealth.Application
import com.dzeio.openhealth.R
import com.dzeio.openhealth.adapters.FoodAdapter
import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.databinding.FragmentFoodHomeBinding
import com.dzeio.openhealth.ui.steps.FoodHomeViewModel
import dagger.hilt.android.AndroidEntryPoint
import java.util.Calendar
@AndroidEntryPoint
class FoodHomeFragment :
@ -26,6 +31,9 @@ class FoodHomeFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// FIXME: deprecated
setHasOptionsMenu(true)
viewModel.init()
val recycler = binding.list
@ -35,13 +43,72 @@ class FoodHomeFragment :
val adapter = FoodAdapter()
adapter.onItemClick = {
// findNavController().navigate(
// WaterHomeFragmentDirections.actionNavWaterHomeToNavWaterEdit(
// it.id
// )
// )
findNavController().navigate(
FoodHomeFragmentDirections.actionFoodHomeFragmentToNavDialogFoodProduct(
it.id,
false
)
)
}
recycler.adapter = adapter
viewModel.items.observe(viewLifecycleOwner) {
adapter.set(it)
var energy = 0f
var proteins = 0f
var carbohydrates = 0f
var fat = 0f
for (food in it) {
energy += food.energy / 100 * food.quantity
proteins += food.proteins / 100 * food.quantity
carbohydrates += food.carbohydrates / 100 * food.quantity
fat += food.fat / 100 * food.quantity
}
binding.energyTxt.text = "${energy.toInt()} / 2594kcal"
binding.energyBar.progress = (100 * energy / 2594).toInt()
binding.proteinsTxt.text = "${proteins.toInt()} / 130g"
binding.proteinsBar.progress = (100 * proteins / 130).toInt()
binding.carbsTxt.text = "${carbohydrates.toInt()} / 324g"
binding.carbsBar.progress = (100 * carbohydrates / 324).toInt()
binding.fatTxt.text = "${fat.toInt()} / 87g"
binding.fatBar.progress = (100 * fat / 87).toInt()
}
binding.next.setOnClickListener {
viewModel.next()
}
binding.previous.setOnClickListener {
viewModel.previous()
}
viewModel.date.observe(viewLifecycleOwner) {
val date = Calendar.getInstance()
date.timeInMillis = it
binding.date.text = "${date.get(Calendar.YEAR)}-${date.get(Calendar.MONTH) + 1}-${date.get(Calendar.DAY_OF_MONTH)}"
}
}
@Deprecated("Deprecated in Java")
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.action_add).isVisible = true
super.onPrepareOptionsMenu(menu)
}
@Deprecated("Deprecated in Java")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_add -> {
findNavController().navigate(
FoodHomeFragmentDirections.actionFoodHomeFragmentToNavDialogFoodSearch()
)
true
}
else -> super.onOptionsItemSelected(item)
}
}
}

View File

@ -5,22 +5,65 @@ import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.food.Food
import com.dzeio.openhealth.data.food.FoodRepository
import com.dzeio.openhealth.data.openfoodfact.OpenFoodFactService
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.util.Calendar
import javax.inject.Inject
@HiltViewModel
class FoodHomeViewModel @Inject internal constructor(
private val foodRepository: FoodRepository
private val foodRepository: FoodRepository,
private val foodFactService: OpenFoodFactService
) : BaseViewModel() {
val items: MutableLiveData<List<Food>> = MutableLiveData()
private val list: MutableLiveData<List<Food>> = MutableLiveData(arrayListOf())
val date: MutableLiveData<Long> = MutableLiveData(Calendar.getInstance().timeInMillis)
fun init() {
val now = Calendar.getInstance()
now.set(Calendar.HOUR, 0)
now.set(Calendar.MINUTE, 0)
now.set(Calendar.SECOND, 0)
date.postValue(now.timeInMillis)
viewModelScope.launch {
foodRepository.getAll().collectLatest {
items.postValue(it)
list.postValue(it)
updateList(it)
}
}
}
fun next() {
val now = Calendar.getInstance()
now.timeInMillis = date.value!!
now.add(Calendar.DAY_OF_YEAR, 1)
date.value = now.timeInMillis
updateList()
}
fun previous() {
val now = Calendar.getInstance()
now.timeInMillis = date.value!!
now.add(Calendar.DAY_OF_YEAR, -1)
date.value = now.timeInMillis
updateList()
}
private fun updateList(foods: List<Food>? = null) {
val day = Calendar.getInstance()
day.timeInMillis = date.value!!
val todayInMillis = day.timeInMillis
day.add(Calendar.DAY_OF_YEAR, 1)
val tomorrow = day.timeInMillis
items.postValue((foods ?: list.value!!).filter { food ->
food.timestamp in todayInMillis until tomorrow
})
}
}

View File

@ -0,0 +1,81 @@
package com.dzeio.openhealth.ui.food
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.dzeio.openhealth.R
import com.dzeio.openhealth.adapters.FoodAdapter
import com.dzeio.openhealth.core.BaseDialog
import com.dzeio.openhealth.databinding.DialogFoodSearchProductBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@AndroidEntryPoint
class SearchFoodDialog :
BaseDialog<SearchFoodDialogViewModel, DialogFoodSearchProductBinding>(SearchFoodDialogViewModel::class.java) {
override val bindingInflater: (LayoutInflater) -> DialogFoodSearchProductBinding =
DialogFoodSearchProductBinding::inflate
override fun onBuilderInit(builder: MaterialAlertDialogBuilder) {
super.onBuilderInit(builder)
builder.apply {
setTitle("Add Product")
setIcon(R.drawable.ic_outline_fastfood_24)
setNegativeButton(R.string.close) { dialog, _ ->
dialog.cancel()
}
setNeutralButton("Search", null)
}
}
override fun onDialogInit(dialog: AlertDialog) {
super.onDialogInit(dialog)
dialog.setOnShowListener {
val btn = dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
btn.setOnClickListener {
viewModel.search(binding.input.text.toString())
binding.loading.visibility = View.VISIBLE
}
}
}
override fun onCreated() {
super.onCreated()
val recycler = binding.list
val manager = LinearLayoutManager(requireContext())
recycler.layoutManager = manager
val adapter = FoodAdapter()
adapter.onItemClick = {
lifecycleScope.launch {
val id = viewModel.addProduct(it)
findNavController().navigate(
SearchFoodDialogDirections.actionNavDialogFoodSearchToNavDialogFoodProduct(
id,
true
)
)
}
}
recycler.adapter = adapter
viewModel.items.observe(this) {
adapter.set(it)
binding.loading.visibility = View.GONE
}
}
}

View File

@ -0,0 +1,37 @@
package com.dzeio.openhealth.ui.food
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.food.Food
import com.dzeio.openhealth.data.food.FoodRepository
import com.dzeio.openhealth.data.openfoodfact.OpenFoodFactService
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class SearchFoodDialogViewModel @Inject internal constructor(
private val foodRepository: FoodRepository,
private val foodFactService: OpenFoodFactService
) : BaseViewModel() {
val items: MutableLiveData<List<Food>> = MutableLiveData()
fun search(text: String) {
viewModelScope.launch {
val response = foodFactService.searchProducts(text)
val product = response.body()
if (product != null) {
items.postValue(product.products
.filter { it.name != null }
.map { Food.fromOpenFoodFact(it) }
)
}
}
}
suspend fun addProduct(product: Food): Long {
return foodRepository.add(product)
}
}

View File

@ -21,7 +21,6 @@ import com.dzeio.openhealth.utils.GraphUtils
import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint
import kotlin.math.max
import kotlin.math.min
@AndroidEntryPoint
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) {
@ -104,6 +103,14 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
}
}
viewModel.steps.observe(viewLifecycleOwner) {
binding.stepsCurrent.text = it.toString()
}
viewModel.stepsGoal.observe(viewLifecycleOwner) {
binding.stepsTotal.text = it.toString()
}
viewModel.weights.observe(viewLifecycleOwner) {
if (it != null) {
updateGraph(it)
@ -169,55 +176,42 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
height = binding.background.height
}
val graph = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(graph)
val rect = RectF(
10f,
15f,
90f,
85f
)
// DrawUtils.drawRect(
// canvas,
// RectF(
// 0f,
// 0f,
// 100f,
// 100f
// ),
// MaterialColors.getColor(
// requireView(),
// com.google.android.material.R.attr.colorOnPrimary
// ),
// 3f
// )
DrawUtils.drawArc(
canvas,
100f,
rect,
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorOnPrimary
),
3f
)
val animator = ValueAnimator.ofInt(
min(this.oldValue, viewModel.dailyWaterIntake.toFloat()).toInt(),
min(newValue, viewModel.dailyWaterIntake)
this.oldValue.toInt(),
newValue
)
animator.duration = 300 // ms
animator.addUpdateListener {
this.oldValue = 100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat()
this.oldValue = (it.animatedValue as Int).toFloat()
val value = 100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat()
// Log.d("Test2", "${this.oldValue}")
val graph = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(graph)
val rect = RectF(
10f,
15f,
90f,
85f
)
// background Arc
DrawUtils.drawArc(
canvas,
100f,
rect,
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorOnPrimary
),
3f
)
DrawUtils.drawArc(
canvas,
max(this.oldValue, 1f),
max(value, 1f),
rect,
MaterialColors.getColor(
requireView(),

View File

@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.Settings
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.step.StepRepository
import com.dzeio.openhealth.data.water.Water
import com.dzeio.openhealth.data.water.WaterRepository
import com.dzeio.openhealth.data.weight.Weight
@ -22,10 +23,17 @@ import javax.inject.Inject
class HomeViewModel @Inject internal constructor(
private val weightRepository: WeightRepository,
private val waterRepository: WaterRepository,
stepRepository: StepRepository,
settings: SharedPreferences,
config: Configuration
) : BaseViewModel() {
private val _steps = MutableLiveData(0)
val steps: LiveData<Int> = _steps
private val _stepsGoal: MutableLiveData<Int?> = MutableLiveData()
val stepsGoal: LiveData<Int?> = _stepsGoal
private val _water = MutableLiveData<Water?>(null)
val water: LiveData<Water?> = _water
@ -53,6 +61,14 @@ class HomeViewModel @Inject internal constructor(
}
}
viewModelScope.launch {
_steps.postValue(stepRepository.todaySteps())
}
this._stepsGoal.postValue(
config.getInt(Settings.STEPS_GOAL).value
)
viewModelScope.launch {
weightRepository.getWeights().collectLatest {
_weights.postValue(it)

View File

@ -0,0 +1,30 @@
package com.dzeio.openhealth.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.AsyncTask
import android.util.Log
import android.widget.ImageView
import java.net.URL
class DownloadImageTask(var bmImage: ImageView) :
AsyncTask<String?, Void?, Bitmap?>() {
@Deprecated("Deprecated in Java")
override fun doInBackground(vararg urls: String?): Bitmap? {
val urldisplay = urls[0]
var mIcon11: Bitmap? = null
try {
val `in` = URL(urldisplay).openStream()
mIcon11 = BitmapFactory.decodeStream(`in`)
} catch (e: Exception) {
Log.e("Error", e.message!!)
e.printStackTrace()
}
return mIcon11
}
@Deprecated("Deprecated in Java", ReplaceWith("bmImage.setImageBitmap(result)"))
override fun onPostExecute(result: Bitmap?) {
bmImage.setImageBitmap(result)
}
}

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M1,21.98c0,0.56 0.45,1.01 1.01,1.01H15c0.56,0 1.01,-0.45 1.01,-1.01V21H1v0.98zM8.5,8.99C4.75,8.99 1,11 1,15h15c0,-4 -3.75,-6.01 -7.5,-6.01zM3.62,13c1.11,-1.55 3.47,-2.01 4.88,-2.01s3.77,0.46 4.88,2.01H3.62zM1,17h15v2H1zM18,5V1h-2v4h-5l0.23,2h9.56l-1.4,14H18v2h1.72c0.84,0 1.53,-0.65 1.63,-1.47L23,5h-5z"/>
</vector>

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:paddingHorizontal="16dp"
android:nestedScrollingEnabled="true"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:srcCompat="@tools:sample/avatars" />
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent"
android:layout_marginVertical="16dp"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Energy" />
<TextView
android:id="@+id/energy_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/energy_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Proteins" />
<TextView
android:id="@+id/proteins_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/proteins_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Carbs" />
<TextView
android:id="@+id/carbs_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/carbs_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Fat" />
<TextView
android:id="@+id/fat_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/fat_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/serving"
android:layout_width="match_parent"
android:textAlignment="textEnd"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Serving Size: 250g" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/quantity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</LinearLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:paddingHorizontal="16dp"
android:nestedScrollingEnabled="true"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/loading"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="5"
tools:listitem="@layout/item_food"/>
</LinearLayout>

View File

@ -16,13 +16,155 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
android:orientation="horizontal"
>
<ImageView
android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_chevron_left_24"/>
<TextView
android:id="@+id/date"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_gravity="center"
android:text="2022-12-24" />
<ImageView
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_chevron_right_24"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Energy" />
<TextView
android:id="@+id/energy_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/energy_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Proteins" />
<TextView
android:id="@+id/proteins_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/proteins_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Carbs" />
<TextView
android:id="@+id/carbs_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/carbs_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Fat" />
<TextView
android:id="@+id/fat_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd"
android:text="250 / 2594 kcal" />
</LinearLayout>
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/fat_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="wrap_content"
android:progress="24"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<androidx.recyclerview.widget.RecyclerView
android:clipToPadding="false"
android:id="@+id/list"

View File

@ -96,11 +96,11 @@
<TextView
android:id="@+id/fragment_home_water_total"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textColor="?attr/colorOnBackground"
android:text="@string/unit_volume_milliliter_unit"
android:textAlignment="center" />
android:textAlignment="center"
android:textColor="?attr/colorOnBackground" />
</LinearLayout>
@ -200,7 +200,7 @@
<TextView
android:id="@+id/steps_current"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/unit_volume_milliliter_unit"
android:textAlignment="center"
@ -208,18 +208,17 @@
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="2dp"
/>
android:layout_height="2dp" />
<TextView
android:id="@+id/steps_total"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:textColor="?attr/colorOnBackground"
android:layout_height="match_parent"
android:text="@string/unit_volume_milliliter_unit"
android:textAlignment="center" />
android:textAlignment="center"
android:textColor="?attr/colorOnBackground" />
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewFilledStyle"
android:layout_marginBottom="8dp"
android:clickable="true"
@ -16,8 +17,18 @@
android:orientation="horizontal"
android:weightSum="1">
<ImageView
android:id="@+id/product_image"
android:layout_width="43dp"
android:layout_height="match_parent"
android:layout_marginEnd="16dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
tools:srcCompat="@tools:sample/avatars" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">

View File

@ -152,5 +152,34 @@
android:id="@+id/foodHomeFragment"
tools:layout="@layout/fragment_food_home"
android:name="com.dzeio.openhealth.ui.food.FoodHomeFragment"
android:label="FoodHomeFragment" />
android:label="FoodHomeFragment" >
<action
android:id="@+id/action_foodHomeFragment_to_nav_dialog_food_search"
app:destination="@id/nav_dialog_food_search" />
<action
android:id="@+id/action_foodHomeFragment_to_nav_dialog_food_product"
app:destination="@id/nav_dialog_food_product" />
</fragment>
<dialog
android:id="@+id/nav_dialog_food_search"
android:name="com.dzeio.openhealth.ui.food.SearchFoodDialog"
tools:layout="@layout/dialog_food_search_product"
>
<action
android:id="@+id/action_nav_dialog_food_search_to_nav_dialog_food_product"
app:destination="@id/nav_dialog_food_product" />
</dialog>
<dialog
android:id="@+id/nav_dialog_food_product"
android:name="com.dzeio.openhealth.ui.food.FoodDialog"
tools:layout="@layout/dialog_food_product"
>
<argument
android:name="id"
app:argType="long" />
<argument
android:name="delete_on_cancel"
app:argType="boolean"
/>
</dialog>
</navigation>

View File

@ -42,6 +42,8 @@
<string name="permission_declined">Vous avez décliné une permission, vous ne pouvez pas utiliser cette extension suaf si vous réactivez la permission manuellement</string>
<string name="menu_steps">Pas</string>
<string name="weight_current">Poid actuel: %1$s%2$s</string>
<string name="delete">Supprimer</string>
<string name="close">Fermer</string>
<!-- Error Activity Translations -->
<string name="error_app_crash">Une Erreur est survenu lors de l\'utilisation de l\'application</string>

View File

@ -53,6 +53,8 @@
<string name="edit_daily_goal">Modifiy daily goal</string>
<string name="menu_steps">Steps</string>
<string name="weight_current">Current weight: %1$s</string>
<string name="delete">Delete</string>
<string name="close">Close</string>
<!-- Error Activity Translations -->