Skip to content

Commit

Permalink
Merge pull request #229 from aivanovski/feature/fix-lifecycle-propaga…
Browse files Browse the repository at this point in the history
…tion-in-view-models-adapter

Fix lifecycle propagation for ViewModelsAdapter
  • Loading branch information
aivanovski authored Feb 8, 2024
2 parents fbe643a + a901a5e commit ee8a858
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
Expand All @@ -35,7 +34,6 @@ import com.ivanovsky.passnotes.presentation.core.ScreenState
import com.ivanovsky.passnotes.presentation.core.ScreenStateHandler
import com.ivanovsky.passnotes.presentation.core.ViewModelTypes
import com.ivanovsky.passnotes.presentation.core.adapter.StringArraySpinnerAdapter
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.widget.CellLinearLayout
import com.ivanovsky.passnotes.presentation.core.widget.ErrorPanelView
import com.ivanovsky.passnotes.presentation.core.widget.SecureTextView
Expand All @@ -52,7 +50,6 @@ import com.ivanovsky.passnotes.presentation.core.widget.entity.TextInputType
import com.ivanovsky.passnotes.presentation.core.widget.entity.TextTransformationMethod
import com.ivanovsky.passnotes.presentation.core.widget.entity.TextTransformationMethod.PASSWORD
import com.ivanovsky.passnotes.presentation.core.widget.entity.TextTransformationMethod.PLANE_TEXT
import com.ivanovsky.passnotes.util.getLifecycleOwner

@BindingAdapter("screenState", "screenStateHandler")
fun setScreenState(
Expand All @@ -65,28 +62,6 @@ fun setScreenState(
screenStateHandler.applyScreenState(view, screenState)
}

@BindingAdapter("viewModels", "viewTypes")
fun setViewModel(
recyclerView: RecyclerView,
viewModelsData: List<BaseCellViewModel>?,
viewTypes: ViewModelTypes
) {
val lifecycleOwner = recyclerView.context.getLifecycleOwner()
?: throw IllegalStateException("context doesn't have LifecycleOwner")

val adapter = (recyclerView.adapter as? ViewModelsAdapter)
?: ViewModelsAdapter(
lifecycleOwner,
viewTypes
).also {
recyclerView.adapter = it
}

viewModelsData?.let { viewModels ->
adapter.updateItems(viewModels)
}
}

@BindingAdapter("viewModels", "viewTypes")
fun setViewModels(
view: CellLinearLayout,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ivanovsky.passnotes.presentation.core.extensions

import androidx.recyclerview.widget.RecyclerView
import com.ivanovsky.passnotes.presentation.core.BaseCellViewModel
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter

fun RecyclerView.setViewModels(
viewModels: List<BaseCellViewModel>
) {
(adapter as ViewModelsAdapter).updateItems(viewModels)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import androidx.lifecycle.ViewModelProvider
import com.ivanovsky.passnotes.R
import com.ivanovsky.passnotes.databinding.DiffViewerFragmentBinding
import com.ivanovsky.passnotes.presentation.core.DatabaseInteractionWatcher
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.extensions.getMandatoryArgument
import com.ivanovsky.passnotes.presentation.core.extensions.setViewModels
import com.ivanovsky.passnotes.presentation.core.extensions.setupActionBar
import com.ivanovsky.passnotes.presentation.core.extensions.withArguments

Expand All @@ -29,18 +31,27 @@ class DiffViewerFragment : Fragment() {
)[DiffViewerViewModel::class.java]
}

private lateinit var binding: DiffViewerFragmentBinding

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = DiffViewerFragmentBinding.inflate(inflater, container, false)
binding = DiffViewerFragmentBinding.inflate(inflater, container, false)
.also {
it.lifecycleOwner = viewLifecycleOwner
it.viewModel = viewModel
}

binding.recyclerView.itemAnimator = null
binding.recyclerView
.apply {
adapter = ViewModelsAdapter(
lifecycleOwner = viewLifecycleOwner,
viewTypes = viewModel.viewTypes
)
itemAnimator = null
}

return binding.root
}
Expand All @@ -57,6 +68,8 @@ class DiffViewerFragment : Fragment() {
setHomeAsUpIndicator(null)
}

subscribeToLiveData()

viewModel.start()
}

Expand All @@ -66,6 +79,12 @@ class DiffViewerFragment : Fragment() {
return true
}

