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" /> (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 +27,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") @@ -116,24 +113,10 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { } @Serializable -data class CreateRecipeRequest(val name: String, val description: String, val directions: List, val ingredients: List) +data class ShoppingListAddRecipeRequest(val id: Int, val multiplier: Int) -fun createRecipe(ctx: Context, recipe: CreateRecipe) { - val request = CreateRecipeRequest(recipe.name, recipe.description, recipe.directions, recipe.ingredients) +fun AddRecipeToShoppingList(ctx: Context, id: Int, multiplier: Int) { + val request = ShoppingListAddRecipeRequest(id, multiplier) - requestJson(ctx, "POST", "/api/recipe/create", request) -} - -@Serializable -data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) - -fun getAllRecipies(ctx: Context): List { - return requestJson>(ctx, "GET", "/api/Recipe/getall", null) -} - -@Serializable -data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List, val ingredients: List) - -fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { - return requestJson(ctx, "GET", "/api/Recipe/get/$id", null) + return requestJson(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 e0aa866..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 @@ -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,22 @@ data class GenerateRecipeRequest(val dish: String, val language: String, val num fun generateRecipeWithAI(ctx: Context, title: String, language: String): Recipe { return requestJson(ctx, "POST", "/api/Recipe/chatbot", GenerateRecipeRequest(title, language, 1, arrayOf())) } + +fun createRecipe(ctx: Context, recipe: Recipe) { + requestJson(ctx, "POST", "/api/recipe/create", recipe) +} + +@Serializable +data class GetAllRecipesResponse(val id: Int, val name: String, val description: String) + +fun getAllRecipes(ctx: Context): List { + return requestJson>(ctx, "GET", "/api/Recipe/getall", null) +} + +@Serializable +data class RecipeDetailsResponse(val id: Int, val name: String, val description: String, val directions: List, val ingredients: List) + +fun getRecipeDetails(ctx: Context, id: Int): RecipeDetailsResponse { + return requestJson(ctx, "GET", "/api/Recipe/get/$id", null) +} + 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 { return requestJson>(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(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(ctx, "POST", "/api/ShoppingList/add", request) +fun editShoppingItem(ctx: Context, old: ShoppingListItem, new: ShoppingListItem) { + requestJson(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/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, - val ingredients: List -) - -@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/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/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, @@ -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(R.id.dishName).text.toString().trim(), description = findViewById(R.id.dishDescription).text.toString().trim(), directions = directions, @@ -107,8 +101,8 @@ class CreateDishActivity : AppCompatActivity() { ingredientContainer.addView(ingredientRow) } - private fun collectIngredients(): List { - val ingredients = mutableListOf() + private fun collectIngredients(): List { + val ingredients = mutableListOf() 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/DishDetailsActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishDetailsActivity.kt index 3f41774..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 @@ -1,7 +1,14 @@ 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 import androidx.appcompat.app.AppCompatActivity import tech.mercantec.easyeat.R @@ -9,14 +16,21 @@ 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.ImageButton import android.widget.TextView +import androidx.core.widget.doAfterTextChanged +import tech.mercantec.easyeat.helpers.AddRecipeToShoppingList +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(R.id.ingredients) + val multiplierEditText = findViewById(R.id.ingredient_multiplier) + val dishId = intent.getIntExtra("dish_id", -1) if (dishId == -1) { Toast.makeText(this, "No dish ID provided", Toast.LENGTH_SHORT).show() @@ -35,20 +49,108 @@ class DishDetailsActivity : AppCompatActivity() { return@thread } Log.i("DISH", recipe.ingredients.toString()) + val instructionsLayout = findViewById(R.id.instructions) + +// Example data: recipe.ingredients and recipe.directions runOnUiThread { - // Set title and description findViewById(R.id.dishDetailName).text = recipe.name findViewById(R.id.dishDetailDescription).text = recipe.description - // Set up the ingredient list - val ingredientListView = findViewById(R.id.dishDetailIngredients) - val ingredientAdapter = IngredientAdapter(this, recipe.ingredients) - ingredientListView.adapter = ingredientAdapter - - val instructionsListView = findViewById(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 + setPadding(0, 8, 0, 8) + } + instructionsLayout.addView(textView) + } } + + fun displayIngredients(ingredients: List, multiplier: Int, container: LinearLayout) { + container.removeAllViews() // clear previous views + + for (ingredient in ingredients) { + val row = TextView(this) + val amount = (ingredient.amount ?: 0.0) * multiplier + 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) + } + } + + runOnUiThread { + val nameView = findViewById(R.id.dishDetailName) + val descView = findViewById(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) + } + + findViewById(R.id.increment_multiplier).setOnClickListener { + multiplier++ + multiplierEditText.setText(multiplier.toString(), TextView.BufferType.EDITABLE) + + displayIngredients(recipe.ingredients, multiplier, ingredientsContainer) + } + + findViewById(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(R.id.ingredient_multiplier) + val multiplierText = multiplierEditText.text.toString() + AddRecipeToShoppingList(this, dishId, multiplierText.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/DishesFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt index 4e1a301..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 @@ -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 @@ -15,8 +15,9 @@ 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 tech.mercantec.easyeat.models.Recipe import kotlin.concurrent.thread class DishesFragment : Fragment() { @@ -36,28 +37,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