From a9220c68e0767e1387d2c0ea3fbb7d5c37b614d7 Mon Sep 17 00:00:00 2001 From: Evgenii Balandin Date: Mon, 27 Jan 2025 00:35:27 +0300 Subject: [PATCH 1/7] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B4=D0=BE=D0=BC=D0=B0=D1=88=D0=BD=D0=B5=D0=B5=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20Navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 4 + .../kotlin/ru/otus/cookbook/MainActivity.kt | 16 ++ .../ru/otus/cookbook/ui/CookbookFragment.kt | 59 +++++++- .../cookbook/ui/DeleteRecipeDialogFragment.kt | 66 ++++++++ ...ormalizeAndRoundedCornersTransformation.kt | 143 ++++++++++++++++++ .../ru/otus/cookbook/ui/RecipeAdapter.kt | 85 +++++++++++ .../ru/otus/cookbook/ui/RecipeFragment.kt | 112 +++++++++++++- app/src/main/res/anim/enter_animation.xml | 7 + app/src/main/res/anim/exit_animation.xml | 7 + app/src/main/res/anim/pop_enter_animation.xml | 7 + app/src/main/res/anim/pop_exit_animation.xml | 7 + .../bg_filled_right_rounded_coners_16.xml | 16 ++ .../bg_filled_rounded_coners_50perc.xml | 18 +++ .../bg_transparent_rounded_coners_16.xml | 17 +++ app/src/main/res/drawable/ic_back.xml | 10 ++ app/src/main/res/drawable/ic_close.xml | 10 ++ app/src/main/res/drawable/ic_delete.xml | 10 ++ app/src/main/res/drawable/ic_search.xml | 10 ++ app/src/main/res/layout/activity_main.xml | 16 +- app/src/main/res/layout/fragment_cookbook.xml | 109 +++++++++++++ app/src/main/res/layout/fragment_recipe.xml | 120 ++++++++++++++- .../main/res/layout/vh_recipe_category.xml | 21 ++- app/src/main/res/layout/vh_recipe_item.xml | 67 +++++++- .../res/navigation/cookbook_nav_graph.xml | 53 +++++++ app/src/main/res/values-night/themes.xml | 14 +- app/src/main/res/values/colors.xml | 19 +++ app/src/main/res/values/strings.xml | 21 +++ app/src/main/res/values/themes.xml | 15 ++ gradle/libs.versions.toml | 2 + 30 files changed, 1043 insertions(+), 19 deletions(-) create mode 100644 app/src/main/kotlin/ru/otus/cookbook/ui/DeleteRecipeDialogFragment.kt create mode 100644 app/src/main/kotlin/ru/otus/cookbook/ui/NormalizeAndRoundedCornersTransformation.kt create mode 100644 app/src/main/kotlin/ru/otus/cookbook/ui/RecipeAdapter.kt create mode 100644 app/src/main/res/anim/enter_animation.xml create mode 100644 app/src/main/res/anim/exit_animation.xml create mode 100644 app/src/main/res/anim/pop_enter_animation.xml create mode 100644 app/src/main/res/anim/pop_exit_animation.xml create mode 100644 app/src/main/res/drawable/bg_filled_right_rounded_coners_16.xml create mode 100644 app/src/main/res/drawable/bg_filled_rounded_coners_50perc.xml create mode 100644 app/src/main/res/drawable/bg_transparent_rounded_coners_16.xml create mode 100644 app/src/main/res/drawable/ic_back.xml create mode 100644 app/src/main/res/drawable/ic_close.xml create mode 100644 app/src/main/res/drawable/ic_delete.xml create mode 100644 app/src/main/res/drawable/ic_search.xml create mode 100644 app/src/main/res/navigation/cookbook_nav_graph.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f3207a6..417ab9b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.material) implementation(libs.androidx.activity) implementation(libs.androidx.constraintlayout) + implementation(libs.glide) testImplementation(libs.junit) testImplementation(libs.kotlin.coroutines.test) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 063f4d1..65b6393 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + + + + val activity = requireActivity() + NotificationManagerCompat.from(activity).cancelAll() + activity.finishAndRemoveTask() + } + .setNegativeButton(getString(R.string.no), null) + .show() + } + } + } + + private fun performSearch(query: String) { + // Реализация поиска + Toast.makeText(requireContext(), getString(R.string.query, query), Toast.LENGTH_SHORT).show() + } + + private fun setupSearch() = binding.withBinding { + + // Обработчик нажатия на иконку + searchIcon.setOnClickListener { + val query = searchInput.text.toString() + performSearch(query) + } + + // Обработчик нажатия кнопки "Поиск" на клавиатуре + searchInput.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + val query = searchInput.text.toString() + performSearch(query) + true + } else { + false + } + } } private fun setupRecyclerView() = binding.withBinding { // Setup RecyclerView + val adapter = RecipeAdapter(requireContext()) { recipeId -> + // Handle recipe click, e.g., navigate to RecipeFragment + val action = CookbookFragmentDirections.actionCookbookFragmentToRecipeFragment(recipeId) + findNavController().navigate(action) + } + + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + recyclerView.adapter = adapter } - private fun onRecipeListUpdated(recipeList: List) { + private fun onRecipeListUpdated(recipeList: List) = binding.withBinding { // Handle recipe list + (recyclerView.adapter as RecipeAdapter).submitList(recipeList) } } \ No newline at end of file diff --git a/app/src/main/kotlin/ru/otus/cookbook/ui/DeleteRecipeDialogFragment.kt b/app/src/main/kotlin/ru/otus/cookbook/ui/DeleteRecipeDialogFragment.kt new file mode 100644 index 0000000..18bd6a2 --- /dev/null +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/DeleteRecipeDialogFragment.kt @@ -0,0 +1,66 @@ +package ru.otus.cookbook.ui + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import ru.otus.cookbook.R + + +class DeleteRecipeDialogFragment : DialogFragment() { + + companion object { + const val CONFIRMATION_RESULT = "confirmation_result" +// const val IS_DELETED = "isDeleted" +// const val DELETE_RECIPE_RESULT = "DELETE_RECIPE_RESULT" + } + + private val args: DeleteRecipeDialogFragmentArgs by navArgs() + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val recipeTitle = args.recipeTitle + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.delete_recipe_title)) + .setMessage(getString(R.string.delete_recipe_message, recipeTitle)) + .setPositiveButton(R.string.ok) { _, _ -> +// setFragmentResult(DELETE_RECIPE_RESULT, Bundle().apply { +// putBoolean(IS_DELETED, true) +// }) + dismiss() + setResult(true) + } + .setNegativeButton(R.string.cancel, { _, _ -> + dismiss() + setResult(false) + } + ) + .create() + } + + + +// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = +// AlertDialog.Builder(requireContext()) +// .setMessage(message) +// .setPositiveButton(getString(android.R.string.ok)) { _, _ -> +// dismiss() +// setResult(true) +// } +// .setNegativeButton(getString(android.R.string.cancel)) { _, _ -> +// dismiss() +// setResult(false) +// } +// .create() + + private fun setResult(result: Boolean) { + findNavController().previousBackStackEntry?.savedStateHandle?.set( + CONFIRMATION_RESULT, + result + ) + findNavController().popBackStack() + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/ru/otus/cookbook/ui/NormalizeAndRoundedCornersTransformation.kt b/app/src/main/kotlin/ru/otus/cookbook/ui/NormalizeAndRoundedCornersTransformation.kt new file mode 100644 index 0000000..6b141f0 --- /dev/null +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/NormalizeAndRoundedCornersTransformation.kt @@ -0,0 +1,143 @@ +package ru.otus.cookbook.ui + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PorterDuff +import android.graphics.RectF +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation +import java.security.MessageDigest +import kotlin.math.min + + +class NormalizeAndRoundedCornersTransformation( + private val context: Context, // Контекст для доступа к ресурсам + private val radius: Int, // Радиус скругления в dp + private val margin: Int, // Отступ в dp + private val cornerType: CornerType +) : BitmapTransformation() { + + enum class CornerType { + ALL, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, LEFT, RIGHT + } + + override fun transform( + pool: BitmapPool, + toTransform: Bitmap, + outWidth: Int, + outHeight: Int + ): Bitmap { + val normalized = normalizeImage(toTransform, outWidth, outHeight) + return roundedCorners(pool, normalized, outWidth, outHeight) + } + + private fun normalizeImage(source: Bitmap, outWidth: Int, outHeight: Int): Bitmap { + val sourceWidth = source.width + val sourceHeight = source.height + + // Рассчитываем соотношения сторон + val widthRatio = outWidth.toFloat() / sourceWidth + val heightRatio = outHeight.toFloat() / sourceHeight + + // Выбираем наименьшее соотношение, чтобы сохранить пропорции + val scaleFactor = min(widthRatio, heightRatio) + + // Вычисляем новые размеры + val targetWidth = (sourceWidth * scaleFactor).toInt() + val targetHeight = (sourceHeight * scaleFactor).toInt() + + // Масштабируем изображение с учетом пропорций + return Bitmap.createScaledBitmap(source, targetWidth, targetHeight, true) + } + + private fun roundedCorners(pool: BitmapPool, source: Bitmap, width: Int, height: Int): Bitmap { + var output = pool[width, height, Bitmap.Config.ARGB_8888] + if (output == null) { + output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + } + + val canvas = Canvas(output) + val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG or Paint.FILTER_BITMAP_FLAG) + paint.isAntiAlias = true + + // Преобразование радиуса и отступов из dp в пиксели + val density = context.resources.displayMetrics.density + val radiusPx = radius * density + val marginPx = margin * density + + // Определяем радиусы углов для каждого CornerType + val radii = when (cornerType) { + CornerType.ALL -> floatArrayOf( + radiusPx, radiusPx, // Top-left + radiusPx, radiusPx, // Top-right + radiusPx, radiusPx, // Bottom-right + radiusPx, radiusPx // Bottom-left + ) + CornerType.TOP_LEFT -> floatArrayOf( + radiusPx, radiusPx, // Top-left + 0f, 0f, // Top-right + 0f, 0f, // Bottom-right + 0f, 0f // Bottom-left + ) + CornerType.TOP_RIGHT -> floatArrayOf( + 0f, 0f, // Top-left + radiusPx, radiusPx, // Top-right + 0f, 0f, // Bottom-right + 0f, 0f // Bottom-left + ) + CornerType.BOTTOM_LEFT -> floatArrayOf( + 0f, 0f, // Top-left + 0f, 0f, // Top-right + 0f, 0f, // Bottom-right + radiusPx, radiusPx // Bottom-left + ) + CornerType.BOTTOM_RIGHT -> floatArrayOf( + 0f, 0f, // Top-left + 0f, 0f, // Top-right + radiusPx, radiusPx, // Bottom-right + 0f, 0f // Bottom-left + ) + CornerType.LEFT -> floatArrayOf( + radiusPx, radiusPx, // Top-left + 0f, 0f, // Top-right + 0f, 0f, // Bottom-right + radiusPx, radiusPx // Bottom-left + ) + CornerType.RIGHT -> floatArrayOf( + 0f, 0f, // Top-left + radiusPx, radiusPx, // Top-right + radiusPx, radiusPx, // Bottom-right + 0f, 0f // Bottom-left + ) + } + + // Создаем путь с учетом радиусов углов и отступов + val path = Path() + path.addRoundRect( + RectF( + marginPx, + marginPx, + width.toFloat() - marginPx, + height.toFloat() - marginPx + ), + radii, + Path.Direction.CW + ) + + // Очищаем холст и применяем клип + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) + canvas.clipPath(path) + canvas.drawBitmap(source, null, RectF(0f, 0f, width.toFloat(), height.toFloat()), paint) + + return output + } + + + override fun updateDiskCacheKey(messageDigest: MessageDigest) { + messageDigest.update(("normalized_rounded_" + radius + "_" + margin + "_" + cornerType).toByteArray(CHARSET)) + } +} diff --git a/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeAdapter.kt b/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeAdapter.kt new file mode 100644 index 0000000..31ba089 --- /dev/null +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeAdapter.kt @@ -0,0 +1,85 @@ +package ru.otus.cookbook.ui + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import ru.otus.cookbook.R +import ru.otus.cookbook.data.RecipeListItem +import ru.otus.cookbook.databinding.VhRecipeCategoryBinding +import ru.otus.cookbook.databinding.VhRecipeItemBinding + +class RecipeAdapter( + private val context: Context, + private val onRecipeClick: (Int) -> Unit +) : ListAdapter(RecipeDiffCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + RECIPE_TYPE -> { + val binding = VhRecipeItemBinding.inflate(LayoutInflater.from(context), parent, false) + RecipeViewHolder(binding) + } + CATEGORY_TYPE -> { + val binding = VhRecipeCategoryBinding.inflate(LayoutInflater.from(context), parent, false) + CategoryViewHolder(binding) + } + else -> throw IllegalArgumentException(context.getString(R.string.unknown_view_type)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val item = getItem(position)) { + is RecipeListItem.RecipeItem -> (holder as RecipeViewHolder).bind(item) + is RecipeListItem.CategoryItem -> (holder as CategoryViewHolder).bind(item) + } + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is RecipeListItem.RecipeItem -> RECIPE_TYPE + is RecipeListItem.CategoryItem -> CATEGORY_TYPE + } + } + + inner class RecipeViewHolder(private val binding: VhRecipeItemBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: RecipeListItem.RecipeItem) { + binding.firstCaps.text = item.title.firstOrNull()?.toString() ?: "" + binding.title.text = item.title + binding.description.text = item.description + Glide.with(binding.root.context) + .load(item.imageUrl) + .transform(NormalizeAndRoundedCornersTransformation(context,16, 0, NormalizeAndRoundedCornersTransformation.CornerType.RIGHT)) + .into(binding.image) + binding.root.setOnClickListener { onRecipeClick(item.id) } + } + } + + inner class CategoryViewHolder(private val binding: VhRecipeCategoryBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: RecipeListItem.CategoryItem) { + binding.categoryName.text = item.name + } + } + + companion object { + const val RECIPE_TYPE = 0 + const val CATEGORY_TYPE = 1 + } +} + +class RecipeDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: RecipeListItem, newItem: RecipeListItem): Boolean { + return when { + oldItem is RecipeListItem.RecipeItem && newItem is RecipeListItem.RecipeItem -> oldItem.id == newItem.id + oldItem is RecipeListItem.CategoryItem && newItem is RecipeListItem.CategoryItem -> oldItem.name == newItem.name + else -> false + } + } + + override fun areContentsTheSame(oldItem: RecipeListItem, newItem: RecipeListItem): Boolean { + return oldItem == newItem + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt b/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt index e4460c1..e807233 100644 --- a/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt @@ -1,21 +1,29 @@ package ru.otus.cookbook.ui import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.viewmodel.MutableCreationExtras +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.bumptech.glide.Glide import kotlinx.coroutines.launch +import ru.otus.cookbook.R import ru.otus.cookbook.data.Recipe import ru.otus.cookbook.databinding.FragmentRecipeBinding class RecipeFragment : Fragment() { - private val recipeId: Int get() = TODO("Use Safe Args to get the recipe ID: https://developer.android.com/guide/navigation/use-graph/pass-data#Safe-args") + private val args: RecipeFragmentArgs by navArgs() + private val recipeId: Int get() = args.recipeId // Теперь у вас есть recipeId //= TODO("Use Safe Args to get the recipe ID: https://developer.android.com/guide/navigation/use-graph/pass-data#Safe-args") private val binding = FragmentBindingDelegate(this) private val model: RecipeFragmentViewModel by viewModels( @@ -43,6 +51,61 @@ class RecipeFragment : Fragment() { .flowWithLifecycle(viewLifecycleOwner.lifecycle) .collect(::displayRecipe) } + binding.withBinding { + btnBack.setOnClickListener({ + findNavController().navigateUp() + }) + btnDelete.setOnClickListener({ +// val dialog = MaterialAlertDialogBuilder(requireContext()) +// .setTitle("Delete Recipe " + getTitle()) +// .setMessage("Are you sure you want to delete this recipe?") +// .setPositiveButton("OK") { dialogInterface, _ -> +// deleteRecipe() +// dialogInterface.dismiss() // Закрывает диалог явно +// findNavController().navigateUp() +// } +// .setNegativeButton("Cancel") { dialogInterface, _ -> +// dialogInterface.dismiss() // Закрывает диалог явно +// } +// .create() +// +// dialog.show() + + val argTitle = getTitle() + val action = RecipeFragmentDirections + .actionRecipeFragmentToDeleteRecipeDialogFragment(argTitle) + findNavController().navigate(action) + +// val options = NavOptions.Builder() +// .setLaunchSingleTop(true) +// .build() +// findNavController().navigate(action, options) + + }) + } + +// findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData(DeleteRecipeDialogFragment.DELETE_RECIPE_RESULT)?.observe(viewLifecycleOwner) { +// isDeleted -> +// if (isDeleted == true) { +// lifecycleScope.launch { +// deleteRecipe() +// findNavController().navigateUp() +// } +// } +// } + +// findNavController().currentBackStackEntry?.savedStateHandle +// ?.getLiveData(DeleteRecipeDialogFragment.DELETE_RECIPE_RESULT) +// ?.observe(viewLifecycleOwner) { isDeleted -> +// Log.d("RecipeFragment", "Result received: isDeleted = $isDeleted") +// if (isDeleted == true) { +// lifecycleScope.launch { +// deleteRecipe() +// findNavController().navigateUp() +// } +// } +// } + setupAlertResult() } /** @@ -52,11 +115,56 @@ class RecipeFragment : Fragment() { return model.recipe.value.title } - private fun displayRecipe(recipe: Recipe) { + private fun displayRecipe(recipe: Recipe) = binding.withBinding { // Display the recipe + tvTitle.text = recipe.title + // Устанавливаем заголовок рецепта + detailTitle.text = recipe.title + // Устанавливаем категорию рецепта + detailShortDescription.text = recipe.category.name + // Устанавливаем описание рецепта + detailFullDescription.text = recipe.description + // Загружаем изображение рецепта + Glide.with(this@RecipeFragment) + .load(recipe.imageUrl) + //.placeholder(R.drawable.placeholder_image) // Плейсхолдер на случай загрузки + //.error(R.drawable.error_image) // Изображение на случай ошибки + .transform(NormalizeAndRoundedCornersTransformation( + requireContext(),16, 0, + NormalizeAndRoundedCornersTransformation.CornerType.ALL)) + .into(detailImage) } private fun deleteRecipe() { model.delete() } + + + /** + * Sets up alert dialog for delete result. + * https://developer.android.com/guide/navigation/use-graph/programmatic#returning_a_result + */ + private fun setupAlertResult() { + val navBackStackEntry = findNavController().getBackStackEntry(R.id.recipeFragment) + + val observer = object : DefaultLifecycleObserver { + override fun onResume(owner: LifecycleOwner) { + if (navBackStackEntry.savedStateHandle.contains(DeleteRecipeDialogFragment.CONFIRMATION_RESULT)) { + if (true == navBackStackEntry.savedStateHandle.get(DeleteRecipeDialogFragment.CONFIRMATION_RESULT)) { + deleteRecipe() + findNavController().popBackStack() + } + navBackStackEntry.savedStateHandle.remove(DeleteRecipeDialogFragment.CONFIRMATION_RESULT) + } + } + } + + navBackStackEntry.lifecycle.addObserver(observer) + + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + navBackStackEntry.lifecycle.removeObserver(observer) + } + }) + } } \ No newline at end of file diff --git a/app/src/main/res/anim/enter_animation.xml b/app/src/main/res/anim/enter_animation.xml new file mode 100644 index 0000000..93562e7 --- /dev/null +++ b/app/src/main/res/anim/enter_animation.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_animation.xml b/app/src/main/res/anim/exit_animation.xml new file mode 100644 index 0000000..5b49a38 --- /dev/null +++ b/app/src/main/res/anim/exit_animation.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/pop_enter_animation.xml b/app/src/main/res/anim/pop_enter_animation.xml new file mode 100644 index 0000000..30d0674 --- /dev/null +++ b/app/src/main/res/anim/pop_enter_animation.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/pop_exit_animation.xml b/app/src/main/res/anim/pop_exit_animation.xml new file mode 100644 index 0000000..d97d5b9 --- /dev/null +++ b/app/src/main/res/anim/pop_exit_animation.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_filled_right_rounded_coners_16.xml b/app/src/main/res/drawable/bg_filled_right_rounded_coners_16.xml new file mode 100644 index 0000000..35c72a9 --- /dev/null +++ b/app/src/main/res/drawable/bg_filled_right_rounded_coners_16.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_filled_rounded_coners_50perc.xml b/app/src/main/res/drawable/bg_filled_rounded_coners_50perc.xml new file mode 100644 index 0000000..abdbb49 --- /dev/null +++ b/app/src/main/res/drawable/bg_filled_rounded_coners_50perc.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_transparent_rounded_coners_16.xml b/app/src/main/res/drawable/bg_transparent_rounded_coners_16.xml new file mode 100644 index 0000000..7d76479 --- /dev/null +++ b/app/src/main/res/drawable/bg_transparent_rounded_coners_16.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 0000000..71621ee --- /dev/null +++ b/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..23a67da --- /dev/null +++ b/app/src/main/res/drawable/ic_close.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..7052a55 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..bb6d429 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 86a5d97..4758168 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,13 +7,15 @@ android:layout_height="match_parent" tools:context=".MainActivity"> - + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + android:name="androidx.navigation.fragment.NavHostFragment" + app:navGraph="@navigation/cookbook_nav_graph" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_cookbook.xml b/app/src/main/res/layout/fragment_cookbook.xml index 77d9ef6..e22a367 100644 --- a/app/src/main/res/layout/fragment_cookbook.xml +++ b/app/src/main/res/layout/fragment_cookbook.xml @@ -1,6 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recipe.xml b/app/src/main/res/layout/fragment_recipe.xml index 77d9ef6..cc7a94b 100644 --- a/app/src/main/res/layout/fragment_recipe.xml +++ b/app/src/main/res/layout/fragment_recipe.xml @@ -1,6 +1,122 @@ - + android:layout_height="match_parent" + android:padding="0dp"> + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/vh_recipe_category.xml b/app/src/main/res/layout/vh_recipe_category.xml index 006fd49..4db4997 100644 --- a/app/src/main/res/layout/vh_recipe_category.xml +++ b/app/src/main/res/layout/vh_recipe_category.xml @@ -1,6 +1,21 @@ - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/vh_recipe_item.xml b/app/src/main/res/layout/vh_recipe_item.xml index 006fd49..704d9bc 100644 --- a/app/src/main/res/layout/vh_recipe_item.xml +++ b/app/src/main/res/layout/vh_recipe_item.xml @@ -1,6 +1,67 @@ - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/cookbook_nav_graph.xml b/app/src/main/res/navigation/cookbook_nav_graph.xml new file mode 100644 index 0000000..f55de43 --- /dev/null +++ b/app/src/main/res/navigation/cookbook_nav_graph.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index d5b075a..5e24003 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,7 +1,19 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c8524cd..768dc5c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,4 +2,23 @@ #FF000000 #FFFFFFFF + #FFFF0000 + + + #65558F + #EADDFF + #000000 + + #F3EDF7 + #EADDFF + #a0a0a0 + + + #1f2a36 + #0c1520 + #ffffff + + #1a1a1a + #a0a0a0 + #ffffff \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fce1d1..0e70a94 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,24 @@ Cookbook + Recipe image + Unknown view type + Close + Cookbook + Search + First Caps + BC + Title + Description + Full Description + Recipe + Back + Close Application + Are you sure you want to exit? + Yes + No + Query: %1$s + Cancel + OK + Are you sure you want to delete %1$s? + Delete \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 47d6575..1708edb 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -3,7 +3,22 @@ +