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..61e8daf --- /dev/null +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/DeleteRecipeDialogFragment.kt @@ -0,0 +1,45 @@ +package ru.otus.cookbook.ui + +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" + } + + 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) { _, _ -> + dismiss() + setResult(true) + } + .setNegativeButton(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..2e8e404 100644 --- a/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt +++ b/app/src/main/kotlin/ru/otus/cookbook/ui/RecipeFragment.kt @@ -6,16 +6,23 @@ 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 // ("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 +50,18 @@ class RecipeFragment : Fragment() { .flowWithLifecycle(viewLifecycleOwner.lifecycle) .collect(::displayRecipe) } + binding.withBinding { + btnBack.setOnClickListener({ + findNavController().navigateUp() + }) + btnDelete.setOnClickListener({ + val argTitle = getTitle() + val action = RecipeFragmentDirections + .actionRecipeFragmentToDeleteRecipeDialogFragment(argTitle) + findNavController().navigate(action) + }) + } + setupAlertResult() } /** @@ -52,11 +71,54 @@ 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) + .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..d264281 100644 --- a/app/src/main/res/layout/fragment_cookbook.xml +++ b/app/src/main/res/layout/fragment_cookbook.xml @@ -1,6 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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..4ddc68d 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..55b4840 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 + + + #EADDFF + #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..8abf17e 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,9 +1,24 @@ - + +