diff --git a/mvi-arch/build.gradle b/mvi-arch/build.gradle index bfaa0aa..44b9301 100644 --- a/mvi-arch/build.gradle +++ b/mvi-arch/build.gradle @@ -7,10 +7,13 @@ dependencies { implementation project(":kotlin-extensions") implementation project(":logging") implementation project(":utils") + implementation project(":views") implementation("androidx.core:core-ktx") implementation("androidx.appcompat:appcompat") + implementation("com.google.android.material:material") + implementation("androidx.fragment:fragment") implementation("androidx.fragment:fragment-ktx") @@ -31,6 +34,7 @@ dependencies { def lifecycleVersion = "2.2.0" def coroutinesVersion = "1.3.7" def daggerVersion = "2.27" + def materialDesignVersion = "1.2.0-rc01" constraints { implementation("androidx.core:core-ktx") { @@ -43,6 +47,11 @@ dependencies { require("1.0.2") } } + implementation("com.google.android.material:material") { + version { + require(materialDesignVersion) + } + } implementation("androidx.fragment:fragment") { version { require(fragmentVersion) diff --git a/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/FullscreenBottomSheetDialog.kt b/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/FullscreenBottomSheetDialog.kt new file mode 100644 index 0000000..5cc70cd --- /dev/null +++ b/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/FullscreenBottomSheetDialog.kt @@ -0,0 +1,111 @@ +package ru.touchin.roboswag.mvi_arch.core + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.os.Parcelable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.activity.OnBackPressedCallback +import androidx.annotation.LayoutRes +import androidx.annotation.MainThread +import androidx.core.os.bundleOf +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import ru.touchin.mvi_arch.R +import ru.touchin.roboswag.mvi_arch.di.ViewModelAssistedFactory +import ru.touchin.roboswag.mvi_arch.di.ViewModelFactory +import ru.touchin.roboswag.mvi_arch.marker.ViewAction +import ru.touchin.roboswag.mvi_arch.marker.ViewState +import javax.inject.Inject + +abstract class FullscreenBottomSheetDialog( + @LayoutRes private val layoutId: Int +) : BottomSheetDialogFragment(), IMvi + where NavArgs : Parcelable, + Action : ViewAction, + State : ViewState, + VM : MviViewModel { + + @Inject + lateinit var viewModelMap: MutableMap, ViewModelAssistedFactory> + + protected lateinit var state: NavArgs + + protected abstract fun injectDependencies() + + @MainThread + protected inline fun viewModel(): Lazy = + lazy { + val fragmentArguments = arguments ?: bundleOf() + + ViewModelProvider( + viewModelStore, + ViewModelFactory(viewModelMap, this, fragmentArguments) + ).get(ViewModel::class.java) + } + + private val onShowListener by lazy { + DialogInterface.OnShowListener { dialog -> + (dialog as BottomSheetDialog).findViewById(com.google.android.material.R.id.design_bottom_sheet) + ?.let { BottomSheetBehavior.from(it) } + ?.apply { + state = BottomSheetBehavior.STATE_EXPANDED + skipCollapsed = true + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NORMAL, R.style.RoundedCornersBottomSheetDialogTheme) + (savedInstanceState ?: arguments)?.getParcelable(MviFragment.INIT_ARGS_KEY)?.let { savedState -> + state = savedState + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + inflater.inflate(layoutId, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.state.observe(viewLifecycleOwner, Observer(this::renderState)) + } + + override fun onAttach(context: Context) { + super.onAttach(context) + injectDependencies() + } + + override fun onStart() { + super.onStart() + + val bottomSheet = dialog?.findViewById(com.google.android.material.R.id.design_bottom_sheet) + bottomSheet?.layoutParams?.height = ViewGroup.LayoutParams.MATCH_PARENT + } + + override fun addOnBackPressedCallback(action: Action) { + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + dispatchAction(action) + } + }) + } + + override fun setupDialog(dialog: Dialog, style: Int) { + dialog.setOnShowListener(onShowListener) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putParcelable(MviFragment.INIT_ARGS_KEY, state) + } + +} diff --git a/views/src/main/res/values/styles.xml b/views/src/main/res/values/styles.xml index 06eee69..35bfcf3 100644 --- a/views/src/main/res/values/styles.xml +++ b/views/src/main/res/values/styles.xml @@ -17,4 +17,14 @@ @font/roboto + + + +