private fun subscribeToLiveData() {
viewModel.cellViewModels.observe(viewLifecycleOwner) { viewModels ->
binding.recyclerView.setViewModels(viewModels)
}
}

companion object {

private const val ARGUMENTS = "arguments"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import com.ivanovsky.passnotes.domain.PermissionHelper
import com.ivanovsky.passnotes.domain.entity.SystemPermission
import com.ivanovsky.passnotes.injection.GlobalInjector.inject
import com.ivanovsky.passnotes.presentation.core.FragmentWithDoneButton
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.dialog.AllFilesPermissionDialog
import com.ivanovsky.passnotes.presentation.core.extensions.getMandatoryArgument
import com.ivanovsky.passnotes.presentation.core.extensions.requestSystemPermission
import com.ivanovsky.passnotes.presentation.core.extensions.setViewModels
import com.ivanovsky.passnotes.presentation.core.extensions.setupActionBar
import com.ivanovsky.passnotes.presentation.core.extensions.showSnackbarMessage
import com.ivanovsky.passnotes.presentation.core.extensions.withArguments
Expand All @@ -39,6 +41,7 @@ class FilePickerFragment :
}
private val permissionHelper: PermissionHelper by inject()
private val args by lazy { getMandatoryArgument<FilePickerArgs>(ARGUMENTS) }
private lateinit var binding: FilePickerFragmentBinding

