Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ implement navigation between fragments #108

Merged
merged 14 commits into from
Jul 26, 2024
1 change: 1 addition & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
alias(libs.plugins.google.gms.google.services)
id("kotlin-kapt")
id("kotlin-parcelize")
id("androidx.navigation.safeargs.kotlin")
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MainActivity : AppCompatActivity() {

navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.homeFragment -> bottomNav.visibility = View.VISIBLE
R.id.homeFragment, R.id.searchFragment, R.id.profileFragment, R.id.categoryFragment -> bottomNav.visibility = View.VISIBLE
else -> bottomNav.visibility = View.GONE
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import net.pengcook.android.databinding.FragmentDetailRecipeBinding
import net.pengcook.android.presentation.core.model.Recipe

class DetailRecipeFragment : Fragment() {
private val args: DetailRecipeFragmentArgs by navArgs()
private val binding by lazy { FragmentDetailRecipeBinding.inflate(layoutInflater) }
private val viewModel by lazy { DetailRecipeViewModel(recipe) }
private lateinit var recipe: Recipe
private val recipe: Recipe by lazy { args.recipe }

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -25,14 +28,26 @@ class DetailRecipeFragment : Fragment() {
) {
super.onViewCreated(view, savedInstanceState)
fetchRecipe()
observeNavigationEvent()
}

private fun fetchRecipe() {
val argument = arguments?.getParcelable(RECIPE_KEY) as Recipe?
if (argument is Recipe) {
recipe = argument
private fun observeNavigationEvent() {
viewModel.navigateToStepEvent.observe(viewLifecycleOwner) { navigationEvent ->
val navigationAvailable = navigationEvent.getContentIfNotHandled() ?: return@observe
if (navigationAvailable) {
navigateToStep()
}
}
binding.recipe = recipe
}

private fun fetchRecipe() {
binding.recipe = args.recipe
binding.vm = viewModel
}

private fun navigateToStep() {
val action = DetailRecipeFragmentDirections.actionDetailRecipeFragmentToRecipeStepFragment()
findNavController().navigate(action)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package net.pengcook.android.presentation.detail

// DetailRecipeViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import net.pengcook.android.presentation.core.model.Recipe
import net.pengcook.android.presentation.core.util.Event

class DetailRecipeViewModel(
private val recipe: Recipe,
) : ViewModel()
) : ViewModel() {
private val _navigateToStepEvent = MutableLiveData<Event<Boolean>>()
val navigateToStepEvent: LiveData<Event<Boolean>> get() = _navigateToStepEvent

fun onNavigateToMakingStep() {
_navigateToStepEvent.value = Event(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import net.pengcook.android.databinding.ItemFeedBinding
import net.pengcook.android.presentation.core.model.Recipe
import net.pengcook.android.presentation.home.holder.FeedViewHolder
import net.pengcook.android.presentation.home.listener.FeedItemEventListener

class FeedRecyclerViewAdapter(private val eventListener: FeedItemEventListener) :
PagingDataAdapter<Recipe, FeedRecyclerViewAdapter.ViewHolder>(diffCallback) {
PagingDataAdapter<Recipe, FeedViewHolder>(diffCallback) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): ViewHolder {
): FeedViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemFeedBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
return FeedViewHolder(binding, eventListener)
}

override fun onBindViewHolder(
holder: ViewHolder,
holder: FeedViewHolder,
position: Int,
) {
val item = getItem(position)
Expand All @@ -30,13 +30,6 @@ class FeedRecyclerViewAdapter(private val eventListener: FeedItemEventListener)
}
}

class ViewHolder(private val binding: ItemFeedBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Recipe) {
binding.recipe = item
binding.executePendingBindings()
}
}

companion object {
val diffCallback =
object : DiffUtil.ItemCallback<Recipe>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -15,6 +16,7 @@ import net.pengcook.android.data.remote.api.FeedService
import net.pengcook.android.data.repository.feed.DefaultFeedRepository
import net.pengcook.android.data.util.network.RetrofitClient
import net.pengcook.android.databinding.FragmentHomeBinding
import net.pengcook.android.presentation.core.model.Recipe

class HomeFragment : Fragment() {
private val viewModel: HomeViewModel by viewModels {
Expand Down Expand Up @@ -49,7 +51,23 @@ class HomeFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)

initBinding()
observing()
}

private fun observing() {
observeFeedData()
observeUiEvent()
}

private fun observeUiEvent() {
viewModel.uiEvent.observe(viewLifecycleOwner) { event ->
val newEvent = event.getContentIfNotHandled() ?: return@observe
when (newEvent) {
is HomeEvent.NavigateToDetail -> {
onSingleMovieClicked(newEvent.recipe)
}
}
}
}

private fun observeFeedData() {
Expand All @@ -62,6 +80,11 @@ class HomeFragment : Fragment() {
}
}

private fun onSingleMovieClicked(recipe: Recipe) {
val action = HomeFragmentDirections.actionHomeFragmentToDetailRecipeFragment(recipe)
findNavController().navigate(action)
}

private fun initBinding() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.pengcook.android.presentation.home

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
Expand All @@ -11,11 +12,16 @@ import androidx.paging.liveData
import net.pengcook.android.data.datasource.FeedPagingSource
import net.pengcook.android.data.repository.feed.FeedRepository
import net.pengcook.android.presentation.core.model.Recipe
import net.pengcook.android.presentation.core.util.Event
import net.pengcook.android.presentation.home.listener.FeedItemEventListener

class HomeViewModel(
private val feedRepository: FeedRepository,
) : ViewModel(), FeedItemEventListener {
private val _uiEvent: MutableLiveData<Event<HomeEvent>> = MutableLiveData()
val uiEvent: LiveData<Event<HomeEvent>>
get() = _uiEvent

val feedData: LiveData<PagingData<Recipe>> =
Pager(
config = PagingConfig(pageSize = PAGE_SIZE, enablePlaceholders = false),
Expand All @@ -24,11 +30,15 @@ class HomeViewModel(
.liveData
.cachedIn(viewModelScope)

override fun onNavigateToDetail(recipe: Recipe) {
_uiEvent.value = Event(HomeEvent.NavigateToDetail(recipe))
}

companion object {
private const val PAGE_SIZE = 10
}
}

override fun onNavigateToDetail(recipe: Recipe) {
// Navigate to detail page
}
sealed interface HomeEvent {
data class NavigateToDetail(val recipe: Recipe) : HomeEvent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.pengcook.android.presentation.home.holder

import androidx.recyclerview.widget.RecyclerView
import net.pengcook.android.databinding.ItemFeedBinding
import net.pengcook.android.presentation.core.model.Recipe
import net.pengcook.android.presentation.home.listener.FeedItemEventListener

class FeedViewHolder(
private val binding: ItemFeedBinding,
private val eventListener: FeedItemEventListener,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Recipe) {
binding.recipe = item
binding.eventListener = eventListener
binding.executePendingBindings()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import net.pengcook.android.databinding.FragmentRecipeMakingBinding

class RecipeMakingFragment : Fragment() {
Expand All @@ -29,13 +30,24 @@ class RecipeMakingFragment : Fragment() {
) {
super.onViewCreated(view, savedInstanceState)
initBinding()
viewModel.uiEvent.observe(viewLifecycleOwner) { event ->
val newEvent = event.getContentIfNotHandled() ?: return@observe
when (newEvent) {
is MakingEvent.NavigateToMakingStep -> onNextClicked()
}
}
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

private fun onNextClicked() {
val action = RecipeMakingFragmentDirections.actionRecipeMakingFragmentToStepMakingFragment()
findNavController().navigate(action)
}

private fun initBinding() {
binding.lifecycleOwner = this
binding.vm = viewModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package net.pengcook.android.presentation.making

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import net.pengcook.android.presentation.core.util.Event
import net.pengcook.android.presentation.making.listener.RecipeMakingEventListener

class RecipeMakingViewModel : ViewModel(), RecipeMakingEventListener {
private val _uiEvent: MutableLiveData<Event<MakingEvent>> = MutableLiveData()
val uiEvent: LiveData<Event<MakingEvent>>
get() = _uiEvent

val titleContent = MutableLiveData<String>()

val categorySelectedValue = MutableLiveData<String>()
Expand All @@ -15,7 +21,11 @@ class RecipeMakingViewModel : ViewModel(), RecipeMakingEventListener {

val introductionContent = MutableLiveData<String>()

override fun onNavigateToStep() {
// TODO: Implement navigation logic
override fun onNavigateToMakingStep() {
_uiEvent.value = Event(MakingEvent.NavigateToMakingStep)
}
}

sealed interface MakingEvent {
data object NavigateToMakingStep : MakingEvent
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package net.pengcook.android.presentation.making.listener

interface RecipeMakingEventListener {
fun onNavigateToStep()
fun onNavigateToMakingStep()
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,4 @@ class RecipeStepFragment : Fragment() {
super.onDestroyView()
_binding = null
}

companion object {
fun newInstance(recipeId: Long): RecipeStepFragment {
val fragment = RecipeStepFragment()
val args = Bundle()
args.putLong("recipeId", recipeId)
fragment.arguments = args
return fragment
}
}
}
10 changes: 8 additions & 2 deletions android/app/src/main/res/layout/fragment_detail_recipe.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<variable
name="Recipe"
type="net.pengcook.android.presentation.core.model.Recipe" />

<variable
name="vm"
type="net.pengcook.android.presentation.detail.DetailRecipeViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
Expand Down Expand Up @@ -241,10 +245,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@{Recipe.cookingTime}"
android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_required_time_title"
android:text="@{Recipe.cookingTime}"
tools:text="60mins" />

<TextView
Expand All @@ -264,7 +268,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@{Recipe.difficulty}"
android:text="@{String.valueOf(Recipe.difficulty)}"
android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_difficulty_title"
Expand Down Expand Up @@ -294,11 +298,13 @@
tools:text="Hard to cook, but worth the effort" />

<TextView
android:id="@+id/tv_making_step_title"
android:layout_width="0dp"
android:layout_height="52dp"
android:layout_marginTop="20dp"
android:background="@drawable/shape_button_round_corner"
android:gravity="center"
android:onClick="@{() -> vm.onNavigateToMakingStep()}"
android:text="Start"
android:textColor="@color/black"
android:textSize="20sp"
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/res/layout/fragment_home.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/followingText"
app:layout_constraintVertical_bias="0.0"
tools:listitem="@layout/item_feed" />

</androidx.constraintlayout.widget.ConstraintLayout>
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/res/layout/fragment_recipe_making.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
android:backgroundTint="@color/gray_300"
android:text="@string/making_next"
android:textColor="@color/black_100"
android:onClick="@{() -> vm.onNavigateToMakingStep()}"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
Expand Down
Loading
Loading