mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-23 11:22:10 +00:00
feat: Add support for Food input
This commit is contained in:
parent
4bcae95d52
commit
643861bf1e
@ -2,7 +2,7 @@
|
|||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"identityHash": "794ed5ee15db239f9a2708b951f55552",
|
"identityHash": "0f92ae44f4503b964d4986959a15ef4e",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "Weight",
|
"tableName": "Weight",
|
||||||
@ -150,7 +150,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "Food",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
@ -164,6 +164,12 @@
|
|||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serving",
|
||||||
|
"columnName": "serving",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "quantity",
|
"fieldPath": "quantity",
|
||||||
"columnName": "quantity",
|
"columnName": "quantity",
|
||||||
@ -214,7 +220,7 @@
|
|||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
232
app/schemas/com.dzeio.openhealth.data.AppDatabase/3.json
Normal file
232
app/schemas/com.dzeio.openhealth.data.AppDatabase/3.json
Normal 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')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
5
app/src/debug/res/drawable/baseline_chevron_left_24.xml
Normal file
5
app/src/debug/res/drawable/baseline_chevron_left_24.xml
Normal 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>
|
5
app/src/debug/res/drawable/baseline_chevron_right_24.xml
Normal file
5
app/src/debug/res/drawable/baseline_chevron_right_24.xml
Normal 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>
|
@ -6,8 +6,11 @@ import com.dzeio.openhealth.core.BaseAdapter
|
|||||||
import com.dzeio.openhealth.core.BaseViewHolder
|
import com.dzeio.openhealth.core.BaseViewHolder
|
||||||
import com.dzeio.openhealth.data.food.Food
|
import com.dzeio.openhealth.data.food.Food
|
||||||
import com.dzeio.openhealth.databinding.ItemFoodBinding
|
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
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> ItemFoodBinding
|
||||||
get() = ItemFoodBinding::inflate
|
get() = ItemFoodBinding::inflate
|
||||||
@ -19,8 +22,9 @@ class FoodAdapter() : BaseAdapter<Food, ItemFoodBinding>() {
|
|||||||
item: Food,
|
item: Food,
|
||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
holder.binding.foodName.text = item.name
|
DownloadImageTask(holder.binding.productImage).execute(item.image)
|
||||||
holder.binding.foodDescription.text = item.energy.toString()
|
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 {
|
holder.binding.edit.setOnClickListener {
|
||||||
onItemClick?.invoke(item)
|
onItemClick?.invoke(item)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import androidx.room.AutoMigration
|
|||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
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.Food
|
||||||
import com.dzeio.openhealth.data.food.FoodDao
|
import com.dzeio.openhealth.data.food.FoodDao
|
||||||
import com.dzeio.openhealth.data.step.Step
|
import com.dzeio.openhealth.data.step.Step
|
||||||
@ -21,7 +23,7 @@ import com.dzeio.openhealth.data.weight.WeightDao
|
|||||||
Step::class,
|
Step::class,
|
||||||
Food::class
|
Food::class
|
||||||
],
|
],
|
||||||
version = 2,
|
version = 3,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration(from = 1, to = 2)
|
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
|
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
|
||||||
private fun buildDatabase(context: Context): AppDatabase {
|
private fun buildDatabase(context: Context): AppDatabase {
|
||||||
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
|
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
|
||||||
|
.addMigrations(MIGRATION_2_3)
|
||||||
// .addCallback(object : Callback() {
|
// .addCallback(object : Callback() {
|
||||||
// override fun onCreate(db: SupportSQLiteDatabase) {
|
// override fun onCreate(db: SupportSQLiteDatabase) {
|
||||||
// super.onCreate(db)
|
// super.onCreate(db)
|
||||||
@ -66,5 +69,13 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
// })
|
// })
|
||||||
.build()
|
.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 ")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.dzeio.openhealth.data.food
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import com.dzeio.openhealth.data.openfoodfact.OFFProduct
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
|
||||||
@ -10,11 +11,40 @@ data class Food(
|
|||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0,
|
var id: Long = 0,
|
||||||
var name: String,
|
var name: String,
|
||||||
var quantity: Int,
|
var serving: String,
|
||||||
|
var quantity: Float,
|
||||||
var proteins: Float,
|
var proteins: Float,
|
||||||
var carbohydrates: Float,
|
var carbohydrates: Float,
|
||||||
var fat: Float,
|
var fat: Float,
|
||||||
var energy: 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,4 +16,12 @@ class FoodRepository @Inject constructor(
|
|||||||
|
|
||||||
fun getAll() = dao.getAll()
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ data class OFFNutriments(
|
|||||||
@SerializedName("carbohydrates_100g")
|
@SerializedName("carbohydrates_100g")
|
||||||
var carbohydrates: Float,
|
var carbohydrates: Float,
|
||||||
@SerializedName("energy-kcal_100g")
|
@SerializedName("energy-kcal_100g")
|
||||||
var energy: Float,
|
var energy: Float?,
|
||||||
|
@SerializedName("energy-kj_100g")
|
||||||
|
var energyKJ: Float,
|
||||||
@SerializedName("fat_100g")
|
@SerializedName("fat_100g")
|
||||||
var fat: Float,
|
var fat: Float,
|
||||||
@SerializedName("proteins_100g")
|
@SerializedName("proteins_100g")
|
||||||
|
@ -9,8 +9,20 @@ data class OFFProduct(
|
|||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
@SerializedName("serving_size")
|
@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")
|
@SerializedName("nutriments")
|
||||||
var nutriments: OFFNutriments
|
var nutriments: OFFNutriments,
|
||||||
|
|
||||||
|
@SerializedName("image_url")
|
||||||
|
var image: String?
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.dzeio.openhealth.data.openfoodfact
|
package com.dzeio.openhealth.data.openfoodfact
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
@ -14,25 +12,30 @@ interface OpenFoodFactService {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getService(): OpenFoodFactService {
|
fun getService(): OpenFoodFactService {
|
||||||
val interceptor = HttpLoggingInterceptor()
|
// val interceptor = HttpLoggingInterceptor()
|
||||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
|
// interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||||
val client = OkHttpClient.Builder()
|
// val client = OkHttpClient.Builder()
|
||||||
.addInterceptor(interceptor)
|
// .addInterceptor(interceptor)
|
||||||
.build()
|
// .build()
|
||||||
val gson = GsonBuilder()
|
val gson = GsonBuilder()
|
||||||
.setLenient()
|
.setLenient()
|
||||||
.create()
|
.create()
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://world.openfoodfacts.org/")
|
.baseUrl("https://world.openfoodfacts.org/")
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||||
.client(client)
|
// .client(client)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return retrofit.create(OpenFoodFactService::class.java)
|
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")
|
@Headers("User-Agent: OpenHealth - Android - Version 1.0 - https://github.com/dzeiocom/OpenHealth")
|
||||||
@GET("/api/v2/search?fields=_id,nutriments,product_name,serving_quantity")
|
@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>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
88
app/src/main/java/com/dzeio/openhealth/ui/food/FoodDialog.kt
Normal file
88
app/src/main/java/com/dzeio/openhealth/ui/food/FoodDialog.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,20 @@ package com.dzeio.openhealth.ui.food
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.dzeio.openhealth.Application
|
import com.dzeio.openhealth.Application
|
||||||
|
import com.dzeio.openhealth.R
|
||||||
import com.dzeio.openhealth.adapters.FoodAdapter
|
import com.dzeio.openhealth.adapters.FoodAdapter
|
||||||
import com.dzeio.openhealth.core.BaseFragment
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
import com.dzeio.openhealth.databinding.FragmentFoodHomeBinding
|
import com.dzeio.openhealth.databinding.FragmentFoodHomeBinding
|
||||||
import com.dzeio.openhealth.ui.steps.FoodHomeViewModel
|
import com.dzeio.openhealth.ui.steps.FoodHomeViewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class FoodHomeFragment :
|
class FoodHomeFragment :
|
||||||
@ -26,6 +31,9 @@ class FoodHomeFragment :
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// FIXME: deprecated
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
viewModel.init()
|
viewModel.init()
|
||||||
|
|
||||||
val recycler = binding.list
|
val recycler = binding.list
|
||||||
@ -35,13 +43,72 @@ class FoodHomeFragment :
|
|||||||
|
|
||||||
val adapter = FoodAdapter()
|
val adapter = FoodAdapter()
|
||||||
adapter.onItemClick = {
|
adapter.onItemClick = {
|
||||||
// findNavController().navigate(
|
findNavController().navigate(
|
||||||
// WaterHomeFragmentDirections.actionNavWaterHomeToNavWaterEdit(
|
FoodHomeFragmentDirections.actionFoodHomeFragmentToNavDialogFoodProduct(
|
||||||
// it.id
|
it.id,
|
||||||
// )
|
false
|
||||||
// )
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
recycler.adapter = adapter
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,22 +5,65 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.dzeio.openhealth.core.BaseViewModel
|
import com.dzeio.openhealth.core.BaseViewModel
|
||||||
import com.dzeio.openhealth.data.food.Food
|
import com.dzeio.openhealth.data.food.Food
|
||||||
import com.dzeio.openhealth.data.food.FoodRepository
|
import com.dzeio.openhealth.data.food.FoodRepository
|
||||||
|
import com.dzeio.openhealth.data.openfoodfact.OpenFoodFactService
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Calendar
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class FoodHomeViewModel @Inject internal constructor(
|
class FoodHomeViewModel @Inject internal constructor(
|
||||||
private val foodRepository: FoodRepository
|
private val foodRepository: FoodRepository,
|
||||||
|
private val foodFactService: OpenFoodFactService
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
val items: MutableLiveData<List<Food>> = MutableLiveData()
|
val items: MutableLiveData<List<Food>> = MutableLiveData()
|
||||||
|
private val list: MutableLiveData<List<Food>> = MutableLiveData(arrayListOf())
|
||||||
|
val date: MutableLiveData<Long> = MutableLiveData(Calendar.getInstance().timeInMillis)
|
||||||
|
|
||||||
fun init() {
|
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 {
|
viewModelScope.launch {
|
||||||
foodRepository.getAll().collectLatest {
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,6 @@ import com.dzeio.openhealth.utils.GraphUtils
|
|||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) {
|
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) {
|
viewModel.weights.observe(viewLifecycleOwner) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
updateGraph(it)
|
updateGraph(it)
|
||||||
@ -169,55 +176,42 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
height = binding.background.height
|
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(
|
val animator = ValueAnimator.ofInt(
|
||||||
min(this.oldValue, viewModel.dailyWaterIntake.toFloat()).toInt(),
|
this.oldValue.toInt(),
|
||||||
min(newValue, viewModel.dailyWaterIntake)
|
newValue
|
||||||
)
|
)
|
||||||
animator.duration = 300 // ms
|
animator.duration = 300 // ms
|
||||||
animator.addUpdateListener {
|
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}")
|
// 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(
|
DrawUtils.drawArc(
|
||||||
canvas,
|
canvas,
|
||||||
max(this.oldValue, 1f),
|
max(value, 1f),
|
||||||
rect,
|
rect,
|
||||||
MaterialColors.getColor(
|
MaterialColors.getColor(
|
||||||
requireView(),
|
requireView(),
|
||||||
|
@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.dzeio.openhealth.Settings
|
import com.dzeio.openhealth.Settings
|
||||||
import com.dzeio.openhealth.core.BaseViewModel
|
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.Water
|
||||||
import com.dzeio.openhealth.data.water.WaterRepository
|
import com.dzeio.openhealth.data.water.WaterRepository
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
@ -22,10 +23,17 @@ import javax.inject.Inject
|
|||||||
class HomeViewModel @Inject internal constructor(
|
class HomeViewModel @Inject internal constructor(
|
||||||
private val weightRepository: WeightRepository,
|
private val weightRepository: WeightRepository,
|
||||||
private val waterRepository: WaterRepository,
|
private val waterRepository: WaterRepository,
|
||||||
|
stepRepository: StepRepository,
|
||||||
settings: SharedPreferences,
|
settings: SharedPreferences,
|
||||||
config: Configuration
|
config: Configuration
|
||||||
) : BaseViewModel() {
|
) : 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)
|
private val _water = MutableLiveData<Water?>(null)
|
||||||
val water: LiveData<Water?> = _water
|
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 {
|
viewModelScope.launch {
|
||||||
weightRepository.getWeights().collectLatest {
|
weightRepository.getWeights().collectLatest {
|
||||||
_weights.postValue(it)
|
_weights.postValue(it)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
@ -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>
|
5
app/src/main/res/drawable/outline_fastfood_24.xml
Normal file
5
app/src/main/res/drawable/outline_fastfood_24.xml
Normal 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>
|
154
app/src/main/res/layout/dialog_food_product.xml
Normal file
154
app/src/main/res/layout/dialog_food_product.xml
Normal 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>
|
30
app/src/main/res/layout/dialog_food_search_product.xml
Normal file
30
app/src/main/res/layout/dialog_food_search_product.xml
Normal 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>
|
@ -16,13 +16,155 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</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
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:id="@+id/list"
|
android:id="@+id/list"
|
||||||
|
@ -96,11 +96,11 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/fragment_home_water_total"
|
android:id="@+id/fragment_home_water_total"
|
||||||
style="@style/TextAppearance.Material3.LabelMedium"
|
style="@style/TextAppearance.Material3.LabelMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:textColor="?attr/colorOnBackground"
|
|
||||||
android:text="@string/unit_volume_milliliter_unit"
|
android:text="@string/unit_volume_milliliter_unit"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center"
|
||||||
|
android:textColor="?attr/colorOnBackground" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -200,7 +200,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/steps_current"
|
android:id="@+id/steps_current"
|
||||||
style="@style/TextAppearance.Material3.LabelMedium"
|
style="@style/TextAppearance.Material3.LabelMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="@string/unit_volume_milliliter_unit"
|
android:text="@string/unit_volume_milliliter_unit"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
@ -208,18 +208,17 @@
|
|||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="2dp"
|
android:layout_height="2dp" />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/steps_total"
|
android:id="@+id/steps_total"
|
||||||
style="@style/TextAppearance.Material3.LabelMedium"
|
style="@style/TextAppearance.Material3.LabelMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:textColor="?attr/colorOnBackground"
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="@string/unit_volume_milliliter_unit"
|
android:text="@string/unit_volume_milliliter_unit"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center"
|
||||||
|
android:textColor="?attr/colorOnBackground" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<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"
|
style="?attr/materialCardViewFilledStyle"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
@ -16,8 +17,18 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:weightSum="1">
|
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
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
@ -152,5 +152,34 @@
|
|||||||
android:id="@+id/foodHomeFragment"
|
android:id="@+id/foodHomeFragment"
|
||||||
tools:layout="@layout/fragment_food_home"
|
tools:layout="@layout/fragment_food_home"
|
||||||
android:name="com.dzeio.openhealth.ui.food.FoodHomeFragment"
|
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>
|
</navigation>
|
||||||
|
@ -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="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="menu_steps">Pas</string>
|
||||||
<string name="weight_current">Poid actuel: %1$s%2$s</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 -->
|
<!-- Error Activity Translations -->
|
||||||
<string name="error_app_crash">Une Erreur est survenu lors de l\'utilisation de l\'application</string>
|
<string name="error_app_crash">Une Erreur est survenu lors de l\'utilisation de l\'application</string>
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
<string name="edit_daily_goal">Modifiy daily goal</string>
|
<string name="edit_daily_goal">Modifiy daily goal</string>
|
||||||
<string name="menu_steps">Steps</string>
|
<string name="menu_steps">Steps</string>
|
||||||
<string name="weight_current">Current weight: %1$s</string>
|
<string name="weight_current">Current weight: %1$s</string>
|
||||||
|
<string name="delete">Delete</string>
|
||||||
|
<string name="close">Close</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Error Activity Translations -->
|
<!-- Error Activity Translations -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user