override fun onPermissionRequestResult(permission: SystemPermission, isGranted: Boolean) {
when (permission) {
Expand Down Expand Up @@ -79,12 +82,18 @@ class FilePickerFragment :
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FilePickerFragmentBinding.inflate(inflater)
binding = FilePickerFragmentBinding.inflate(inflater)
.also {
it.lifecycleOwner = viewLifecycleOwner
it.viewModel = viewModel
}
.root

binding.recyclerView.adapter = ViewModelsAdapter(
lifecycleOwner = viewLifecycleOwner,
viewTypes = viewModel.viewTypes
)

return binding.root
}

override fun onDoneMenuClicked() {
Expand All @@ -111,9 +120,17 @@ class FilePickerFragment :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

subscribeToLiveData()
subscribeToEvents()
}

private fun subscribeToLiveData() {
viewModel.cellViewModels.observe(viewLifecycleOwner) { viewModels ->
binding.recyclerView.setViewModels(viewModels)
}
}

private fun subscribeToEvents() {
viewModel.isDoneButtonVisible.observe(viewLifecycleOwner) { isVisible ->
setDoneButtonVisibility(isVisible)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import com.ivanovsky.passnotes.presentation.ApplicationLaunchMode
import com.ivanovsky.passnotes.presentation.autofill.model.AutofillStructure
import com.ivanovsky.passnotes.presentation.core.BaseFragment
import com.ivanovsky.passnotes.presentation.core.ThemeProvider
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.extensions.getMandatoryExtra
import com.ivanovsky.passnotes.presentation.core.extensions.initActionBar
import com.ivanovsky.passnotes.presentation.core.extensions.setViewModels
import com.ivanovsky.passnotes.presentation.core.permission.PermissionRequestResultReceiver
import com.ivanovsky.passnotes.presentation.core.permission.PermissionRequestSender
import com.ivanovsky.passnotes.presentation.main.ActivityResultManager.LauncherType
Expand Down Expand Up @@ -94,6 +96,11 @@ class MainActivity :
it.navigationViewModel = navigationViewModel
}

binding.navigationRecyclerView.adapter = ViewModelsAdapter(
lifecycleOwner = this,
viewTypes = navigationViewModel.cellViewTypes
)

setContentView(binding.root)
initActionBar(R.id.toolbar)

Expand Down Expand Up @@ -170,6 +177,9 @@ class MainActivity :
}

private fun subscribeToLiveData() {
navigationViewModel.cellViewModels.observe(this) { viewModels ->
binding.navigationRecyclerView.setViewModels(viewModels)
}
navigationViewModel.isNavigationMenuEnabled.observe(this) {
setDrawerEnabled(it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import com.github.terrakok.cicerone.Router
import com.ivanovsky.passnotes.R
import com.ivanovsky.passnotes.data.entity.Note
Expand All @@ -20,11 +19,13 @@ import com.ivanovsky.passnotes.presentation.Screens
import com.ivanovsky.passnotes.presentation.autofill.AutofillDialogFactory
import com.ivanovsky.passnotes.presentation.core.BaseFragment
import com.ivanovsky.passnotes.presentation.core.DatabaseInteractionWatcher
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.dialog.ConfirmationDialog
import com.ivanovsky.passnotes.presentation.core.extensions.finishActivity
import com.ivanovsky.passnotes.presentation.core.extensions.getMandatoryArgument
import com.ivanovsky.passnotes.presentation.core.extensions.openUrl
import com.ivanovsky.passnotes.presentation.core.extensions.sendAutofillResult
import com.ivanovsky.passnotes.presentation.core.extensions.setViewModels
import com.ivanovsky.passnotes.presentation.core.extensions.setupActionBar
import com.ivanovsky.passnotes.presentation.core.extensions.showSnackbarMessage
import com.ivanovsky.passnotes.presentation.core.extensions.updateMenuItemVisibility
Expand Down Expand Up @@ -52,6 +53,7 @@ class NoteFragment : BaseFragment() {
}
private val router: Router by inject()
private var menu: Menu? = null
private lateinit var binding: NoteFragmentBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -116,12 +118,18 @@ class NoteFragment : BaseFragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return NoteFragmentBinding.inflate(inflater, container, false)
binding = NoteFragmentBinding.inflate(inflater, container, false)
.also {
it.lifecycleOwner = viewLifecycleOwner
it.viewModel = viewModel
}
.root

binding.recyclerView.adapter = ViewModelsAdapter(
lifecycleOwner = viewLifecycleOwner,
viewTypes = viewModel.viewTypes
)

return binding.root
}

override fun onStart() {
Expand All @@ -140,6 +148,9 @@ class NoteFragment : BaseFragment() {
}

private fun subscribeToLiveData() {
viewModel.cellViewModels.observe(viewLifecycleOwner) { viewModels ->
binding.recyclerView.setViewModels(viewModels)
}
viewModel.visibleMenuItems.observe(viewLifecycleOwner) { visibleItems ->
menu?.let { menu ->
updateMenuItemVisibility(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.lifecycle.observe
import com.github.terrakok.cicerone.Router
import com.ivanovsky.passnotes.R
import com.ivanovsky.passnotes.databinding.NoteEditorFragmentBinding
Expand All @@ -16,10 +15,12 @@ import com.ivanovsky.passnotes.presentation.ApplicationLaunchMode
import com.ivanovsky.passnotes.presentation.Screens
import com.ivanovsky.passnotes.presentation.core.DatabaseInteractionWatcher
import com.ivanovsky.passnotes.presentation.core.FragmentWithDoneButton
import com.ivanovsky.passnotes.presentation.core.adapter.ViewModelsAdapter
import com.ivanovsky.passnotes.presentation.core.dialog.ConfirmationDialog
import com.ivanovsky.passnotes.presentation.core.extensions.getMandatoryArgument
import com.ivanovsky.passnotes.presentation.core.extensions.hideKeyboard
import com.ivanovsky.passnotes.presentation.core.extensions.requireArgument
import com.ivanovsky.passnotes.presentation.core.extensions.setViewModels
import com.ivanovsky.passnotes.presentation.core.extensions.setupActionBar
import com.ivanovsky.passnotes.presentation.core.extensions.showToastMessage
import com.ivanovsky.passnotes.presentation.core.extensions.withArguments
Expand All @@ -39,18 +40,25 @@ class NoteEditorFragment : FragmentWithDoneButton() {
)
private val router: Router by inject()
private var backCallback: OnBackPressedCallback? = null
private lateinit var binding: NoteEditorFragmentBinding

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return NoteEditorFragmentBinding.inflate(inflater, container, false)
binding = NoteEditorFragmentBinding.inflate(inflater, container, false)
.also {
it.lifecycleOwner = viewLifecycleOwner
it.viewModel = viewModel
}
.root

binding.recyclerView.adapter = ViewModelsAdapter(
lifecycleOwner = viewLifecycleOwner,
viewTypes = viewModel.viewTypes
)

return binding.root
}

override fun onDoneMenuClicked() {
Expand Down Expand Up @@ -103,6 +111,9 @@ class NoteEditorFragment : FragmentWithDoneButton() {
}

private fun subscribeToLiveData() {
viewModel.cellViewModels.observe(viewLifecycleOwner) { viewModels ->
binding.recyclerView.setViewModels(viewModels)
}
viewModel.isDoneButtonVisible.observe(viewLifecycleOwner) { isVisible ->
setDoneButtonVisibility(isVisible)
}
Expand Down
Loading

0 comments on commit ee8a858

Please sign in to comment.