From 4772c0ef2639fcb2043e9e59dcd832e9383c176b Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Tue, 13 May 2025 12:41:10 +0200 Subject: [PATCH 01/10] Create recipe fragment --- .../easyeat/ui/dishes/RecipeFragment.kt | 54 +++++++++++++++++++ .../src/main/res/layout/fragment_recipe.xml | 48 +++++++++++++++++ app/app/src/main/res/values-night/styles.xml | 6 +++ app/app/src/main/res/values/strings.xml | 2 + app/app/src/main/res/values/styles.xml | 6 +++ 5 files changed, 116 insertions(+) create mode 100644 app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/RecipeFragment.kt create mode 100644 app/app/src/main/res/layout/fragment_recipe.xml create mode 100644 app/app/src/main/res/values-night/styles.xml create mode 100644 app/app/src/main/res/values/styles.xml diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/RecipeFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/RecipeFragment.kt new file mode 100644 index 0000000..2639315 --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/RecipeFragment.kt @@ -0,0 +1,54 @@ +package tech.mercantec.easyeat.ui.dishes + +import android.os.Bundle +import android.text.Html +import android.text.Html.FROM_HTML_MODE_LEGACY +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import kotlinx.serialization.json.Json +import tech.mercantec.easyeat.R +import tech.mercantec.easyeat.models.Recipe + +class RecipeFragment : Fragment() { + private var recipe: Recipe? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + arguments?.let { args -> + recipe = args.getString("RECIPE")?.let { Json.decodeFromString<Recipe>(it) } + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + val binding = inflater.inflate(R.layout.fragment_recipe, container, false) + + recipe?.let { recipe -> + binding.findViewById<TextView>(R.id.title).text = recipe.name + + binding.findViewById<TextView>(R.id.ingredients).text = + Html.fromHtml( + "<ul>" + + recipe.ingredients.map { "<li>${it.amount} ${it.unit} ${it.name}</li>" } + + "</ul>", + FROM_HTML_MODE_LEGACY + ) + + binding.findViewById<TextView>(R.id.directions).text = + Html.fromHtml( + "<ul>" + + recipe.directions.map { "<li>${it}</li>" } + + "</ul>", + FROM_HTML_MODE_LEGACY + ) + } + + return binding + } +} \ No newline at end of file diff --git a/app/app/src/main/res/layout/fragment_recipe.xml b/app/app/src/main/res/layout/fragment_recipe.xml new file mode 100644 index 0000000..8b66e2d --- /dev/null +++ b/app/app/src/main/res/layout/fragment_recipe.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:padding="30dp" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/title" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:textSize="24sp" + /> + + <TextView + android:layout_marginTop="20dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textStyle="bold" + android:text="@string/ingredients_label" + style="@style/HighContrastText" + /> + + <TextView + android:id="@+id/ingredients" + android:layout_marginTop="10dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + <TextView + android:layout_marginTop="20dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textStyle="bold" + android:text="@string/directions_label" + style="@style/HighContrastText" + /> + + <TextView + android:id="@+id/directions" + android:layout_marginTop="10dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + +</LinearLayout> diff --git a/app/app/src/main/res/values-night/styles.xml b/app/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..4aa21da --- /dev/null +++ b/app/app/src/main/res/values-night/styles.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="HighContrastText" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/white</item> + </style> +</resources> diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index e7d5584..367630e 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -47,6 +47,8 @@ <string name="ai_generate_recipe_title">Generate recipe with AI</string> <string name="dish_title_label">Name of dish</string> <string name="generate_recipe_label">Generate</string> + <string name="ingredients_label">Ingredients</string> + <string name="directions_label">Directions</string> <string-array name="units"> <item></item> <item>g</item> diff --git a/app/app/src/main/res/values/styles.xml b/app/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..560b559 --- /dev/null +++ b/app/app/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="HighContrastText" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/black</item> + </style> +</resources> From 1c085a4262c0445bfbe6859a572793e38e1f93b3 Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Tue, 13 May 2025 12:59:18 +0200 Subject: [PATCH 02/10] Finish generating recipe, refactor directions property and request helpers --- app/app/src/main/AndroidManifest.xml | 2 +- .../tech/mercantec/easyeat/helpers/auth.kt | 32 ++----------------- .../tech/mercantec/easyeat/helpers/dishes.kt | 19 +++++++++++ .../mercantec/easyeat/models/CreateRecipe.kt | 24 -------------- .../tech/mercantec/easyeat/models/recipe.kt | 5 ++- .../easyeat/ui/dishes/CreateDishActivity.kt | 18 ++++------- .../easyeat/ui/dishes/DishesFragment.kt | 6 ++-- ...IActivity.kt => GenerateRecipeActivity.kt} | 28 ++++++++-------- .../easyeat/ui/dishes/InstructionsAdapter.kt | 9 +++--- ..._form.xml => activity_generate_recipe.xml} | 0 10 files changed, 52 insertions(+), 91 deletions(-) delete mode 100644 app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt rename app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/{CreateDishAIActivity.kt => GenerateRecipeActivity.kt} (66%) rename app/app/src/main/res/layout/{create_dish_ai_form.xml => activity_generate_recipe.xml} (100%) diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml index 10e464a..e1e0150 100644 --- a/app/app/src/main/AndroidManifest.xml +++ b/app/app/src/main/AndroidManifest.xml @@ -45,7 +45,7 @@ android:exported="false" /> <activity - android:name=".ui.dishes.CreateDishAIActivity" + android:name=".ui.dishes.GenerateRecipeActivity" android:exported="false" /> <activity diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index 6ce46f1..ff8a3f2 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -3,11 +3,6 @@ package tech.mercantec.easyeat.helpers import android.content.Context import android.util.Log import kotlinx.serialization.Serializable -import tech.mercantec.easyeat.models.CreateDirection -import tech.mercantec.easyeat.models.CreateIngredient -import tech.mercantec.easyeat.models.CreateRecipe -import tech.mercantec.easyeat.models.Direction -import tech.mercantec.easyeat.models.Ingredient @Serializable data class LoginRequest(val emailUsr: String, val password: String) @@ -20,7 +15,7 @@ fun login(ctx: Context, email: String, password: String) { val response = requestJson<LoginRequest, LoginResponse>(ctx, "POST", "/api/User/login", request) - with (ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) { + with(ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) { putInt("user-id", response.id) putString("username", response.userName) putString("auth-token", response.token) @@ -30,7 +25,7 @@ fun login(ctx: Context, email: String, password: String) { } fun logout(ctx: Context) { - with (ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) { + with(ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) { remove("user-id") remove("username") remove("auth-token") @@ -114,26 +109,3 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { return requestJson<ChangePasswordRequest, Unit>(ctx, "PUT", "/api/User/change-password", request) } - -@Serializable -data class CreateRecipeRequest(val name: String, val description: String, val directions: List<CreateDirection>, val ingredients: List<CreateIngredient>) - -fun createRecipe(ctx: Context, recipe: CreateRecipe) { - val request = CreateRecipeRequest(recipe.name, recipe.description, recipe.directions, recipe.ingredients) - - requestJson<CreateRecipeRequest, Boolean>(ctx, "POST", "/api/recipe/create", request) -} - -@Serializable -data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) - -fun getAllRecipies(ctx: Context): List<GetAllRecipesResponse> { - return requestJson<Unit, List<GetAllRecipesResponse>>(ctx, "GET", "/api/Recipe/getall", null) -} - -@Serializable -data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List<Direction>, val ingredients: List<Ingredient>) - -fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { - return requestJson<Unit, RecipeDetailsResponse>(ctx, "GET", "/api/Recipe/get/$id", null) -} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt index e0aa866..4a7a599 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt @@ -2,6 +2,7 @@ package tech.mercantec.easyeat.helpers import android.content.Context import kotlinx.serialization.Serializable +import tech.mercantec.easyeat.models.Ingredient import tech.mercantec.easyeat.models.Recipe @Serializable @@ -10,3 +11,21 @@ data class GenerateRecipeRequest(val dish: String, val language: String, val num fun generateRecipeWithAI(ctx: Context, title: String, language: String): Recipe { return requestJson<GenerateRecipeRequest, Recipe>(ctx, "POST", "/api/Recipe/chatbot", GenerateRecipeRequest(title, language, 1, arrayOf())) } + +fun createRecipe(ctx: Context, recipe: Recipe) { + requestJson<Recipe, Boolean>(ctx, "POST", "/api/recipe/create", recipe) +} + +@Serializable +data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) + +fun getAllRecipes(ctx: Context): List<GetAllRecipesResponse> { + return requestJson<Unit, List<GetAllRecipesResponse>>(ctx, "GET", "/api/Recipe/getall", null) +} + +@Serializable +data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List<String>, val ingredients: List<Ingredient>) + +fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { + return requestJson<Unit, RecipeDetailsResponse>(ctx, "GET", "/api/Recipe/get/$id", null) +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt deleted file mode 100644 index 782f449..0000000 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt +++ /dev/null @@ -1,24 +0,0 @@ -package tech.mercantec.easyeat.models - -import kotlinx.serialization.Serializable - -@Serializable -data class CreateRecipe( - val name: String, - val description: String, - val directions: List<CreateDirection>, - val ingredients: List<CreateIngredient> -) - -@Serializable -data class CreateDirection( - val instructions: String -) - - -@Serializable -data class CreateIngredient( - val amount: Double?, - val unit: String?, - val name: String -) \ No newline at end of file diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt index 86e5796..2ee35ed 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt @@ -2,10 +2,9 @@ package tech.mercantec.easyeat.models import kotlinx.serialization.Serializable - @Serializable data class Recipe( - val id: Int, + val id: Int? = null, val name: String, val description: String, val directions: List<String>, @@ -14,7 +13,7 @@ data class Recipe( @Serializable data class Ingredient( - val id: Int, + val id: Int? = null, val amount: Double?, val unit: String?, val name: String diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt index 2ece5a7..b5cf99f 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt @@ -2,9 +2,7 @@ package tech.mercantec.easyeat.ui.dishes import android.app.Activity import android.app.ProgressDialog -import android.content.Context import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.widget.ArrayAdapter import android.widget.Button @@ -14,15 +12,11 @@ import android.widget.LinearLayout import android.widget.Spinner import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContentProviderCompat.requireContext import tech.mercantec.easyeat.R import tech.mercantec.easyeat.helpers.ApiRequestException -import tech.mercantec.easyeat.helpers.changePassword import tech.mercantec.easyeat.helpers.createRecipe -import tech.mercantec.easyeat.helpers.request -import tech.mercantec.easyeat.models.CreateDirection -import tech.mercantec.easyeat.models.CreateIngredient -import tech.mercantec.easyeat.models.CreateRecipe +import tech.mercantec.easyeat.models.Ingredient +import tech.mercantec.easyeat.models.Recipe import kotlin.concurrent.thread class CreateDishActivity : AppCompatActivity() { @@ -53,7 +47,7 @@ class CreateDishActivity : AppCompatActivity() { .map { line -> line.trim() } .filter { it.isNotEmpty() } - val recipe = CreateRecipe( + val recipe = Recipe( name = findViewById<EditText>(R.id.dishName).text.toString().trim(), description = findViewById<EditText>(R.id.dishDescription).text.toString().trim(), directions = directions, @@ -107,8 +101,8 @@ class CreateDishActivity : AppCompatActivity() { ingredientContainer.addView(ingredientRow) } - private fun collectIngredients(): List<CreateIngredient> { - val ingredients = mutableListOf<CreateIngredient>() + private fun collectIngredients(): List<Ingredient> { + val ingredients = mutableListOf<Ingredient>() for (i in 0 until ingredientContainer.childCount) { val ingredientView = ingredientContainer.getChildAt(i) @@ -125,7 +119,7 @@ class CreateDishActivity : AppCompatActivity() { // Optional: Only add non-empty rows if (element.isNotEmpty() && amount.isNotEmpty()) { - ingredients.add(CreateIngredient(name = element, amount = amount.toDouble(), unit = unit)) + ingredients.add(Ingredient(name = element, amount = amount.toDouble(), unit = unit)) } } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt index 4e1a301..93e06c7 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt @@ -15,7 +15,7 @@ import tech.mercantec.easyeat.R import tech.mercantec.easyeat.databinding.FragmentDishesBinding import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.GetAllRecipesResponse -import tech.mercantec.easyeat.helpers.getAllRecipies +import tech.mercantec.easyeat.helpers.getAllRecipes import tech.mercantec.easyeat.models.DishListItem import kotlin.concurrent.thread @@ -52,7 +52,7 @@ class DishesFragment : Fragment() { } dialogView.findViewById<Button>(R.id.createAIBtn).setOnClickListener { - val intent = Intent(requireContext(), CreateDishAIActivity::class.java) + val intent = Intent(requireContext(), GenerateRecipeActivity::class.java) createDishLauncher.launch(intent) dialog.dismiss() } @@ -68,7 +68,7 @@ class DishesFragment : Fragment() { thread { val recipes: List<GetAllRecipesResponse> try { - recipes = getAllRecipies(requireContext()) + recipes = getAllRecipes(requireContext()) } catch (e: ApiRequestException) { activity?.runOnUiThread { Toast.makeText(requireContext(), e.message, Toast.LENGTH_LONG).show() diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt similarity index 66% rename from app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt rename to app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt index fcf4bdc..140f74f 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt @@ -4,20 +4,19 @@ import android.app.ProgressDialog import android.os.Bundle import android.widget.Button import android.widget.EditText -import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import tech.mercantec.easyeat.R import tech.mercantec.easyeat.helpers.ApiRequestException -import tech.mercantec.easyeat.helpers.GenerateRecipeResponse +import tech.mercantec.easyeat.helpers.createRecipe import tech.mercantec.easyeat.helpers.generateRecipeWithAI import java.util.Locale import kotlin.concurrent.thread -class CreateDishAIActivity : AppCompatActivity() { +class GenerateRecipeActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.create_dish_ai_form) + setContentView(R.layout.activity_generate_recipe) findViewById<Button>(R.id.generate).setOnClickListener { val name = findViewById<EditText>(R.id.dish_title).text.toString() @@ -27,21 +26,24 @@ class CreateDishAIActivity : AppCompatActivity() { progressDialog.show() thread { - val response: GenerateRecipeResponse try { - response = generateRecipeWithAI(this, name, Locale.getDefault().displayLanguage) + val recipe = generateRecipeWithAI(this, name, Locale.getDefault().displayLanguage) + + runOnUiThread { + progressDialog.setMessage("Saving...") + } + + createRecipe(this, recipe) + + finish() } catch (e: ApiRequestException) { runOnUiThread { Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() } - - return@thread } finally { - progressDialog.hide() - } - - runOnUiThread { - Toast.makeText(this, response.toString(), Toast.LENGTH_LONG).show() + runOnUiThread { + progressDialog.hide() + } } } } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt index e0a2fee..0dc738e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt @@ -7,19 +7,18 @@ import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.TextView import tech.mercantec.easyeat.R -import tech.mercantec.easyeat.models.Direction -class InstructionsAdapter (context: Context, instructions: List<Direction>) - : ArrayAdapter<Direction>(context, 0, instructions) { +class InstructionsAdapter (context: Context, instructions: List<String>) + : ArrayAdapter<String>(context, R.layout.activity_dish_details_instructions_list_item, instructions) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val ingredient = getItem(position) + val instruction = getItem(position) val view = convertView ?: LayoutInflater.from(context) .inflate(R.layout.activity_dish_details_instructions_list_item, parent, false) val nameView = view.findViewById<TextView>(R.id.instructionText) - nameView.text = ingredient?.instruktions ?: "" + nameView.text = instruction return view } diff --git a/app/app/src/main/res/layout/create_dish_ai_form.xml b/app/app/src/main/res/layout/activity_generate_recipe.xml similarity index 100% rename from app/app/src/main/res/layout/create_dish_ai_form.xml rename to app/app/src/main/res/layout/activity_generate_recipe.xml From dbb25a394dda588aa0d469c95ae8e09aecb6d869 Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Tue, 13 May 2025 14:56:34 +0200 Subject: [PATCH 03/10] Use popup menu for creating dish --- .../easyeat/ui/dishes/DishesFragment.kt | 41 ++++++++++--------- .../ui/shopping_list/ShoppingListFragment.kt | 3 ++ .../res/layout/create_dish_modal_dialog.xml | 36 ---------------- .../src/main/res/menu/create_dish_menu.xml | 11 +++++ app/app/src/main/res/values-night/themes.xml | 1 + app/app/src/main/res/values/strings.xml | 4 +- 6 files changed, 39 insertions(+), 57 deletions(-) delete mode 100644 app/app/src/main/res/layout/create_dish_modal_dialog.xml create mode 100644 app/app/src/main/res/menu/create_dish_menu.xml diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt index 93e06c7..70ab3fd 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt @@ -6,7 +6,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button +import android.widget.PopupMenu import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts @@ -36,28 +36,31 @@ class DishesFragment : Fragment() { loadRecipes() binding.addDish.setOnClickListener { - val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.create_dish_modal_dialog, null) + val popup = PopupMenu(requireActivity(), it) - val dialog = android.app.AlertDialog.Builder(requireContext()) - .setView(dialogView) - .setCancelable(true) // tap outside to dismiss - .create() + popup.apply { + menuInflater.inflate(R.menu.create_dish_menu, menu) - dialog.window?.setBackgroundDrawableResource(android.R.color.transparent) + setOnMenuItemClickListener { + when (it.itemId) { + R.id.create_manually -> { + val intent = Intent(requireContext(), CreateDishActivity::class.java) + createDishLauncher.launch(intent) - dialogView.findViewById<Button>(R.id.createManualBtn).setOnClickListener { - val intent = Intent(requireContext(), CreateDishActivity::class.java) - createDishLauncher.launch(intent) - dialog.dismiss() + true + } + R.id.create_with_ai -> { + val intent = Intent(requireContext(), GenerateRecipeActivity::class.java) + createDishLauncher.launch(intent) + + true + } + else -> false + } + } + + show() } - - dialogView.findViewById<Button>(R.id.createAIBtn).setOnClickListener { - val intent = Intent(requireContext(), GenerateRecipeActivity::class.java) - createDishLauncher.launch(intent) - dialog.dismiss() - } - - dialog.show() } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index 7267253..4c5f49f 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -86,7 +86,9 @@ class ShoppingListFragment : Fragment() { val popup = PopupMenu(requireActivity(), view) popup.apply { + menuInflater.inflate(R.menu.shopping_item_context_menu, menu) + setOnMenuItemClickListener { when (it.itemId) { R.id.remove_shopping_item -> { @@ -107,6 +109,7 @@ class ShoppingListFragment : Fragment() { else -> false } } + show() } diff --git a/app/app/src/main/res/layout/create_dish_modal_dialog.xml b/app/app/src/main/res/layout/create_dish_modal_dialog.xml deleted file mode 100644 index 0433bb6..0000000 --- a/app/app/src/main/res/layout/create_dish_modal_dialog.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/dialog_root" - android:layout_width="250dp" - android:layout_height="400dp" - android:orientation="vertical" - android:background="@drawable/rounded_background" - android:padding="0dp" - android:gravity="center"> - - <TextView - android:layout_width="match_parent" - android:layout_height="40dp" - android:text="@string/create_dish" - android:textAlignment="center" - android:textSize="30sp"/> - - <Button - android:id="@+id/createManualBtn" - android:layout_width="200dp" - android:layout_height="0dp" - android:layout_weight="1" - android:text="@string/manually" /> - - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="#CCCCCC" /> - - <Button - android:id="@+id/createAIBtn" - android:layout_width="200dp" - android:layout_height="0dp" - android:layout_weight="1" - android:text="@string/search_for_dishes" /> -</LinearLayout> diff --git a/app/app/src/main/res/menu/create_dish_menu.xml b/app/app/src/main/res/menu/create_dish_menu.xml new file mode 100644 index 0000000..65cb3bf --- /dev/null +++ b/app/app/src/main/res/menu/create_dish_menu.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/create_manually" + android:title="@string/create_manually_label" + /> + <item + android:id="@+id/create_with_ai" + android:title="@string/create_ai_label" + /> +</menu> diff --git a/app/app/src/main/res/values-night/themes.xml b/app/app/src/main/res/values-night/themes.xml index 91d3f75..d17ff6d 100644 --- a/app/app/src/main/res/values-night/themes.xml +++ b/app/app/src/main/res/values-night/themes.xml @@ -8,5 +8,6 @@ <item name="colorSurface">@color/dark_cyan</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:colorBackground">@color/black</item> + <item name="android:windowBackground">@color/black</item> </style> </resources> diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index 367630e..9953f76 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -41,14 +41,14 @@ <string name="checked_desc">Checked</string> <string name="delete_label">Delete</string> <string name="empty_shopping_list">Your shopping list is empty</string> - <string name="search_for_dishes">Search For Dishes</string> - <string name="manually">Manually</string> <string name="create_dish">Create Dish</string> <string name="ai_generate_recipe_title">Generate recipe with AI</string> <string name="dish_title_label">Name of dish</string> <string name="generate_recipe_label">Generate</string> <string name="ingredients_label">Ingredients</string> <string name="directions_label">Directions</string> + <string name="create_manually_label">Create manually</string> + <string name="create_ai_label">Generate recipe using AI</string> <string-array name="units"> <item></item> <item>g</item> From 00cc8101f1b9d551302627e5f17b0138893260de Mon Sep 17 00:00:00 2001 From: LilleBRG <lillebrgmc@gmail.com> Date: Tue, 13 May 2025 15:11:57 +0200 Subject: [PATCH 04/10] need to merge but trying to add recipe to shoppinglist --- .../tech/mercantec/easyeat/helpers/auth.kt | 15 ++- .../mercantec/easyeat/models/CreateRecipe.kt | 8 +- .../easyeat/ui/dishes/CreateDishAIActivity.kt | 4 +- .../easyeat/ui/dishes/CreateDishActivity.kt | 1 - .../easyeat/ui/dishes/DishDetailsActivity.kt | 116 ++++++++++++++++-- .../easyeat/ui/dishes/InstructionsAdapter.kt | 7 +- .../activity_create_dish_ingredient_row.xml | 4 +- .../main/res/layout/activity_dish_details.xml | 54 ++++++-- app/app/src/main/res/values/strings.xml | 1 + 9 files changed, 174 insertions(+), 36 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index 6ce46f1..f152195 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -3,10 +3,8 @@ package tech.mercantec.easyeat.helpers import android.content.Context import android.util.Log import kotlinx.serialization.Serializable -import tech.mercantec.easyeat.models.CreateDirection import tech.mercantec.easyeat.models.CreateIngredient import tech.mercantec.easyeat.models.CreateRecipe -import tech.mercantec.easyeat.models.Direction import tech.mercantec.easyeat.models.Ingredient @Serializable @@ -116,7 +114,7 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { } @Serializable -data class CreateRecipeRequest(val name: String, val description: String, val directions: List<CreateDirection>, val ingredients: List<CreateIngredient>) +data class CreateRecipeRequest(val name: String, val description: String, val directions: List<String>, val ingredients: List<CreateIngredient>) fun createRecipe(ctx: Context, recipe: CreateRecipe) { val request = CreateRecipeRequest(recipe.name, recipe.description, recipe.directions, recipe.ingredients) @@ -132,8 +130,17 @@ fun getAllRecipies(ctx: Context): List<GetAllRecipesResponse> { } @Serializable -data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List<Direction>, val ingredients: List<Ingredient>) +data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List<String>, val ingredients: List<Ingredient>) fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { return requestJson<Unit, RecipeDetailsResponse>(ctx, "GET", "/api/Recipe/get/$id", null) } + +@Serializable +data class ShoppingListAddRecipeRequest(val id: Int, val multiplier: Int) + +fun AddRecipeToShoppingList(ctx: Context, id: Int, multiplier: Int) { + val request = ShoppingListAddRecipeRequest(id, multiplier) + + return requestJson<ShoppingListAddRecipeRequest, Unit>(ctx, "ADD", "/api/ShoppingList/recipeadd", request) +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt index 782f449..e7661bb 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/CreateRecipe.kt @@ -6,16 +6,10 @@ import kotlinx.serialization.Serializable data class CreateRecipe( val name: String, val description: String, - val directions: List<CreateDirection>, + val directions: List<String>, val ingredients: List<CreateIngredient> ) -@Serializable -data class CreateDirection( - val instructions: String -) - - @Serializable data class CreateIngredient( val amount: Double?, diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt index fcf4bdc..6b46c5e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishAIActivity.kt @@ -9,8 +9,8 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import tech.mercantec.easyeat.R import tech.mercantec.easyeat.helpers.ApiRequestException -import tech.mercantec.easyeat.helpers.GenerateRecipeResponse import tech.mercantec.easyeat.helpers.generateRecipeWithAI +import tech.mercantec.easyeat.models.Recipe import java.util.Locale import kotlin.concurrent.thread @@ -27,7 +27,7 @@ class CreateDishAIActivity : AppCompatActivity() { progressDialog.show() thread { - val response: GenerateRecipeResponse + val response: Recipe try { response = generateRecipeWithAI(this, name, Locale.getDefault().displayLanguage) } catch (e: ApiRequestException) { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt index 2ece5a7..e5b72a9 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt @@ -20,7 +20,6 @@ import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.changePassword import tech.mercantec.easyeat.helpers.createRecipe import tech.mercantec.easyeat.helpers.request -import tech.mercantec.easyeat.models.CreateDirection import tech.mercantec.easyeat.models.CreateIngredient import tech.mercantec.easyeat.models.CreateRecipe import kotlin.concurrent.thread diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index 3f41774..a87bc46 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -1,7 +1,12 @@ package tech.mercantec.easyeat.ui.dishes +import android.app.Activity +import android.app.ProgressDialog import android.os.Bundle import android.util.Log +import android.view.View +import android.widget.Button +import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import tech.mercantec.easyeat.R @@ -9,14 +14,22 @@ import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.RecipeDetailsResponse import tech.mercantec.easyeat.helpers.getRecipeDetails import kotlin.concurrent.thread -import android.widget.ListView +import android.widget.EditText import android.widget.TextView +import androidx.core.widget.doAfterTextChanged +import tech.mercantec.easyeat.helpers.AddRecipeToShoppingList +import tech.mercantec.easyeat.helpers.createRecipe +import tech.mercantec.easyeat.models.CreateRecipe +import tech.mercantec.easyeat.models.Ingredient class DishDetailsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dish_details) + val ingredientsContainer = findViewById<LinearLayout>(R.id.dishDetailIngredients) + val multiplierEditText = findViewById<EditText>(R.id.ingredientMultiplier) + val dishId = intent.getIntExtra("dish_id", -1) if (dishId == -1) { Toast.makeText(this, "No dish ID provided", Toast.LENGTH_SHORT).show() @@ -24,6 +37,8 @@ class DishDetailsActivity : AppCompatActivity() { return } + + thread { val recipe: RecipeDetailsResponse try { @@ -35,20 +50,101 @@ class DishDetailsActivity : AppCompatActivity() { return@thread } Log.i("DISH", recipe.ingredients.toString()) + val ingredientsLayout = findViewById<LinearLayout>(R.id.dishDetailIngredients) + val instructionsLayout = findViewById<LinearLayout>(R.id.dishDetailInstructions) + +// Example data: recipe.ingredients and recipe.directions runOnUiThread { - // Set title and description findViewById<TextView>(R.id.dishDetailName).text = recipe.name findViewById<TextView>(R.id.dishDetailDescription).text = recipe.description + // Populate Ingredients + recipe.ingredients.forEach { ingredient -> + val textView = TextView(this).apply { + text = "${ingredient.name} ${ingredient.amount ?: ""} ${ingredient.unit ?: ""}" + textSize = 18f + } + textView.textAlignment = View.TEXT_ALIGNMENT_CENTER + ingredientsLayout.addView(textView) + } - // Set up the ingredient list - val ingredientListView = findViewById<ListView>(R.id.dishDetailIngredients) - val ingredientAdapter = IngredientAdapter(this, recipe.ingredients) - ingredientListView.adapter = ingredientAdapter - - val instructionsListView = findViewById<ListView>(R.id.dishDetailInstructions) - val instructionsAdapter = InstructionsAdapter(this, recipe.directions) - instructionsListView.adapter = instructionsAdapter + // Populate Instructions (if directions are strings) + recipe.directions.forEachIndexed { index, direction -> + val textView = TextView(this).apply { + text = "${index + 1}. $direction" + textSize = 18f + } + textView.textAlignment = View.TEXT_ALIGNMENT_CENTER + instructionsLayout.addView(textView) + } } + + fun displayIngredients(ingredients: List<Ingredient>, multiplier: Int, container: LinearLayout) { + container.removeAllViews() // clear previous views + + for (ingredient in ingredients) { + val row = TextView(this) + val amount = (ingredient.amount ?: 0.0) * multiplier + row.text = "${ingredient.name}: ${"%.2f".format(amount)} ${ingredient.unit ?: ""}" + row.textSize = 18f + row.setPadding(0, 8, 0, 8) + row.textAlignment = View.TEXT_ALIGNMENT_CENTER + container.addView(row) + } + } + + runOnUiThread { + val nameView = findViewById<TextView>(R.id.dishDetailName) + val descView = findViewById<TextView>(R.id.dishDetailDescription) + + nameView.text = recipe.name + descView.text = recipe.description + + // Default multiplier + var multiplier = 1 + + // Initial display + displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) + + // Listen for user input changes + multiplierEditText.doAfterTextChanged { + multiplier = it.toString().toIntOrNull() ?: 1 + displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) + } + + // You can do the same for directions if needed + } + + val saveButton: Button = findViewById(R.id.saveDishButton) + saveButton.setOnClickListener { + + + val progressDialog = ProgressDialog(this) + progressDialog.setMessage("Loading...") + progressDialog.show() + thread { + try { + AddRecipeToShoppingList(this, dishId, multiplierEditText.text.toString().toIntOrNull() ?: 1) + } catch (e: ApiRequestException) { + runOnUiThread { + Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() + } + + return@thread + } finally { + runOnUiThread { + progressDialog.hide() + } + } + + runOnUiThread { + Toast.makeText(this, "Password changed successfully", Toast.LENGTH_LONG).show() + } + + setResult(Activity.RESULT_OK) + finish() + } + } + } } } \ No newline at end of file diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt index e0a2fee..161f58c 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/InstructionsAdapter.kt @@ -7,10 +7,9 @@ import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.TextView import tech.mercantec.easyeat.R -import tech.mercantec.easyeat.models.Direction -class InstructionsAdapter (context: Context, instructions: List<Direction>) - : ArrayAdapter<Direction>(context, 0, instructions) { +class InstructionsAdapter (context: Context, instructions: List<String>) + : ArrayAdapter<String>(context, 0, instructions) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val ingredient = getItem(position) @@ -19,7 +18,7 @@ class InstructionsAdapter (context: Context, instructions: List<Direction>) val nameView = view.findViewById<TextView>(R.id.instructionText) - nameView.text = ingredient?.instruktions ?: "" + nameView.text = ingredient?: "" return view } diff --git a/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml b/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml index c9d590b..6d2a736 100644 --- a/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml +++ b/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml @@ -43,7 +43,9 @@ <EditText android:id="@+id/ingredientAmountEditText" android:layout_width="173dp" - android:layout_height="48dp" /> + android:layout_height="48dp" + android:inputType="numberDecimal" + /> </LinearLayout> <!-- Measurement field --> diff --git a/app/app/src/main/res/layout/activity_dish_details.xml b/app/app/src/main/res/layout/activity_dish_details.xml index 584e952..d431ab4 100644 --- a/app/app/src/main/res/layout/activity_dish_details.xml +++ b/app/app/src/main/res/layout/activity_dish_details.xml @@ -19,13 +19,16 @@ android:textSize="30sp" android:textAlignment="center" android:layout_marginBottom="10dp" - android:textStyle="bold" /> + android:textStyle="bold" + android:layout_marginHorizontal="10sp"/> + /> <TextView android:id="@+id/dishDetailDescription" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" - android:textAlignment="center" /> + android:textAlignment="center" + android:layout_marginHorizontal="10sp"/> <TextView android:layout_width="match_parent" android:layout_height="match_parent" @@ -34,11 +37,37 @@ android:textAlignment="center" android:textStyle="bold" android:text="Ingredients"/> - <ListView + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textSize="18sp" + android:layout_marginLeft="10sp" + android:text="Food for how many people?" + android:layout_weight="0.7"/> + <EditText + android:id="@+id/ingredientMultiplier" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ems="10" + android:inputType="number" + android:layout_weight="1" + android:layout_marginRight="10sp"/> + </LinearLayout> + + + <LinearLayout android:id="@+id/dishDetailIngredients" android:layout_width="match_parent" android:layout_height="match_parent" - /> + android:orientation="vertical" + android:layout_marginHorizontal="10sp"> + + </LinearLayout> + <TextView android:layout_width="match_parent" @@ -49,10 +78,21 @@ android:textStyle="bold" android:text="Instructions"/> - <ListView + <LinearLayout android:id="@+id/dishDetailInstructions" android:layout_width="match_parent" android:layout_height="match_parent" - /> + android:orientation="vertical" + android:layout_marginHorizontal="10sp"/> + + <Button + android:id="@+id/add_dish" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_margin="16dp" + android:backgroundTint="@color/cyan" + android:text="@string/add_ingredients_to_shopping_list" + android:tint="@android:color/white"/> </LinearLayout> -</ScrollView> \ No newline at end of file +</ScrollView> diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index e7d5584..737af97 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ <string name="ai_generate_recipe_title">Generate recipe with AI</string> <string name="dish_title_label">Name of dish</string> <string name="generate_recipe_label">Generate</string> + <string name="add_ingredients_to_shopping_list">Add ingredients to Shopping List</string> <string-array name="units"> <item></item> <item>g</item> From 1e01d1ee9070757327561472cdbf423dc563726d Mon Sep 17 00:00:00 2001 From: LilleBRG <lillebrgmc@gmail.com> Date: Tue, 13 May 2025 15:24:25 +0200 Subject: [PATCH 05/10] fixed errors in merge --- .../tech/mercantec/easyeat/helpers/auth.kt | 20 +------------------ .../tech/mercantec/easyeat/helpers/dishes.kt | 3 ++- .../easyeat/ui/dishes/DishDetailsActivity.kt | 2 -- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index fb5c664..bb8cdce 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -3,9 +3,8 @@ package tech.mercantec.easyeat.helpers import android.content.Context import android.util.Log import kotlinx.serialization.Serializable -import tech.mercantec.easyeat.models.CreateIngredient -import tech.mercantec.easyeat.models.CreateRecipe import tech.mercantec.easyeat.models.Ingredient +import tech.mercantec.easyeat.models.Recipe @Serializable data class LoginRequest(val emailUsr: String, val password: String) @@ -113,28 +112,11 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { return requestJson<ChangePasswordRequest, Unit>(ctx, "PUT", "/api/User/change-password", request) } -@Serializable -data class CreateRecipeRequest(val name: String, val description: String, val directions: List<String>, val ingredients: List<CreateIngredient>) -fun createRecipe(ctx: Context, recipe: CreateRecipe) { - val request = CreateRecipeRequest(recipe.name, recipe.description, recipe.directions, recipe.ingredients) - requestJson<CreateRecipeRequest, Boolean>(ctx, "POST", "/api/recipe/create", request) -} -@Serializable -data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) -fun getAllRecipies(ctx: Context): List<GetAllRecipesResponse> { - return requestJson<Unit, List<GetAllRecipesResponse>>(ctx, "GET", "/api/Recipe/getall", null) -} -@Serializable -data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List<String>, val ingredients: List<Ingredient>) - -fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { - return requestJson<Unit, RecipeDetailsResponse>(ctx, "GET", "/api/Recipe/get/$id", null) -} @Serializable data class ShoppingListAddRecipeRequest(val id: Int, val multiplier: Int) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt index 4a7a599..146e876 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt @@ -19,7 +19,7 @@ fun createRecipe(ctx: Context, recipe: Recipe) { @Serializable data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) -fun getAllRecipes(ctx: Context): List<GetAllRecipesResponse> { +fun getAllRecipies(ctx: Context): List<GetAllRecipesResponse> { return requestJson<Unit, List<GetAllRecipesResponse>>(ctx, "GET", "/api/Recipe/getall", null) } @@ -29,3 +29,4 @@ data class RecipeDetailsResponse(val id: Int, val name: String, val description: fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { return requestJson<Unit, RecipeDetailsResponse>(ctx, "GET", "/api/Recipe/get/$id", null) } + diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index a87bc46..5c5f1f6 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -18,8 +18,6 @@ import android.widget.EditText import android.widget.TextView import androidx.core.widget.doAfterTextChanged import tech.mercantec.easyeat.helpers.AddRecipeToShoppingList -import tech.mercantec.easyeat.helpers.createRecipe -import tech.mercantec.easyeat.models.CreateRecipe import tech.mercantec.easyeat.models.Ingredient class DishDetailsActivity : AppCompatActivity() { From 2c22f8d8e134bf83d824ab2e1a5b57f77d2278f6 Mon Sep 17 00:00:00 2001 From: LilleBRG <lillebrgmc@gmail.com> Date: Tue, 13 May 2025 15:38:11 +0200 Subject: [PATCH 06/10] more fixes idk --- app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt | 2 +- app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt | 2 +- .../tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt | 3 --- .../tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt | 2 +- .../java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt | 1 + .../tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt | 2 +- app/app/src/main/res/layout/activity_dish_details.xml | 2 +- 7 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index bb8cdce..5362641 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -124,5 +124,5 @@ data class ShoppingListAddRecipeRequest(val id: Int, val multiplier: Int) fun AddRecipeToShoppingList(ctx: Context, id: Int, multiplier: Int) { val request = ShoppingListAddRecipeRequest(id, multiplier) - return requestJson<ShoppingListAddRecipeRequest, Unit>(ctx, "ADD", "/api/ShoppingList/recipeadd", request) + return requestJson<ShoppingListAddRecipeRequest, Unit>(ctx, "POST", "/api/ShoppingList/recipeadd", request) } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt index 146e876..bcaba3a 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/dishes.kt @@ -19,7 +19,7 @@ fun createRecipe(ctx: Context, recipe: Recipe) { @Serializable data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) -fun getAllRecipies(ctx: Context): List<GetAllRecipesResponse> { +fun getAllRecipes(ctx: Context): List<GetAllRecipesResponse> { return requestJson<Unit, List<GetAllRecipesResponse>>(ctx, "GET", "/api/Recipe/getall", null) } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt index 7743358..b5cf99f 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt @@ -15,9 +15,6 @@ import androidx.appcompat.app.AppCompatActivity import tech.mercantec.easyeat.R import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.createRecipe -import tech.mercantec.easyeat.helpers.request -import tech.mercantec.easyeat.models.CreateIngredient -import tech.mercantec.easyeat.models.CreateRecipe import tech.mercantec.easyeat.models.Ingredient import tech.mercantec.easyeat.models.Recipe import kotlin.concurrent.thread diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index 5c5f1f6..bfa9630 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -112,7 +112,7 @@ class DishDetailsActivity : AppCompatActivity() { // You can do the same for directions if needed } - val saveButton: Button = findViewById(R.id.saveDishButton) + val saveButton: Button = findViewById(R.id.addDishToShoppingList) saveButton.setOnClickListener { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt index 70ab3fd..b80f99a 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt @@ -17,6 +17,7 @@ import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.GetAllRecipesResponse import tech.mercantec.easyeat.helpers.getAllRecipes import tech.mercantec.easyeat.models.DishListItem +import tech.mercantec.easyeat.models.Recipe import kotlin.concurrent.thread class DishesFragment : Fragment() { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt index 94523f1..b4fc843 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/GenerateRecipeActivity.kt @@ -27,7 +27,7 @@ class GenerateRecipeActivity : AppCompatActivity() { progressDialog.show() thread { - val response: GenerateRecipeResponse +// val response: GenerateRecipeResponse try { val recipe = generateRecipeWithAI(this, name, Locale.getDefault().displayLanguage) diff --git a/app/app/src/main/res/layout/activity_dish_details.xml b/app/app/src/main/res/layout/activity_dish_details.xml index d431ab4..088988f 100644 --- a/app/app/src/main/res/layout/activity_dish_details.xml +++ b/app/app/src/main/res/layout/activity_dish_details.xml @@ -86,7 +86,7 @@ android:layout_marginHorizontal="10sp"/> <Button - android:id="@+id/add_dish" + android:id="@+id/addDishToShoppingList" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" From b04f385bdb0c90fed6be3a4391fe764ce4c1a106 Mon Sep 17 00:00:00 2001 From: LilleBRG <lillebrgmc@gmail.com> Date: Tue, 13 May 2025 15:52:41 +0200 Subject: [PATCH 07/10] very close to add to shopping list wrks --- .../src/main/java/tech/mercantec/easyeat/helpers/auth.kt | 6 ------ .../tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt | 4 +++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index 5362641..0ef3061 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -112,12 +112,6 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { return requestJson<ChangePasswordRequest, Unit>(ctx, "PUT", "/api/User/change-password", request) } - - - - - - @Serializable data class ShoppingListAddRecipeRequest(val id: Int, val multiplier: Int) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index bfa9630..f89f414 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -121,7 +121,9 @@ class DishDetailsActivity : AppCompatActivity() { progressDialog.show() thread { try { - AddRecipeToShoppingList(this, dishId, multiplierEditText.text.toString().toIntOrNull() ?: 1) + val multiplierEditText = findViewById<EditText>(R.id.ingredientMultiplier) + val multiplierText = multiplierEditText.text.toString() + AddRecipeToShoppingList(this, dishId, multiplierText.toIntOrNull() ?: 1) } catch (e: ApiRequestException) { runOnUiThread { Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() From 7a25b0708beca0a5240edd65f8ca6456e4c41934 Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Tue, 13 May 2025 21:15:06 +0200 Subject: [PATCH 08/10] Implement editing shopping items --- .../easyeat/helpers/shopping_list.kt | 11 +- .../easyeat/models/ShoppingListItem.kt | 8 +- .../ui/shopping_list/ShoppingListFragment.kt | 154 ++++++++++++------ .../src/main/res/drawable/ic_delete_24px.xml | 10 -- ...list.xml => dialog_edit_shopping_item.xml} | 0 .../res/menu/shopping_item_context_menu.xml | 5 +- app/app/src/main/res/values/strings.xml | 2 + 7 files changed, 120 insertions(+), 70 deletions(-) delete mode 100644 app/app/src/main/res/drawable/ic_delete_24px.xml rename app/app/src/main/res/layout/{dialog_add_to_shopping_list.xml => dialog_edit_shopping_item.xml} (100%) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt index a9adbc8..e6af1db 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt @@ -8,13 +8,12 @@ fun getShoppingList(ctx: Context): Array<ShoppingListItem> { return requestJson<Unit, Array<ShoppingListItem>>(ctx, "GET", "/api/ShoppingList/get", null) } -@Serializable -data class AddShoppingItemRequest(val name: String, val amount: Double?, val unit: String?, val checked: Boolean) +fun addShoppingItem(ctx: Context, item: ShoppingListItem): ShoppingListItem { + return requestJson<ShoppingListItem, ShoppingListItem>(ctx, "POST", "/api/ShoppingList/add", item) +} -fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?): ShoppingListItem { - val request = AddShoppingItemRequest(name, amount, unit, false) - - return requestJson<AddShoppingItemRequest, ShoppingListItem>(ctx, "POST", "/api/ShoppingList/add", request) +fun editShoppingItem(ctx: Context, old: ShoppingListItem, new: ShoppingListItem) { + requestJson<ShoppingListItem, Boolean>(ctx, "PUT", "/api/ShoppingList/update?itemId=${old.id}", new) } fun toggleShoppingItemChecked(ctx: Context, item: ShoppingListItem) { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt index a97a6a7..f9d5711 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt @@ -3,4 +3,10 @@ package tech.mercantec.easyeat.models import kotlinx.serialization.Serializable @Serializable -data class ShoppingListItem(val id: Int, var name: String, var amount: Double?, var unit: String?, var checked: Boolean) +data class ShoppingListItem( + var id: Int? = null, + var name: String, + var amount: Double?, + var unit: String?, + var checked: Boolean = false, +) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index 4c5f49f..2523ed8 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -9,6 +9,7 @@ import android.widget.ArrayAdapter import android.widget.EditText import android.widget.PopupMenu import android.widget.Spinner +import android.widget.TextView import android.widget.Toast import androidx.fragment.app.Fragment import tech.mercantec.easyeat.R @@ -16,6 +17,7 @@ import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.addShoppingItem import tech.mercantec.easyeat.helpers.deleteShoppingItem +import tech.mercantec.easyeat.helpers.editShoppingItem import tech.mercantec.easyeat.helpers.getShoppingList import tech.mercantec.easyeat.helpers.toggleShoppingItemChecked import tech.mercantec.easyeat.models.ShoppingListItem @@ -86,11 +88,36 @@ class ShoppingListFragment : Fragment() { val popup = PopupMenu(requireActivity(), view) popup.apply { - menuInflater.inflate(R.menu.shopping_item_context_menu, menu) setOnMenuItemClickListener { when (it.itemId) { + R.id.edit_shopping_item -> { + showEditDialog(item) { dialog, newItem -> + thread { + try { + editShoppingItem(requireContext(), item, newItem) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + + return@thread + } finally { + dialog.dismiss() + } + + activity?.runOnUiThread { + val adapter = binding.shoppingList.adapter as ShoppingItemAdapter + + adapter.remove(item) + adapter.insert(newItem, position) + } + } + } + + true + } R.id.remove_shopping_item -> { (parent.adapter as ShoppingItemAdapter).remove(item) @@ -119,65 +146,88 @@ class ShoppingListFragment : Fragment() { // Show new item dialog when clicking add binding.addToShoppingList.setOnClickListener { - val view = requireActivity().layoutInflater.inflate(R.layout.dialog_add_to_shopping_list, null) - - val dialog = AlertDialog.Builder(activity) - .setView(view) - .setPositiveButton(R.string.add_label) { dialog, id -> - val dialog = dialog as AlertDialog - - val amount = view.findViewById<EditText>(R.id.amount).text.toString().toDouble() - val unit = view.findViewById<Spinner>(R.id.unit_selector).selectedItem.toString().ifEmpty { null } - val name = view.findViewById<EditText>(R.id.name).text.toString() - - dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false - dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false - - thread { - val item: ShoppingListItem - try { - item = addShoppingItem(requireContext(), name, amount, unit) - } catch (e: ApiRequestException) { - activity?.runOnUiThread { - Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() - } - - return@thread - } finally { - activity?.runOnUiThread { - dialog.dismiss() - } + showEditDialog(null) { dialog, item -> + thread { + val newItem: ShoppingListItem + try { + newItem = addShoppingItem(requireContext(), item) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() } + return@thread + } finally { activity?.runOnUiThread { - val adapter = binding.shoppingList.adapter as ShoppingItemAdapter - - for (i in 0 ..< adapter.count) { - if (adapter.getItem(i)?.id == item.id) { - adapter.remove(adapter.getItem(i)) - adapter.insert(item, i) - - return@runOnUiThread - } - } - - adapter.insert(item, adapter.count) + dialog.dismiss() } } + + activity?.runOnUiThread { + val adapter = binding.shoppingList.adapter as ShoppingItemAdapter + + for (i in 0..<adapter.count) { + if (adapter.getItem(i)?.id == newItem.id) { + adapter.remove(adapter.getItem(i)) + adapter.insert(newItem, i) + + return@runOnUiThread + } + } + + adapter.insert(newItem, adapter.count) + } } - .setNegativeButton(R.string.cancel_label) { dialog, _ -> - dialog.cancel() - } - .create() - - dialog.show() - - val adapter = ArrayAdapter.createFromResource(requireContext(), R.array.units, android.R.layout.simple_spinner_item) - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - - dialog.findViewById<Spinner>(R.id.unit_selector).adapter = adapter + } } return binding.root } + + private fun showEditDialog(item: ShoppingListItem?, onSave: (dialog: AlertDialog, item: ShoppingListItem) -> Unit) { + val view = requireActivity().layoutInflater.inflate(R.layout.dialog_edit_shopping_item, null) + + val dialog = AlertDialog.Builder(activity) + .setView(view) + .setPositiveButton(R.string.add_label) { dialog, id -> + val dialog = dialog as AlertDialog + + val amount = view.findViewById<EditText>(R.id.amount).text.toString().toDouble() + val unit = view.findViewById<Spinner>(R.id.unit_selector).selectedItem.toString() + .ifEmpty { null } + val name = view.findViewById<EditText>(R.id.name).text.toString() + + onSave(dialog, ShoppingListItem(item?.id, name, amount, unit, item?.checked ?: false)) + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false + + + } + .setNegativeButton(R.string.cancel_label) { dialog, _ -> + dialog.cancel() + } + .create() + + dialog.show() + + val adapter = ArrayAdapter.createFromResource( + requireContext(), + R.array.units, + android.R.layout.simple_spinner_item + ) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + + dialog.findViewById<Spinner>(R.id.unit_selector).adapter = adapter + + // Pre-fill dialog inputs with current item if applicable + item?.let { item -> + view.findViewById<EditText>(R.id.amount).setText(item.amount?.toBigDecimal()?.stripTrailingZeros().toString(), TextView.BufferType.EDITABLE) + view.findViewById<Spinner>(R.id.unit_selector).setSelection(adapter.getPosition(item.unit)) + view.findViewById<EditText>(R.id.name).setText(item.name, TextView.BufferType.EDITABLE) + + view.findViewById<TextView>(R.id.title).text = resources.getString(R.string.edit_shopping_item_label) + dialog.getButton(AlertDialog.BUTTON_POSITIVE).text = resources.getString(R.string.save_label) + } + } } \ No newline at end of file diff --git a/app/app/src/main/res/drawable/ic_delete_24px.xml b/app/app/src/main/res/drawable/ic_delete_24px.xml deleted file mode 100644 index d1ed443..0000000 --- a/app/app/src/main/res/drawable/ic_delete_24px.xml +++ /dev/null @@ -1,10 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="960" - android:viewportHeight="960" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M280,840Q247,840 223.5,816.5Q200,793 200,760L200,240L160,240L160,160L360,160L360,120L600,120L600,160L800,160L800,240L760,240L760,760Q760,793 736.5,816.5Q713,840 680,840L280,840ZM360,680L440,680L440,320L360,320L360,680ZM520,680L600,680L600,320L520,320L520,680Z"/> -</vector> diff --git a/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml b/app/app/src/main/res/layout/dialog_edit_shopping_item.xml similarity index 100% rename from app/app/src/main/res/layout/dialog_add_to_shopping_list.xml rename to app/app/src/main/res/layout/dialog_edit_shopping_item.xml diff --git a/app/app/src/main/res/menu/shopping_item_context_menu.xml b/app/app/src/main/res/menu/shopping_item_context_menu.xml index 48be440..14f1008 100644 --- a/app/app/src/main/res/menu/shopping_item_context_menu.xml +++ b/app/app/src/main/res/menu/shopping_item_context_menu.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/edit_shopping_item" + android:title="@string/edit_label" + /> <item android:id="@+id/remove_shopping_item" - android:icon="@drawable/ic_delete_24px" android:title="@string/delete_label" /> </menu> diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index a337f1a..bc68bda 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ <string name="Instructions">Instructions</string> <string name="cancel_label">Cancel</string> <string name="add_shopping_item_label">Add shopping item</string> + <string name="edit_shopping_item_label">Edit shopping item</string> <string name="ingredient_amount_label">Amount</string> <string name="ingredient_amount_hint">500</string> <string name="ingredient_name_label">Name</string> @@ -40,6 +41,7 @@ <string name="logout_label">Log out</string> <string name="checked_desc">Checked</string> <string name="delete_label">Delete</string> + <string name="edit_label">Edit</string> <string name="empty_shopping_list">Your shopping list is empty</string> <string name="create_dish">Create Dish</string> <string name="ai_generate_recipe_title">Generate recipe with AI</string> From 48b362254241e5fb92282edec01675d3c027b353 Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Tue, 13 May 2025 22:09:50 +0200 Subject: [PATCH 09/10] Redesign dish details page --- .../easyeat/ui/dishes/DishDetailsActivity.kt | 21 ++--- .../main/res/layout/activity_dish_details.xml | 89 ++++++++++--------- app/app/src/main/res/values/strings.xml | 2 + 3 files changed, 53 insertions(+), 59 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index f89f414..518d6ab 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -3,8 +3,10 @@ package tech.mercantec.easyeat.ui.dishes import android.app.Activity import android.app.ProgressDialog import android.os.Bundle +import android.text.Html import android.util.Log import android.view.View +import android.view.ViewGroup import android.widget.Button import android.widget.LinearLayout import android.widget.Toast @@ -25,7 +27,7 @@ class DishDetailsActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dish_details) - val ingredientsContainer = findViewById<LinearLayout>(R.id.dishDetailIngredients) + val ingredientsContainer = findViewById<LinearLayout>(R.id.ingredients) val multiplierEditText = findViewById<EditText>(R.id.ingredientMultiplier) val dishId = intent.getIntExtra("dish_id", -1) @@ -48,30 +50,20 @@ class DishDetailsActivity : AppCompatActivity() { return@thread } Log.i("DISH", recipe.ingredients.toString()) - val ingredientsLayout = findViewById<LinearLayout>(R.id.dishDetailIngredients) - val instructionsLayout = findViewById<LinearLayout>(R.id.dishDetailInstructions) + val instructionsLayout = findViewById<LinearLayout>(R.id.instructions) // Example data: recipe.ingredients and recipe.directions runOnUiThread { findViewById<TextView>(R.id.dishDetailName).text = recipe.name findViewById<TextView>(R.id.dishDetailDescription).text = recipe.description - // Populate Ingredients - recipe.ingredients.forEach { ingredient -> - val textView = TextView(this).apply { - text = "${ingredient.name} ${ingredient.amount ?: ""} ${ingredient.unit ?: ""}" - textSize = 18f - } - textView.textAlignment = View.TEXT_ALIGNMENT_CENTER - ingredientsLayout.addView(textView) - } // Populate Instructions (if directions are strings) recipe.directions.forEachIndexed { index, direction -> val textView = TextView(this).apply { text = "${index + 1}. $direction" textSize = 18f + setPadding(0, 8, 0, 8) } - textView.textAlignment = View.TEXT_ALIGNMENT_CENTER instructionsLayout.addView(textView) } } @@ -82,10 +74,9 @@ class DishDetailsActivity : AppCompatActivity() { for (ingredient in ingredients) { val row = TextView(this) val amount = (ingredient.amount ?: 0.0) * multiplier - row.text = "${ingredient.name}: ${"%.2f".format(amount)} ${ingredient.unit ?: ""}" + row.text = Html.fromHtml("• ${ingredient.name}: ${"%.2f".format(amount)} ${ingredient.unit ?: ""}", Html.FROM_HTML_MODE_LEGACY) row.textSize = 18f row.setPadding(0, 8, 0, 8) - row.textAlignment = View.TEXT_ALIGNMENT_CENTER container.addView(row) } } diff --git a/app/app/src/main/res/layout/activity_dish_details.xml b/app/app/src/main/res/layout/activity_dish_details.xml index 088988f..e50eb50 100644 --- a/app/app/src/main/res/layout/activity_dish_details.xml +++ b/app/app/src/main/res/layout/activity_dish_details.xml @@ -1,89 +1,89 @@ <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="409dp" - android:layout_height="729dp" - tools:layout_editor_absoluteX="1dp" - tools:layout_editor_absoluteY="1dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - > + android:padding="30sp" + android:orientation="vertical"> + <TextView android:id="@+id/dishDetailName" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" - android:textAlignment="center" android:layout_marginBottom="10dp" android:textStyle="bold" - android:layout_marginHorizontal="10sp"/> + style="@style/HighContrastText" /> + <TextView android:id="@+id/dishDetailDescription" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" - android:textAlignment="center" - android:layout_marginHorizontal="10sp"/> - <TextView - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginVertical="20dp" - android:textSize="25sp" - android:textAlignment="center" - android:textStyle="bold" - android:text="Ingredients"/> + /> + <LinearLayout + android:layout_marginTop="20dp" + android:layout_marginHorizontal="10dp" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> + <TextView - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:textSize="18sp" - android:layout_marginLeft="10sp" - android:text="Food for how many people?" - android:layout_weight="0.7"/> + android:textSize="16sp" + android:paddingTop="8dp" + android:text="@string/portions_label" + /> + <EditText android:id="@+id/ingredientMultiplier" - android:layout_width="match_parent" + android:layout_marginStart="20dp" + android:layout_width="50dp" android:layout_height="wrap_content" - android:ems="10" android:inputType="number" - android:layout_weight="1" - android:layout_marginRight="10sp"/> - </LinearLayout> - - - <LinearLayout - android:id="@+id/dishDetailIngredients" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:layout_marginHorizontal="10sp"> + /> </LinearLayout> - <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginVertical="20dp" - android:textSize="25sp" - android:textAlignment="center" android:textStyle="bold" - android:text="Instructions"/> + android:textSize="18sp" + android:text="@string/ingredients_label" + style="@style/HighContrastText" + /> <LinearLayout - android:id="@+id/dishDetailInstructions" + android:id="@+id/ingredients" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginVertical="20dp" + android:textStyle="bold" + android:textSize="18sp" + android:text="@string/instructions_label" + style="@style/HighContrastText" + /> + + <LinearLayout + android:id="@+id/instructions" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:layout_marginHorizontal="10sp"/> + /> <Button android:id="@+id/addDishToShoppingList" @@ -93,6 +93,7 @@ android:layout_margin="16dp" android:backgroundTint="@color/cyan" android:text="@string/add_ingredients_to_shopping_list" - android:tint="@android:color/white"/> + android:tint="@android:color/white" + /> </LinearLayout> </ScrollView> diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index bc68bda..d527807 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -52,6 +52,8 @@ <string name="directions_label">Directions</string> <string name="create_manually_label">Create manually</string> <string name="create_ai_label">Generate recipe using AI</string> + <string name="portions_label">Portions</string> + <string name="instructions_label">Instructions</string> <string-array name="units"> <item></item> <item>g</item> From 6e72f549cfde95b496a4023426c181af9123f36a Mon Sep 17 00:00:00 2001 From: Reimar <mail@reim.ar> Date: Wed, 14 May 2025 08:40:36 +0200 Subject: [PATCH 10/10] Add buttons for incrementing/decrementing portion size, fix decimal numbers --- .../easyeat/ui/dishes/DishDetailsActivity.kt | 29 ++++++++++++++----- .../ui/shopping_list/ShoppingListFragment.kt | 2 +- .../res/drawable/ic_arrow_drop_down_24px.xml | 10 +++++++ .../res/drawable/ic_arrow_drop_up_24px.xml | 10 +++++++ .../main/res/layout/activity_dish_details.xml | 28 +++++++++++++++++- .../src/main/res/layout/activity_register.xml | 2 +- app/app/src/main/res/values/strings.xml | 2 ++ 7 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 app/app/src/main/res/drawable/ic_arrow_drop_down_24px.xml create mode 100644 app/app/src/main/res/drawable/ic_arrow_drop_up_24px.xml diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index 518d6ab..e25238e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt @@ -17,6 +17,7 @@ import tech.mercantec.easyeat.helpers.RecipeDetailsResponse import tech.mercantec.easyeat.helpers.getRecipeDetails import kotlin.concurrent.thread import android.widget.EditText +import android.widget.ImageButton import android.widget.TextView import androidx.core.widget.doAfterTextChanged import tech.mercantec.easyeat.helpers.AddRecipeToShoppingList @@ -28,7 +29,7 @@ class DishDetailsActivity : AppCompatActivity() { setContentView(R.layout.activity_dish_details) val ingredientsContainer = findViewById<LinearLayout>(R.id.ingredients) - val multiplierEditText = findViewById<EditText>(R.id.ingredientMultiplier) + val multiplierEditText = findViewById<EditText>(R.id.ingredient_multiplier) val dishId = intent.getIntExtra("dish_id", -1) if (dishId == -1) { @@ -37,8 +38,6 @@ class DishDetailsActivity : AppCompatActivity() { return } - - thread { val recipe: RecipeDetailsResponse try { @@ -74,7 +73,9 @@ class DishDetailsActivity : AppCompatActivity() { for (ingredient in ingredients) { val row = TextView(this) val amount = (ingredient.amount ?: 0.0) * multiplier - row.text = Html.fromHtml("• ${ingredient.name}: ${"%.2f".format(amount)} ${ingredient.unit ?: ""}", Html.FROM_HTML_MODE_LEGACY) + val amountStr = amount.toBigDecimal().stripTrailingZeros().toPlainString() + + row.text = Html.fromHtml("• ${ingredient.name}: $amountStr ${ingredient.unit ?: ""}", Html.FROM_HTML_MODE_LEGACY) row.textSize = 18f row.setPadding(0, 8, 0, 8) container.addView(row) @@ -100,19 +101,33 @@ class DishDetailsActivity : AppCompatActivity() { displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) } + findViewById<ImageButton>(R.id.increment_multiplier).setOnClickListener { + multiplier++ + multiplierEditText.setText(multiplier.toString(), TextView.BufferType.EDITABLE) + + displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) + } + + findViewById<ImageButton>(R.id.decrement_multiplier).setOnClickListener { + if (multiplier <= 1) return@setOnClickListener + + multiplier-- + multiplierEditText.setText(multiplier.toString(), TextView.BufferType.EDITABLE) + + displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) + } + // You can do the same for directions if needed } val saveButton: Button = findViewById(R.id.addDishToShoppingList) saveButton.setOnClickListener { - - val progressDialog = ProgressDialog(this) progressDialog.setMessage("Loading...") progressDialog.show() thread { try { - val multiplierEditText = findViewById<EditText>(R.id.ingredientMultiplier) + val multiplierEditText = findViewById<EditText>(R.id.ingredient_multiplier) val multiplierText = multiplierEditText.text.toString() AddRecipeToShoppingList(this, dishId, multiplierText.toIntOrNull() ?: 1) } catch (e: ApiRequestException) { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index 2523ed8..1ece23e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -222,7 +222,7 @@ class ShoppingListFragment : Fragment() { // Pre-fill dialog inputs with current item if applicable item?.let { item -> - view.findViewById<EditText>(R.id.amount).setText(item.amount?.toBigDecimal()?.stripTrailingZeros().toString(), TextView.BufferType.EDITABLE) + view.findViewById<EditText>(R.id.amount).setText(item.amount?.toBigDecimal()?.stripTrailingZeros()?.toPlainString(), TextView.BufferType.EDITABLE) view.findViewById<Spinner>(R.id.unit_selector).setSelection(adapter.getPosition(item.unit)) view.findViewById<EditText>(R.id.name).setText(item.name, TextView.BufferType.EDITABLE) diff --git a/app/app/src/main/res/drawable/ic_arrow_drop_down_24px.xml b/app/app/src/main/res/drawable/ic_arrow_drop_down_24px.xml new file mode 100644 index 0000000..dfea22c --- /dev/null +++ b/app/app/src/main/res/drawable/ic_arrow_drop_down_24px.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M480,600L280,400L680,400L480,600Z"/> +</vector> diff --git a/app/app/src/main/res/drawable/ic_arrow_drop_up_24px.xml b/app/app/src/main/res/drawable/ic_arrow_drop_up_24px.xml new file mode 100644 index 0000000..05735c6 --- /dev/null +++ b/app/app/src/main/res/drawable/ic_arrow_drop_up_24px.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M280,560L480,360L680,560L280,560Z"/> +</vector> diff --git a/app/app/src/main/res/layout/activity_dish_details.xml b/app/app/src/main/res/layout/activity_dish_details.xml index e50eb50..a2b134b 100644 --- a/app/app/src/main/res/layout/activity_dish_details.xml +++ b/app/app/src/main/res/layout/activity_dish_details.xml @@ -39,15 +39,41 @@ android:layout_height="match_parent" android:textSize="16sp" android:paddingTop="8dp" + android:labelFor="@id/ingredient_multiplier" android:text="@string/portions_label" /> <EditText - android:id="@+id/ingredientMultiplier" + android:id="@+id/ingredient_multiplier" android:layout_marginStart="20dp" android:layout_width="50dp" android:layout_height="wrap_content" + android:importantForAutofill="no" android:inputType="number" + android:text="1" + /> + + <ImageButton + android:id="@+id/decrement_multiplier" + android:layout_marginStart="20dp" + android:layout_width="32dp" + android:layout_height="40dp" + android:scaleType="fitXY" + android:src="@drawable/ic_arrow_drop_down_24px" + android:contentDescription="@string/decrement_multiplier_desc" + android:background="?android:attr/selectableItemBackgroundBorderless" + /> + + + <ImageButton + android:id="@+id/increment_multiplier" + android:layout_marginStart="5dp" + android:layout_width="32dp" + android:layout_height="40dp" + android:scaleType="fitXY" + android:src="@drawable/ic_arrow_drop_up_24px" + android:contentDescription="@string/increment_multiplier_desc" + android:background="?android:attr/selectableItemBackgroundBorderless" /> </LinearLayout> diff --git a/app/app/src/main/res/layout/activity_register.xml b/app/app/src/main/res/layout/activity_register.xml index 54e8968..5b3daf1 100644 --- a/app/app/src/main/res/layout/activity_register.xml +++ b/app/app/src/main/res/layout/activity_register.xml @@ -52,7 +52,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/password_label" - /> + /> <EditText android:id="@+id/password" diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index d527807..32c8a1f 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -54,6 +54,8 @@ <string name="create_ai_label">Generate recipe using AI</string> <string name="portions_label">Portions</string> <string name="instructions_label">Instructions</string> + <string name="increment_multiplier_desc">Increment portion count</string> + <string name="decrement_multiplier_desc">Decrement portion size</string> <string-array name="units"> <item></item> <item>g</item>