diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 50816f1..a78afd6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ plugins { android { defaultConfig { applicationId = "gq.kirmanak.mealient" - versionCode = 17 - versionName = "0.3.2" + versionCode = 18 + versionName = "0.3.3" } signingConfigs { diff --git a/app/src/main/java/gq/kirmanak/mealient/extensions/ViewExtensions.kt b/app/src/main/java/gq/kirmanak/mealient/extensions/ViewExtensions.kt index 25c5fba..452b2c9 100644 --- a/app/src/main/java/gq/kirmanak/mealient/extensions/ViewExtensions.kt +++ b/app/src/main/java/gq/kirmanak/mealient/extensions/ViewExtensions.kt @@ -2,10 +2,13 @@ package gq.kirmanak.mealient.extensions import android.content.Context import android.content.SharedPreferences +import android.view.View +import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.TextView import android.widget.Toast import androidx.annotation.StringRes +import androidx.core.content.getSystemService import androidx.core.widget.doAfterTextChanged import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData @@ -107,3 +110,8 @@ fun Context.showLongToast(@StringRes text: Int) = showLongToast(getString(text)) private fun Context.showToast(text: String, length: Int) { Toast.makeText(this, text, length).show() } + +fun View.hideKeyboard() { + val imm = context.getSystemService() + imm?.hideSoftInputFromWindow(windowToken, 0) +} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt index 39dc6bb..63270ce 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt @@ -123,7 +123,13 @@ class MainActivity : AppCompatActivity(R.layout.main_activity) { logger.e { "setupSearchItem: search item's actionView is null or not SearchView" } return } + searchView.queryHint = getString(R.string.search_recipes_hint) + searchView.isSubmitButtonEnabled = false + + searchView.setQuery(viewModel.lastSearchQuery, false) + searchView.isIconified = viewModel.lastSearchQuery.isNullOrBlank() + searchView.setOnCloseListener { logger.v { "onClose() called" } viewModel.onSearchQuery(null) diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt index 73ab467..cb910bb 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt @@ -32,6 +32,9 @@ class MainActivityViewModel @Inject constructor( private val _startDestination = MutableLiveData() val startDestination: LiveData = _startDestination + var lastSearchQuery: String? = null + private set + init { authRepo.isAuthorizedFlow .onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } } @@ -57,6 +60,9 @@ class MainActivityViewModel @Inject constructor( fun onSearchQuery(query: String?) { logger.v { "onSearchQuery() called with: query = $query" } - recipeRepo.updateNameQuery(query) + if (lastSearchQuery != query) { + lastSearchQuery = query + recipeRepo.updateNameQuery(query) + } } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/add/AddRecipeFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/add/AddRecipeFragment.kt index 2da5a36..d311d68 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/add/AddRecipeFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/add/AddRecipeFragment.kt @@ -127,9 +127,8 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) { private fun saveValues() = with(binding) { logger.v { "saveValues() called" } - val instructions = - parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(text = it) } - val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(note = it) } + val instructions = parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(it) } + val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(it) } val settings = AddRecipeSettingsInfo( public = publicRecipe.isChecked, disableComments = disableComments.isChecked, @@ -156,17 +155,18 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) { private fun onSavedInputLoaded(request: AddRecipeInfo) = with(binding) { logger.v { "onSavedInputLoaded() called with: request = $request" } - recipeNameInput.setText(request.name) - recipeDescriptionInput.setText(request.description) - recipeYieldInput.setText(request.recipeYield) - publicRecipe.isChecked = request.settings.public - disableComments.isChecked = request.settings.disableComments request.recipeIngredient.map { it.note } .showIn(ingredientsFlow, R.string.fragment_add_recipe_ingredient_hint) request.recipeInstructions.map { it.text } .showIn(instructionsFlow, R.string.fragment_add_recipe_instruction_hint) + + recipeNameInput.setText(request.name) + recipeDescriptionInput.setText(request.description) + recipeYieldInput.setText(request.recipeYield) + publicRecipe.isChecked = request.settings.public + disableComments.isChecked = request.settings.disableComments } private fun Iterable.showIn(flow: Flow, @StringRes hintId: Int) { diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt index 019d099..4752876 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt @@ -1,5 +1,6 @@ package gq.kirmanak.mealient.ui.recipes +import android.annotation.SuppressLint import android.os.Bundle import android.view.View import androidx.annotation.StringRes @@ -17,10 +18,7 @@ import gq.kirmanak.mealient.R import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity import gq.kirmanak.mealient.databinding.FragmentRecipesListBinding import gq.kirmanak.mealient.datasource.NetworkError -import gq.kirmanak.mealient.extensions.collectWhenViewResumed -import gq.kirmanak.mealient.extensions.refreshRequestFlow -import gq.kirmanak.mealient.extensions.showLongToast -import gq.kirmanak.mealient.extensions.valueUpdatesOnly +import gq.kirmanak.mealient.extensions.* import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.ui.activity.MainActivityViewModel import gq.kirmanak.mealient.ui.recipes.RecipesListFragmentDirections.Companion.actionRecipesFragmentToRecipeInfoFragment @@ -59,6 +57,15 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) { ) } setupRecipeAdapter() + hideKeyboardOnScroll() + } + + @SuppressLint("ClickableViewAccessibility") + private fun hideKeyboardOnScroll() { + binding.recipes.setOnTouchListener { view, _ -> + view?.hideKeyboard() + false + } } private fun navigateToRecipeInfo(id: String) { @@ -70,11 +77,9 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) { private fun onRecipeClicked(recipe: RecipeSummaryEntity) { logger.v { "onRecipeClicked() called with: recipe = $recipe" } binding.progress.isVisible = true - viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) { result -> + viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) { binding.progress.isVisible = false - if (result.isSuccess && !isNavigatingSomewhere()) { - navigateToRecipeInfo(recipe.remoteId) - } + if (!isNavigatingSomewhere()) navigateToRecipeInfo(recipe.remoteId) } } diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index eb4254b..cb5ca08 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -63,7 +63,8 @@ + app:destination="@id/authenticationFragment" + app:popUpTo="@id/recipesListFragment" /> + app:destination="@id/addRecipeFragment" + app:popUpTo="@id/recipesListFragment" /> + app:destination="@id/baseURLFragment" + app:popUpTo="@id/recipesListFragment" /> \ No newline at end of file diff --git a/app/src/test/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModelTest.kt b/app/src/test/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModelTest.kt index d050856..e069e6d 100644 --- a/app/src/test/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModelTest.kt +++ b/app/src/test/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModelTest.kt @@ -4,7 +4,9 @@ import gq.kirmanak.mealient.data.auth.AuthRepo import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage import gq.kirmanak.mealient.data.recipes.RecipeRepo +import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL import gq.kirmanak.mealient.test.BaseUnitTest +import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.verify @@ -32,6 +34,8 @@ class MainActivityViewModelTest : BaseUnitTest() { override fun setUp() { super.setUp() every { authRepo.isAuthorizedFlow } returns emptyFlow() + coEvery { disclaimerStorage.isDisclaimerAccepted() } returns true + coEvery { serverInfoRepo.getUrl() } returns TEST_BASE_URL subject = MainActivityViewModel( authRepo = authRepo, logger = logger, @@ -49,6 +53,7 @@ class MainActivityViewModelTest : BaseUnitTest() { @Test fun `when onSearchQuery with null expect call to recipe repo`() { + subject.onSearchQuery("query") subject.onSearchQuery(null) verify { recipeRepo.updateNameQuery(null) } }