From a63ec5f7538c57627a48d76fa2ed026fd8e2f53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9C?= =?UTF-8?q?=D1=83=D0=B4=D1=80=D1=8F=D0=BA=D0=BE=D0=B2?= Date: Fri, 20 May 2022 23:32:10 +0300 Subject: [PATCH 01/13] screen logic added --- .../presentation/fragment/BaseVMFragment.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt index c4223f1..51638ed 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt @@ -3,8 +3,10 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.content.Context import android.os.Bundle import android.view.View +import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding import androidx.lifecycle.ViewModel +import com.google.android.material.snackbar.Snackbar import com.merseyside.archy.presentation.fragment.BaseBindingFragment import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel @@ -19,6 +21,7 @@ import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlin.reflect.KClass + abstract class BaseVMFragment : BaseBindingFragment() { @@ -145,6 +148,22 @@ abstract class BaseVMFragment } } + fun showInteractSnack( + @StringRes text: Int, + @StringRes clickButtonText: Int, + onClick: () -> Unit + ) { + Snackbar.make( + requireView(), + getString(text), + Snackbar.LENGTH_INDEFINITE + ).setAction( + getString(clickButtonText) + ) { + onClick() + }.show() + } + protected fun showProgress() { viewModel.showProgress() } From 31a596e3da3152d4b202674a2cd7f69e6dfdecb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9C?= =?UTF-8?q?=D1=83=D0=B4=D1=80=D1=8F=D0=BA=D0=BE=D0=B2?= Date: Tue, 24 May 2022 02:31:28 +0300 Subject: [PATCH 02/13] addded : profile, rating, ratings placeholder. replaced livedata to statemodel. added logic for screen states --- .../android/presentation/fragment/BaseVMFragment.kt | 9 ++++++++- utils-core/utils_core.podspec | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt index 71bc682..2a5f6fa 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt @@ -5,13 +5,13 @@ import android.os.Bundle import android.view.View import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding +import androidx.databinding.ViewStubProxy import androidx.lifecycle.ViewModel import com.google.android.material.snackbar.Snackbar import com.merseyside.archy.presentation.fragment.BaseBindingFragment import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.kotlin.extensions.log import com.merseyside.merseyLib.utils.core.SavedState import com.merseyside.utils.ext.getSerialize import com.merseyside.utils.ext.putSerialize @@ -183,6 +183,13 @@ abstract class BaseVMFragment } } + //TODO move it to baseFragment please + protected fun ViewStubProxy.inflateViewStub() { + if (!isInflated) { + viewStub?.inflate() + } + } + protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, diff --git a/utils-core/utils_core.podspec b/utils-core/utils_core.podspec index 6255301..12d082d 100644 --- a/utils-core/utils_core.podspec +++ b/utils-core/utils_core.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |spec| fi set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" - "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + "$REPO_ROOT/../../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ -Pkotlin.native.cocoapods.archs="$ARCHS" \ -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" From bdb0ed1609ec532d48dbcae4d292bfa04e520424 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Thu, 26 May 2022 15:38:45 +0700 Subject: [PATCH 03/13] Refactoring, add and change koin saved state logic --- .../{BaseVMActivity.kt => VMActivity.kt} | 14 +-- ...MEventsActivity.kt => VMEventsActivity.kt} | 4 +- .../dialog/{BaseVMDialog.kt => VMDialog.kt} | 8 +- ...aseVMEventsDialog.kt => VMEventsDialog.kt} | 6 +- .../fragment/BaseVMEventsFragment.kt | 17 ---- .../presentation/fragment/VMEventsFragment.kt | 34 +++++++ .../{BaseVMFragment.kt => VMFragment.kt} | 40 +++++++-- .../di/{KoinUtils.kt => KoinSavedState.kt} | 25 +++++- .../core/presentation/di/KoinSavedState.kt | 14 +++ .../core/presentation/di/KoinScopedExt.kt | 20 ++++- .../archy/core/presentation/di/KoinUtils.kt | 6 -- .../core/presentation/model/BaseViewModel.kt | 4 +- .../core/presentation/model/StateViewModel.kt | 11 +-- .../core/presentation/di/KoinSavedState.kt | 18 ++++ .../archy/core/presentation/di/KoinUtils.kt | 8 -- .../merseyLib/utils/core/SavedState.kt | 54 ------------ .../merseyLib/utils/core/state/SavedState.kt | 88 +++++++++++++++++++ .../merseyLib/utils/core/state/StateSaver.kt | 10 +++ utils-core/utils_core.podspec | 2 +- 19 files changed, 261 insertions(+), 122 deletions(-) rename archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/{BaseVMActivity.kt => VMActivity.kt} (88%) rename archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/{BaseVMEventsActivity.kt => VMEventsActivity.kt} (74%) rename archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/{BaseVMDialog.kt => VMDialog.kt} (90%) rename archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/{BaseVMEventsDialog.kt => VMEventsDialog.kt} (79%) delete mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMEventsFragment.kt create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt rename archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/{BaseVMFragment.kt => VMFragment.kt} (83%) rename archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/{KoinUtils.kt => KoinSavedState.kt} (56%) create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt create mode 100644 archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt delete mode 100644 archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt delete mode 100644 utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/SavedState.kt create mode 100644 utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt create mode 100644 utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt similarity index 88% rename from archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMActivity.kt rename to archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt index 3d14b0e..9b9b2a8 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt @@ -7,7 +7,7 @@ import com.merseyside.archy.presentation.activity.BaseBindingActivity import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.utils.core.SavedState +import com.merseyside.merseyLib.utils.core.state.SavedState import com.merseyside.utils.ext.putSerialize import com.merseyside.utils.reflection.ReflectionUtils import com.merseyside.utils.requestPermissions @@ -15,7 +15,7 @@ import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlin.reflect.KClass -abstract class BaseVMActivity +abstract class VMActivity : BaseBindingActivity() { protected abstract val viewModel: M @@ -80,10 +80,10 @@ abstract class BaseVMActivity private fun observeViewModel() { viewModel.apply { - messageLiveEvent.ld().observe(this@BaseVMActivity, messageObserver) - isInProgress.ld().observe(this@BaseVMActivity, loadingObserver) - alertDialogLiveEvent.ld().observe(this@BaseVMActivity, alertDialogModel) - grantPermissionLiveEvent.ld().observe(this@BaseVMActivity, permissionObserver) + messageLiveEvent.ld().observe(this@VMActivity, messageObserver) + isInProgress.ld().observe(this@VMActivity, loadingObserver) + alertDialogLiveEvent.ld().observe(this@VMActivity, alertDialogModel) + grantPermissionLiveEvent.ld().observe(this@VMActivity, permissionObserver) } } @@ -116,7 +116,7 @@ abstract class BaseVMActivity protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, - BaseVMActivity::class.java, + VMActivity::class.java, 1 ).kotlin as KClass } diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMEventsActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt similarity index 74% rename from archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMEventsActivity.kt rename to archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt index 2365328..df7d983 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/BaseVMEventsActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt @@ -5,8 +5,8 @@ import androidx.databinding.ViewDataBinding import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner -abstract class BaseVMEventsActivity - : BaseVMActivity() where M : BaseViewModel, M: EventsDispatcherOwner { +abstract class VMEventsActivity + : VMActivity() where M : BaseViewModel, M: EventsDispatcherOwner { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt similarity index 90% rename from archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMDialog.kt rename to archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt index b460192..fcf61e0 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt @@ -11,7 +11,7 @@ import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import com.merseyside.utils.reflection.ReflectionUtils import kotlin.reflect.KClass -abstract class BaseVMDialog : BaseBindingDialog() { +abstract class VMDialog : BaseBindingDialog() { protected lateinit var viewModel: M @@ -56,8 +56,8 @@ abstract class BaseVMDialog : BaseBindin savedInstanceState: Bundle? ): View? { viewModel.apply { - errorLiveEvent.ld().observe(this@BaseVMDialog, errorObserver) - messageLiveEvent.ld().observe(this@BaseVMDialog, messageObserver) + errorLiveEvent.ld().observe(this@VMDialog, errorObserver) + messageLiveEvent.ld().observe(this@VMDialog, messageObserver) } return super.onCreateView(inflater, container, savedInstanceState) } @@ -81,7 +81,7 @@ abstract class BaseVMDialog : BaseBindin protected open fun getPersistentClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, - BaseVMDialog::class.java, + VMDialog::class.java, 1 ).kotlin as KClass } diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMEventsDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt similarity index 79% rename from archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMEventsDialog.kt rename to archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt index af3baf6..cc2b0f1 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/BaseVMEventsDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt @@ -2,12 +2,12 @@ package com.merseyside.merseyLib.archy.android.presentation.dialog import android.os.Bundle import androidx.databinding.ViewDataBinding -import com.merseyside.merseyLib.archy.android.presentation.fragment.BaseVMFragment +import com.merseyside.merseyLib.archy.android.presentation.fragment.VMFragment import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner -abstract class BaseVMEventsDialog - : BaseVMFragment() where M : BaseViewModel { +abstract class VMEventsDialog + : VMFragment() where M : BaseViewModel { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMEventsFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMEventsFragment.kt deleted file mode 100644 index 8fea53c..0000000 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMEventsFragment.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.merseyside.merseyLib.archy.android.presentation.fragment - -import android.os.Bundle -import androidx.databinding.ViewDataBinding -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel -import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner - -abstract class BaseVMEventsFragment - : BaseVMFragment() where M : BaseViewModel { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - @Suppress("UNCHECKED_CAST") - (viewModel as EventsDispatcherOwner).eventsDispatcher.bind(this, this as Listener) - } -} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt new file mode 100644 index 0000000..b1174a1 --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt @@ -0,0 +1,34 @@ +package com.merseyside.merseyLib.archy.android.presentation.fragment + +import android.os.Bundle +import androidx.databinding.ViewDataBinding +import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner +import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain +import org.koin.androidx.viewmodel.ext.android.getViewModel +import org.koin.core.parameter.parametersOf + +abstract class VMEventsFragment + : VMFragment() where Model : BaseViewModel { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + @Suppress("UNCHECKED_CAST") + (viewModel as EventsDispatcherOwner).eventsDispatcher.bind(this, this as Listener) + } + + override fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return getViewModel( + clazz = getViewModelClass(), + parameters = { + parametersOf( + *params, + bundle, + eventsDispatcherOnMain>() + ) + } + ) + } +} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt similarity index 83% rename from archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt rename to archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 71bc682..04efee9 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/BaseVMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -5,27 +5,30 @@ import android.os.Bundle import android.view.View import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding -import androidx.lifecycle.ViewModel import com.google.android.material.snackbar.Snackbar import com.merseyside.archy.presentation.fragment.BaseBindingFragment import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY +import com.merseyside.merseyLib.kotlin.Logger import com.merseyside.merseyLib.kotlin.extensions.log -import com.merseyside.merseyLib.utils.core.SavedState +import com.merseyside.merseyLib.utils.core.state.SavedState import com.merseyside.utils.ext.getSerialize import com.merseyside.utils.ext.putSerialize import com.merseyside.utils.reflection.ReflectionUtils import com.merseyside.utils.requestPermissions import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer +import org.koin.androidx.viewmodel.ext.android.getViewModel +import org.koin.core.context.loadKoinModules +import org.koin.core.module.Module +import org.koin.core.parameter.parametersOf import kotlin.reflect.KClass - -abstract class BaseVMFragment +abstract class VMFragment : BaseBindingFragment() { - protected abstract val viewModel: Model + protected lateinit var viewModel: Model private val messageObserver = { message: BaseViewModel.TextMessage? -> if (message != null) { @@ -83,6 +86,27 @@ abstract class BaseVMFragment setHasOptionsMenu(false) } + override fun performInjection(bundle: Bundle?, vararg params: Any) { + loadKoinModules(getKoinModules()) + viewModel = provideViewModel(bundle, params) + } + + open fun getKoinModules(): List { + return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } + } + + protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return getViewModel( + clazz = getViewModelClass(), + parameters = { + parametersOf( + *params, + bundle + ) + } + ) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { initDataBinding(requireBinding()) @@ -183,11 +207,11 @@ abstract class BaseVMFragment } } - protected fun getViewModelClass(): KClass { + protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, - BaseVMFragment::class.java, + VMFragment::class.java, 1 - ).kotlin as KClass + ).kotlin as KClass } } \ No newline at end of file diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt similarity index 56% rename from archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt rename to archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt index 29012ef..e1193b5 100644 --- a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -2,15 +2,38 @@ package com.merseyside.merseyLib.archy.core.presentation.di import android.os.Bundle import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.utils.core.SavedState +import com.merseyside.merseyLib.utils.core.state.SavedState import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import org.koin.core.parameter.ParametersHolder import com.merseyside.utils.ext.getSerialize import com.merseyside.utils.ext.isNotNullAndEmpty +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { val bundle = parametersHolder.getOrNull() + + return if (bundle.isNotNullAndEmpty()) { + SavedState().apply { + addAll( + bundle.getSerialize( + INSTANCE_STATE_KEY, + MapSerializer(String.serializer(), String.serializer()) + ) ?: throw IllegalArgumentException() + ) + + } + } else null + +} + +actual fun Scope.getSavedState( + qualifier: Qualifier?, + parametersHolder: ParametersDefinition? +): SavedState? { + val bundle = getOrNull(qualifier) return if (bundle.isNotNullAndEmpty()) { SavedState().apply { addAll( diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt new file mode 100644 index 0000000..6be252e --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -0,0 +1,14 @@ +package com.merseyside.merseyLib.archy.core.presentation.di + +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.parameter.ParametersHolder +import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope + +expect fun getSavedState(parametersHolder: ParametersHolder): SavedState? + +expect fun Scope.getSavedState( + qualifier: Qualifier? = null, + parametersHolder: ParametersDefinition? = null +): SavedState? \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt index 4ad70cf..d562948 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt @@ -1,11 +1,14 @@ package com.merseyside.merseyLib.archy.core.presentation.di import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel +import com.merseyside.merseyLib.utils.core.state.SavedState import dev.icerock.moko.mvvm.viewmodel.ViewModel import org.koin.core.definition.Definition import org.koin.core.instance.InstanceFactory import org.koin.core.module.Module +import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope import org.koin.dsl.ScopeDSL inline fun ScopeDSL.viewModel( @@ -15,6 +18,9 @@ inline fun ScopeDSL.viewModel( return factory(qualifier, definition) } +@Deprecated("Use newStateViewModel", + ReplaceWith("ScopeDSL.newStateViewModel") +) inline fun ScopeDSL.stateViewModel( qualifier: Qualifier? = null, noinline viewModelDefinition: Definition @@ -22,9 +28,17 @@ inline fun ScopeDSL.stateViewModel( return factory(qualifier) { viewModelDefinition(it).apply { - getSavedState(it)?.let { savedState -> - onRestoreState(savedState) + getSavedState(it)?.let { state -> + onRestoreState(state) } } } -} \ No newline at end of file +} + +inline fun ScopeDSL.state( + state: T?, + qualifier: Qualifier? = null +) { + state?.let { scoped(qualifier) { state } } +} + diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt deleted file mode 100644 index 358ab68..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.utils.core.SavedState -import org.koin.core.parameter.ParametersHolder - -expect fun getSavedState(parametersHolder: ParametersHolder): SavedState? \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt index 8cb122c..d9a577d 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt @@ -23,9 +23,7 @@ abstract class BaseViewModel protected constructor() : ViewModel() { val isInProgress: LiveData = mutProgress protected var progress: Boolean - get() { - return mutProgress.value - } + get() = mutProgress.value set(value) { mutProgress.value = value } diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt index e814fe5..8ac5503 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt @@ -1,12 +1,13 @@ package com.merseyside.merseyLib.archy.core.presentation.model -import com.merseyside.merseyLib.utils.core.SavedState +import com.merseyside.merseyLib.utils.core.state.DummySavedState +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.utils.core.state.StateSaver -abstract class StateViewModel: BaseViewModel() { - - abstract fun onRestoreState(savedState: SavedState) - abstract fun onSaveState(savedState: SavedState) +abstract class StateViewModel( + override val savedState: SavedState = DummySavedState() +): BaseViewModel(), StateSaver { companion object { const val INSTANCE_STATE_KEY = "instance_state" diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt new file mode 100644 index 0000000..522e576 --- /dev/null +++ b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -0,0 +1,18 @@ +package com.merseyside.merseyLib.archy.core.presentation.di + +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.parameter.ParametersHolder +import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope + +actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { + TODO() +} + +actual inline fun Scope.getSavedState( + qualifier: Qualifier?, + noinline parametersHolder: ParametersDefinition? +): SavedState? { + TODO() +} \ No newline at end of file diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt deleted file mode 100644 index ac4de80..0000000 --- a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinUtils.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.utils.core.SavedState -import org.koin.core.parameter.ParametersHolder - -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { - TODO() -} \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/SavedState.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/SavedState.kt deleted file mode 100644 index 785b0eb..0000000 --- a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/SavedState.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.merseyside.merseyLib.utils.core - -import com.merseyside.merseyLib.kotlin.serialization.serialize -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer - -class SavedState { - - private val map: MutableMap = HashMap() - - fun addAll(map: Map) { - if (map.isNotEmpty()) { - this.map.putAll(map) - } - } - - fun getAll(): Map = map - - fun contains(key: String): Boolean { - return map.contains(key) - } - - fun put(key: String, value: String) { - map[key] = value - } - - fun put(key: String, value: Any) { - map[key] = value.toString() - } - - fun getString(key: String): String? { - return map[key] - } - - fun getBool(key: String): Boolean? { - return map[key]?.toBooleanStrictOrNull() - } - - fun getInt(key: String): Int? { - return map[key]?.toIntOrNull() - } - - fun getLong(key: String): Long? { - return map[key]?.toLongOrNull() - } - - fun getFloat(key: String): Float? { - return map[key]?.toFloatOrNull() - } - - override fun toString(): String { - return map.serialize(MapSerializer(String.serializer(), String.serializer())) - } -} \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt new file mode 100644 index 0000000..ce33174 --- /dev/null +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt @@ -0,0 +1,88 @@ +package com.merseyside.merseyLib.utils.core.state + +import com.merseyside.merseyLib.kotlin.serialization.deserialize +import com.merseyside.merseyLib.kotlin.serialization.serialize +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer + +open class SavedState { + + val map: MutableMap = HashMap() + + fun addAll(map: Map) { + if (map.isNotEmpty()) { + this.map.putAll(map) + } + } + + fun getAll(): Map = map + + fun contains(key: String): Boolean { + return map.contains(key) + } + + fun put(key: String, value: String) { + map[key] = value + } + + fun put(key: String, value: Any) { + map[key] = value.toString() + } + + inline fun putSerializable(key: String, value: T) { + put(key, value.serialize()) + } + + inline fun putSerializable( + key: String, + value: T, + serializationStrategy: SerializationStrategy + ) { + put(key, value.serialize(serializationStrategy)) + } + + fun getString(key: String): String? { + return map[key] + } + + fun getBool(key: String): Boolean? { + return map[key]?.toBooleanStrictOrNull() + } + + fun getInt(key: String): Int? { + return map[key]?.toIntOrNull() + } + + fun getLong(key: String): Long? { + return map[key]?.toLongOrNull() + } + + fun getFloat(key: String): Float? { + return map[key]?.toFloatOrNull() + } + + inline fun getSerializable(key: String): T? { + return map[key]?.deserialize() + } + + @Throws(NullPointerException::class) + inline fun getSerializableOrThrow(key: String): T { + return getSerializable(key) ?: throw NullPointerException() + } + + inline fun getSerializable( + key: String, + deserializationStrategy: DeserializationStrategy + ): T? { + return map[key]?.deserialize(deserializationStrategy) + } + + override fun toString(): String { + return map.serialize(MapSerializer(String.serializer(), String.serializer())) + } +} + + +fun DummySavedState() = SavedState() \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt new file mode 100644 index 0000000..eaa4f61 --- /dev/null +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt @@ -0,0 +1,10 @@ +package com.merseyside.merseyLib.utils.core.state + +interface StateSaver { + val savedState: SavedState? + + @Deprecated("Pass saved state in constructor") + fun onRestoreState(restoredState: SavedState) + fun onSaveState(savedState: SavedState) + +} \ No newline at end of file diff --git a/utils-core/utils_core.podspec b/utils-core/utils_core.podspec index 6255301..12d082d 100644 --- a/utils-core/utils_core.podspec +++ b/utils-core/utils_core.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |spec| fi set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" - "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + "$REPO_ROOT/../../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ -Pkotlin.native.cocoapods.archs="$ARCHS" \ -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" From f88401211c9a07a07ba23c4e186e3e6f8161cbf2 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Fri, 27 May 2022 12:13:25 +0700 Subject: [PATCH 04/13] Refactoring viewModel events logic --- .../presentation/activity/VMActivity.kt | 6 +- .../presentation/activity/VMEventsActivity.kt | 2 +- .../android/presentation/dialog/VMDialog.kt | 2 +- .../presentation/dialog/VMEventsDialog.kt | 2 +- .../presentation/fragment/NewVMFragment.kt | 217 ++++++++++++++++++ .../presentation/fragment/VMEventsFragment.kt | 18 +- .../presentation/fragment/VMFragment.kt | 7 +- .../core/presentation/di/KoinSavedState.kt | 2 +- .../core/presentation/di/KoinModuleExt.kt | 2 +- .../core/presentation/di/KoinScopedExt.kt | 5 +- .../presentation/model/ext/ViewModelExt.kt | 2 - .../{model => viewModel}/BaseViewModel.kt | 2 +- .../presentation/viewModel/EventsViewModel.kt | 123 ++++++++++ .../viewModel/NewBaseViewModel.kt | 99 ++++++++ .../{model => viewModel}/StateViewModel.kt | 2 +- .../presentation/viewModel/entity/Alert.kt | 12 + .../viewModel/entity/TextMessage.kt | 8 + .../viewModel/ext/ViewModelExt.kt | 2 + gradle.properties | 2 +- sample/androidApp/build.gradle.kts | 4 + .../merseyside/sample/view/TestFragment.kt | 18 ++ .../src/main/res/layout/fragment_test.xml | 19 ++ sample/mpp-library/build.gradle.kts | 8 +- .../sample/viewModel/TestViewModel.kt | 12 + settings.gradle.kts | 2 +- utils-core/utils_core.podspec | 2 +- 26 files changed, 547 insertions(+), 33 deletions(-) create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/ext/ViewModelExt.kt rename archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/{model => viewModel}/BaseViewModel.kt (99%) create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt rename archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/{model => viewModel}/StateViewModel.kt (85%) create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/Alert.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/TextMessage.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/ext/ViewModelExt.kt create mode 100644 sample/androidApp/src/main/kotlin/com/merseyside/sample/view/TestFragment.kt create mode 100644 sample/androidApp/src/main/res/layout/fragment_test.xml create mode 100644 sample/mpp-library/src/commonMain/kotlin/com/merseyside/sample/viewModel/TestViewModel.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt index 9b9b2a8..bec93a1 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt @@ -4,9 +4,9 @@ import android.content.Context import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.archy.presentation.activity.BaseBindingActivity -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY +import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.StateViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.StateViewModel.Companion.INSTANCE_STATE_KEY import com.merseyside.merseyLib.utils.core.state.SavedState import com.merseyside.utils.ext.putSerialize import com.merseyside.utils.reflection.ReflectionUtils diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt index df7d983..72c0c0f 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt @@ -2,7 +2,7 @@ package com.merseyside.merseyLib.archy.android.presentation.activity import android.os.Bundle import androidx.databinding.ViewDataBinding -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner abstract class VMEventsActivity diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt index fcf61e0..3a8bd3e 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt @@ -7,7 +7,7 @@ import android.view.View import android.view.ViewGroup import androidx.databinding.ViewDataBinding import com.merseyside.archy.presentation.dialog.BaseBindingDialog -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel import com.merseyside.utils.reflection.ReflectionUtils import kotlin.reflect.KClass diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt index cc2b0f1..354bb9f 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt @@ -3,7 +3,7 @@ package com.merseyside.merseyLib.archy.android.presentation.dialog import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.merseyLib.archy.android.presentation.fragment.VMFragment -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner abstract class VMEventsDialog diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt new file mode 100644 index 0000000..dae1c42 --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt @@ -0,0 +1,217 @@ +package com.merseyside.merseyLib.archy.android.presentation.fragment + +import android.content.Context +import android.os.Bundle +import android.view.View +import androidx.annotation.StringRes +import androidx.databinding.ViewDataBinding +import com.google.android.material.snackbar.Snackbar +import com.merseyside.archy.presentation.fragment.BaseBindingFragment +import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.NewBaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.StateViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.StateViewModel.Companion.INSTANCE_STATE_KEY +import com.merseyside.merseyLib.kotlin.Logger +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.utils.ext.getSerialize +import com.merseyside.utils.ext.putSerialize +import com.merseyside.utils.reflection.ReflectionUtils +import com.merseyside.utils.requestPermissions +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer +import org.koin.androidx.viewmodel.ext.android.getViewModel +import org.koin.core.context.loadKoinModules +import org.koin.core.module.Module +import org.koin.core.parameter.parametersOf +import kotlin.reflect.KClass + +abstract class NewVMFragment + : BaseBindingFragment() { + + protected lateinit var viewModel: Model + + private val messageObserver = { message: BaseViewModel.TextMessage? -> + if (message != null) { + if (message.isError) { + showErrorMsg(message) + } else { + showMsg(message) + } + } + } + + private val errorObserver = { throwable: Throwable? -> + if (throwable != null) { + this.handleError(throwable) + } + } + + private val progressObserver = { isLoading: Boolean? -> + this.loadingObserver(isLoading ?: false) + } + + private val alertDialogModelObserver = { model: BaseViewModel.AlertDialogModel? -> + model?.apply { + showAlertDialog( + title, + message, + positiveButtonText, + negativeButtonText, + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ) + } + Unit + } + + private val permissionObserver = { pair: Pair, Int>? -> + if (pair != null) { + requestPermissions(*pair.first, requestCode = pair.second) + } + } + + abstract fun getBindingVariable(): Int + + open fun initDataBinding(binding: Binding) { + binding.apply { + setVariable(getBindingVariable(), viewModel) + executePendingBindings() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(false) + } + + override fun performInjection(bundle: Bundle?, vararg params: Any) { + loadKoinModules(getKoinModules()) + viewModel = provideViewModel(bundle, params) + } + + open fun getKoinModules(): List { + return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } + } + + protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return getViewModel( + clazz = getViewModelClass(), + parameters = { + parametersOf( + *params, + bundle + ) + } + ) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + + initDataBinding(requireBinding()) + + viewModel.apply { + errorLiveEvent.ld().observe(viewLifecycleOwner, errorObserver) + messageLiveEvent.ld().observe(viewLifecycleOwner, messageObserver) + isInProgress.ld().observe(viewLifecycleOwner, progressObserver) + alertDialogLiveEvent.ld().observe(viewLifecycleOwner, alertDialogModelObserver) + grantPermissionLiveEvent.ld().observe(viewLifecycleOwner, permissionObserver) + } + + super.onViewCreated(view, savedInstanceState) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + + if (viewModel is StateViewModel) { + val bundle = SavedState() + + (viewModel as StateViewModel).onSaveState(bundle) + outState.putSerialize( + INSTANCE_STATE_KEY, bundle.getAll(), + MapSerializer(String.serializer(), String.serializer()) + ) + } + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + val savedState = SavedState().apply { + savedInstanceState?.getSerialize( + INSTANCE_STATE_KEY, MapSerializer(String.serializer(), String.serializer()) + )?.let { addAll(it) } + } + if (viewModel is StateViewModel) { + (viewModel as StateViewModel).onRestoreState(savedState) + } + super.onViewStateRestored(savedInstanceState) + } + + override fun updateLanguage(context: Context) { + super.updateLanguage(context) + //viewModel.updateLanguage(context) + } + + protected open fun loadingObserver(isLoading: Boolean) {} + + fun showErrorMsg(textMessage: BaseViewModel.TextMessage) { + if (textMessage.actionMsg.isNullOrEmpty()) { + showErrorMsg(textMessage.msg) + } else { + showErrorMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) + } + } + + private fun showMsg(textMessage: BaseViewModel.TextMessage) { + if (textMessage.actionMsg.isNullOrEmpty()) { + showMsg(textMessage.msg) + } else { + showMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) + } + } + + fun showInteractSnack( + @StringRes text: Int, + @StringRes clickButtonText: Int, + onClick: () -> Unit + ) { + Snackbar.make( + requireView(), + getString(text), + Snackbar.LENGTH_INDEFINITE + ).setAction( + getString(clickButtonText) + ) { + onClick() + }.show() + } + + protected fun showProgress() { + viewModel.showProgress() + } + + protected fun hideProgress() { + viewModel.hideProgress() + } + + override fun onDestroyView() { + super.onDestroyView() + + viewModel.apply { + errorLiveEvent.removeObserver(errorObserver) + messageLiveEvent.removeObserver(messageObserver) + isInProgress.removeObserver(progressObserver) + alertDialogLiveEvent.removeObserver(alertDialogModelObserver) + grantPermissionLiveEvent.removeObserver(permissionObserver) + } + } + + protected fun getViewModelClass(): KClass { + return ReflectionUtils.getGenericParameterClass( + this.javaClass, + NewVMFragment::class.java, + 1 + ).kotlin as KClass + } +} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt index b1174a1..b7c54a8 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt @@ -2,21 +2,27 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.os.Bundle import androidx.databinding.ViewDataBinding -import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.EventsViewModel import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.core.parameter.parametersOf -abstract class VMEventsFragment - : VMFragment() where Model : BaseViewModel { +abstract class VMEventsFragment : + NewVMFragment(), EventsViewModel.BaseEventsListener + where Model : EventsViewModel, + Listener : EventsViewModel.BaseEventsListener +{ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @Suppress("UNCHECKED_CAST") - (viewModel as EventsDispatcherOwner).eventsDispatcher.bind(this, this as Listener) + (viewModel as EventsDispatcherOwner).eventsDispatcher.bind( + this, + this as Listener + ) } override fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { @@ -31,4 +37,8 @@ abstract class VMEventsFragment ScopeDSL.viewModel( diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/ext/ViewModelExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/ext/ViewModelExt.kt deleted file mode 100644 index 91c286c..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/ext/ViewModelExt.kt +++ /dev/null @@ -1,2 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.model.ext - diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt similarity index 99% rename from archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt rename to archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt index d9a577d..60ba04f 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt @@ -1,4 +1,4 @@ -package com.merseyside.merseyLib.archy.core.presentation.model +package com.merseyside.merseyLib.archy.core.presentation.viewModel import com.merseyside.merseyLib.kotlin.Logger import com.merseyside.merseyLib.kotlin.coroutines.ext.mapState diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt new file mode 100644 index 0000000..4dc041c --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt @@ -0,0 +1,123 @@ +package com.merseyside.merseyLib.archy.core.presentation.viewModel + +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.Alert +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.TextMessage +import com.merseyside.merseyLib.kotlin.Logger +import com.merseyside.merseyLib.utils.core.ext.getString +import com.merseyside.merseyLib.utils.core.ext.getStringNull +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher +import dev.icerock.moko.resources.StringResource + +abstract class EventsViewModel : NewBaseViewModel() { + + abstract val eventsDispatcher: EventsDispatcher + + open fun handleError(throwable: Throwable): Boolean { + eventsDispatcher.dispatchEvent { + if (!onError(throwable)) throw Exception("Error not handled!") + } + return true + } + + protected fun showMsg(id: StringResource, vararg args: String) { + showMsg(getString(id, *args)) + } + + protected fun showErrorMsg(id: StringResource, vararg args: String) { + showErrorMsg(getString(id, *args)) + } + + protected fun showMsg(msg: String) { + Logger.log(this, msg) + TextMessage( + isError = false, + msg = msg + ).also { showMessage(it) } + } + + protected fun showErrorMsg(msg: String) { + Logger.logErr(this, msg) + TextMessage( + isError = true, + msg = msg + ).also { showMessage(it) } + } + + protected fun showMsg(msg: String, actionMsg: String, onClick: () -> Unit = {}) { + Logger.log(this, msg) + TextMessage( + isError = false, + msg = msg, + actionMsg = actionMsg, + onClick = onClick + ).also { showMessage(it) } + } + + protected fun showErrorMsg(msg: String, actionMsg: String, onClick: () -> Unit = {}) { + Logger.logErr(this, msg) + TextMessage( + isError = true, + msg = msg, + actionMsg = actionMsg, + onClick = onClick + ).also { showMessage(it) } + } + + fun showAlert( + title: String? = null, + message: String? = null, + positiveButtonText: String? = null, + negativeButtonText: String? = null, + onPositiveClick: () -> Unit = {}, + onNegativeClick: () -> Unit = {}, + isSingleAction: Boolean? = null, + isCancelable: Boolean? = null + ) { + Alert( + title, + message, + positiveButtonText, + negativeButtonText, + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ).also { showAlert(it) } + } + + fun showAlertDialog( + titleRes: StringResource? = null, + messageRes: StringResource? = null, + positiveButtonTextRes: StringResource? = null, + negativeButtonTextRes: StringResource? = null, + onPositiveClick: () -> Unit = {}, + onNegativeClick: () -> Unit = {}, + isSingleAction: Boolean? = null, + isCancelable: Boolean? = null + ) { + showAlert( + getStringNull(titleRes), + getStringNull(messageRes), + getStringNull(positiveButtonTextRes), + getStringNull(negativeButtonTextRes), + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ) + } + + protected fun showMessage(message: TextMessage) { + eventsDispatcher.dispatchEvent { onMessage(message) } + } + + protected fun showAlert(alert: Alert) { + eventsDispatcher.dispatchEvent { onAlert(alert) } + } + + interface BaseEventsListener { + fun onError(throwable: Throwable): Boolean + fun onMessage(message: TextMessage) + fun onAlert(alert: Alert) + } +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt new file mode 100644 index 0000000..346d373 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt @@ -0,0 +1,99 @@ +package com.merseyside.merseyLib.archy.core.presentation.viewModel + +import com.merseyside.merseyLib.kotlin.coroutines.ext.mapState +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* + +abstract class NewBaseViewModel protected constructor() : ViewModel() { + +// internal val scope: CoroutineScope +// get() { return viewModelScope } + + private val mutProgress = MutableStateFlow(false) + val isInProgress = mutProgress.asStateFlow() + + private val mutProgressText = MutableStateFlow(null) + val progressText = mutProgressText.asStateFlow() + + protected var progress: Boolean + get() = mutProgress.value + set(value) { + mutProgress.value = value + } + + open fun onError(throwable: Throwable) {} + + + fun showProgress(text: String? = null) { + mutProgressText.value = text + progress = true + } + + fun hideProgress() { + if (progress) { + mutProgressText.value = null + progress = false + } + } + + open fun onBack(): Boolean { + return true + } + + + + + fun StateFlow.mapState( + transform: (data: T) -> K + ): StateFlow { + return mapState( + scope = viewModelScope, + transform = transform + ) + } + + fun StateFlow.mapState( + initialValue: K, + transform: suspend (data: T) -> K + ): StateFlow { + return mapState( + scope = viewModelScope, + initialValue = initialValue, + transform = transform + ) + } + + fun combineState( + flow1: StateFlow, + flow2: StateFlow, + scope: CoroutineScope = viewModelScope, + sharingStarted: SharingStarted = SharingStarted.Eagerly, + transform: (T1, T2) -> R + ): StateFlow = combine(flow1, flow2) { + o1, o2 -> transform.invoke(o1, o2) + }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value)) + + fun combineState( + flow1: StateFlow, + flow2: StateFlow, + flow3: StateFlow, + scope: CoroutineScope = viewModelScope, + sharingStarted: SharingStarted = SharingStarted.Eagerly, + transform: (T1, T2, T3) -> R + ): StateFlow = combine(flow1, flow2, flow3) { + o1, o2, o3 -> transform.invoke(o1, o2, o3) + }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value, flow3.value)) + + fun combineState( + flow1: StateFlow, + flow2: StateFlow, + flow3: StateFlow, + flow4: StateFlow, + scope: CoroutineScope = viewModelScope, + sharingStarted: SharingStarted = SharingStarted.Eagerly, + transform: (T1, T2, T3, T4) -> R + ): StateFlow = combine(flow1, flow2, flow3, flow4) { + o1, o2, o3, o4 -> transform.invoke(o1, o2, o3, o4) + }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value, flow3.value, flow4.value)) +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/StateViewModel.kt similarity index 85% rename from archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt rename to archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/StateViewModel.kt index 8ac5503..71c0db0 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/StateViewModel.kt @@ -1,4 +1,4 @@ -package com.merseyside.merseyLib.archy.core.presentation.model +package com.merseyside.merseyLib.archy.core.presentation.viewModel import com.merseyside.merseyLib.utils.core.state.DummySavedState import com.merseyside.merseyLib.utils.core.state.SavedState diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/Alert.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/Alert.kt new file mode 100644 index 0000000..dfea5e8 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/Alert.kt @@ -0,0 +1,12 @@ +package com.merseyside.merseyLib.archy.core.presentation.viewModel.entity + +data class Alert( + val title: String? = null, + val message: String? = null, + val positiveButtonText: String? = null, + val negativeButtonText: String? = null, + val onPositiveClick: () -> Unit = {}, + val onNegativeClick: () -> Unit = {}, + val isSingleAction: Boolean? = null, + val isCancelable: Boolean? = null +) \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/TextMessage.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/TextMessage.kt new file mode 100644 index 0000000..eae0786 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/entity/TextMessage.kt @@ -0,0 +1,8 @@ +package com.merseyside.merseyLib.archy.core.presentation.viewModel.entity + +data class TextMessage( + val isError: Boolean = false, + var msg: String = "", + var actionMsg: String? = null, + val onClick: () -> Unit = {} +) \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/ext/ViewModelExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/ext/ViewModelExt.kt new file mode 100644 index 0000000..ba42f08 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/ext/ViewModelExt.kt @@ -0,0 +1,2 @@ +package com.merseyside.merseyLib.archy.core.presentation.viewModel.ext + diff --git a/gradle.properties b/gradle.properties index 00c65f7..9f53a03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,7 +20,7 @@ android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -build.localAndroidDependencies=false +build.localAndroidDependencies=true build.localKotlinExtLibrary=false kotlin.mpp.stability.nowarn=true diff --git a/sample/androidApp/build.gradle.kts b/sample/androidApp/build.gradle.kts index 9876c95..594ecb6 100644 --- a/sample/androidApp/build.gradle.kts +++ b/sample/androidApp/build.gradle.kts @@ -77,4 +77,8 @@ dependencies { } else { merseyLibs.forEach { lib -> implementation(lib) } } + + implementation(projects.archyCore) + implementation(projects.utilsCore) + implementation(projects.archyAndroid) } diff --git a/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/TestFragment.kt b/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/TestFragment.kt new file mode 100644 index 0000000..8383ef8 --- /dev/null +++ b/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/TestFragment.kt @@ -0,0 +1,18 @@ +package com.merseyside.sample.view + +import android.content.Context +import com.merseyside.merseyLib.archy.android.presentation.fragment.VMEventsFragment +import com.merseyside.sample.BR +import com.merseyside.sample.R +import com.merseyside.sample.databinding.FragmentTestBinding +import com.merseyside.sample.viewModel.TestViewModel + +class TestFragment : VMEventsFragment(), TestViewModel.TestEventsListener { + override fun getLayoutId() = R.layout.fragment_test + override fun getTitle(context: Context) = null + override fun getBindingVariable() = BR.viewModel + override fun doSomeJob() { + showMsg("Doing some job!") + } +} \ No newline at end of file diff --git a/sample/androidApp/src/main/res/layout/fragment_test.xml b/sample/androidApp/src/main/res/layout/fragment_test.xml new file mode 100644 index 0000000..bc6b3dd --- /dev/null +++ b/sample/androidApp/src/main/res/layout/fragment_test.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/mpp-library/build.gradle.kts b/sample/mpp-library/build.gradle.kts index 50b5b73..3b6408b 100644 --- a/sample/mpp-library/build.gradle.kts +++ b/sample/mpp-library/build.gradle.kts @@ -26,21 +26,17 @@ val merseyModules = listOf( kotlinConvention { debug = true -// setCompilerArgs( -// -// ) } val multiplatform = listOf( multiplatformLibs.koin ) -//val android = listOf( -// -//) dependencies { commonMainApi(common.merseyLib.kotlin.ext) + commonMainImplementation(projects.archyCore) + commonMainImplementation(projects.utilsCore) multiplatform.forEach { lib -> commonMainImplementation(lib) diff --git a/sample/mpp-library/src/commonMain/kotlin/com/merseyside/sample/viewModel/TestViewModel.kt b/sample/mpp-library/src/commonMain/kotlin/com/merseyside/sample/viewModel/TestViewModel.kt new file mode 100644 index 0000000..9dcd4bd --- /dev/null +++ b/sample/mpp-library/src/commonMain/kotlin/com/merseyside/sample/viewModel/TestViewModel.kt @@ -0,0 +1,12 @@ +package com.merseyside.sample.viewModel + +import com.merseyside.merseyLib.archy.core.presentation.viewModel.EventsViewModel +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher + +class TestViewModel(override val eventsDispatcher: EventsDispatcher) + : EventsViewModel() { + + interface TestEventsListener : BaseEventsListener { + fun doSomeJob() + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 9188463..131716c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,7 @@ enableFeaturePreview("VERSION_CATALOGS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") -private val isLocalAndroidDependencies = false +private val isLocalAndroidDependencies = true private val isLocalKotlinExtLibrary = false dependencyResolutionManagement { diff --git a/utils-core/utils_core.podspec b/utils-core/utils_core.podspec index 12d082d..6255301 100644 --- a/utils-core/utils_core.podspec +++ b/utils-core/utils_core.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |spec| fi set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" - "$REPO_ROOT/../../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ -Pkotlin.native.cocoapods.archs="$ARCHS" \ -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" From 4f5304332ff0a74861a9720fe3f0c576df5536d2 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Mon, 30 May 2022 18:30:26 +0700 Subject: [PATCH 05/13] Adding shared scope logic, refactoring --- .../archy/android/di/SharedScopeExt.kt | 23 ++++++++++++++ .../archy/android/di/SharedScopeInstance.kt | 26 ++++++++++++++++ .../android/di/SharedScopeLifecycleHandler.kt | 18 +++++++++++ .../presentation/fragment/VMFragment.kt | 2 ++ .../merseyLib/archy/android/utils/KoinExt.kt | 31 ------------------- archy-core/build.gradle.kts | 1 + .../core/presentation/di/SharedScopeHolder.kt | 10 ++++++ buildSrc/settings.gradle.kts | 2 +- settings.gradle.kts | 2 +- 9 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeInstance.kt create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeLifecycleHandler.kt delete mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/utils/KoinExt.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt new file mode 100644 index 0000000..632da5d --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt @@ -0,0 +1,23 @@ +package com.merseyside.merseyLib.archy.android.di + +import androidx.fragment.app.Fragment +import com.merseyside.merseyLib.archy.core.presentation.di.SharedScopeHolder +import org.koin.core.scope.ScopeID + +fun Fragment.findParentSharedScopeHolder(scopeID: ScopeID? = null): SharedScopeHolder? { + val parent = parentFragment + + return if (parent != null) { + if (parent !is SharedScopeHolder) { + parent.findParentSharedScopeHolder(scopeID) + } else if (scopeID != null) { + if (parent.scopeID != scopeID) { + parent.findParentSharedScopeHolder(scopeID) + } else parent + } else parent + } else null +} + +fun Fragment.requireParentSharedScopeHolder(scopeID: ScopeID): SharedScopeHolder { + return findParentSharedScopeHolder(scopeID) ?: throw NullPointerException("Scope required but returned null!") +} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeInstance.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeInstance.kt new file mode 100644 index 0000000..aede61c --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeInstance.kt @@ -0,0 +1,26 @@ +package com.merseyside.merseyLib.archy.android.di + +import com.merseyside.merseyLib.kotlin.extensions.isZero +import com.merseyside.merseyLib.kotlin.extensions.logMsg +import org.koin.core.scope.Scope + +interface SharedScopeInstance { + + val scope: Scope + val sharedScope: Scope + var linkedScopesCount: Int + + private fun linkToScope() { + scope.linkTo(sharedScope) + linkedScopesCount++ + } + + private fun unlinkScope() { + scope.unlink(sharedScope) + val count = ++linkedScopesCount + + if (count.isZero()) { + sharedScope.close().also { logMsg("${this::class.simpleName}", "Scope closed!") } + } + } +} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeLifecycleHandler.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeLifecycleHandler.kt new file mode 100644 index 0000000..b18c8b2 --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeLifecycleHandler.kt @@ -0,0 +1,18 @@ +package com.merseyside.merseyLib.archy.android.di + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.LifecycleOwner + +class SharedScopeLifecycleHandler(private val lifecycleOwner: LifecycleOwner): DefaultLifecycleObserver { + + init { + lifecycleOwner.lifecycle.addObserver(this) + } + + override fun onCreate(owner: LifecycleOwner) { + super.onCreate(owner) + + } +} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 04efee9..b5af67b 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -3,6 +3,7 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.content.Context import android.os.Bundle import android.view.View +import androidx.annotation.CallSuper import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding import com.google.android.material.snackbar.Snackbar @@ -86,6 +87,7 @@ abstract class VMFragment setHasOptionsMenu(false) } + @CallSuper override fun performInjection(bundle: Bundle?, vararg params: Any) { loadKoinModules(getKoinModules()) viewModel = provideViewModel(bundle, params) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/utils/KoinExt.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/utils/KoinExt.kt deleted file mode 100644 index 6c37008..0000000 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/utils/KoinExt.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.merseyside.merseyLib.archy.android.utils - -import androidx.annotation.IdRes -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModel -import androidx.navigation.NavBackStackEntry -import org.koin.core.annotation.KoinInternalApi -import org.koin.core.component.KoinComponent -import org.koin.core.context.GlobalContext -import org.koin.core.scope.Scope -import kotlin.reflect.KClass - -fun Fragment.navGraphViewModel( - @IdRes navGraphId: Int, - clazz: KClass -): VM { - TODO() -// val backStackEntry: NavBackStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) } -// return getKoinScope(this).getViewModel( -// owner = { ViewModelOwner(backStackEntry.viewModelStore) }, -// clazz = clazz -// ) -} - -@OptIn(KoinInternalApi::class) -fun getKoinScope(any: Any): Scope { - return when (any) { - is KoinComponent -> any.getKoin().scopeRegistry.rootScope - else -> GlobalContext.get().scopeRegistry.rootScope - } -} \ No newline at end of file diff --git a/archy-core/build.gradle.kts b/archy-core/build.gradle.kts index 6b4c596..758f730 100644 --- a/archy-core/build.gradle.kts +++ b/archy-core/build.gradle.kts @@ -66,6 +66,7 @@ val mppModules = listOf( ) dependencies { + commonMainImplementation(common.kotlin.stdlib) commonMainImplementation(common.merseyLib.kotlin.ext) androidMainImplementation(androidLibs.merseyLib.utils) diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt new file mode 100644 index 0000000..83c50ba --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt @@ -0,0 +1,10 @@ +package com.merseyside.merseyLib.archy.core.presentation.di + +import org.koin.core.scope.Scope +import org.koin.core.scope.ScopeID + +interface SharedScopeHolder { + + val sharedScope: Scope + val scopeID: ScopeID +} \ No newline at end of file diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 6caa6c6..acde143 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -16,7 +16,7 @@ dependencyResolutionManagement { gradlePluginPortal() } - val catalogVersions = "1.4.8" + val catalogVersions = "1.4.9" val group = "io.github.merseyside" versionCatalogs { diff --git a/settings.gradle.kts b/settings.gradle.kts index 9188463..d1db8b6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ dependencyResolutionManagement { mavenLocal() } - val catalogVersions = "1.4.8" + val catalogVersions = "1.4.9" val group = "io.github.merseyside" versionCatalogs { val multiplatformLibs by creating { From 2241f20134a816e1b03c3992c8ddbd91587fb9fa Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 30 May 2022 17:50:17 +0400 Subject: [PATCH 06/13] optional saved state in state viewmodel --- .../merseyLib/archy/core/presentation/model/StateViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt index 8ac5503..9b3064e 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt @@ -6,7 +6,7 @@ import com.merseyside.merseyLib.utils.core.state.StateSaver abstract class StateViewModel( - override val savedState: SavedState = DummySavedState() + override val savedState: SavedState? = DummySavedState() ): BaseViewModel(), StateSaver { companion object { From 513a251c6501f39716ad7859267f2298245f02bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9C?= =?UTF-8?q?=D1=83=D0=B4=D1=80=D1=8F=D0=BA=D0=BE=D0=B2?= Date: Mon, 30 May 2022 19:56:17 +0300 Subject: [PATCH 07/13] merge done --- .../archy/android/presentation/fragment/VMFragment.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index b5af67b..2dc9bd8 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -29,7 +29,7 @@ import kotlin.reflect.KClass abstract class VMFragment : BaseBindingFragment() { - protected lateinit var viewModel: Model + lateinit var viewModel: Model private val messageObserver = { message: BaseViewModel.TextMessage? -> if (message != null) { @@ -90,11 +90,16 @@ abstract class VMFragment @CallSuper override fun performInjection(bundle: Bundle?, vararg params: Any) { loadKoinModules(getKoinModules()) - viewModel = provideViewModel(bundle, params) + viewModel = provideViewModel(bundle, *params) } open fun getKoinModules(): List { - return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } + return emptyList().also { + Logger.logInfo( + "VMFragment", + "Empty fragment's koin modules" + ) + } } protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { From 37b042406af3b5eacfa9f4de056cad9e960f9c85 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Tue, 31 May 2022 17:58:42 +0700 Subject: [PATCH 08/13] Saved state changes --- .../presentation/fragment/VMFragment.kt | 22 +++-------- .../core/presentation/di/KoinSavedState.kt | 39 +++++-------------- .../archy/core/presentation/ext/BundleExt.kt | 21 ++++++++++ .../core/presentation/di/KoinModuleExt.kt | 21 +++++++--- .../core/presentation/di/KoinSavedState.kt | 7 ++-- .../core/presentation/di/KoinScopedExt.kt | 30 +++++++++----- .../core/presentation/model/BaseViewModel.kt | 3 +- .../core/presentation/model/StateViewModel.kt | 6 +-- .../core/presentation/di/KoinSavedState.kt | 7 ++-- .../utils/core/state/InstanceStateManager.kt | 7 ++++ .../merseyLib/utils/core/state/StateSaver.kt | 4 -- 11 files changed, 87 insertions(+), 80 deletions(-) create mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt create mode 100644 utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/InstanceStateManager.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 8a0e41a..c594d95 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -13,7 +13,7 @@ import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY import com.merseyside.merseyLib.kotlin.Logger import com.merseyside.merseyLib.utils.core.state.SavedState -import com.merseyside.utils.ext.getSerialize +import com.merseyside.merseyLib.utils.core.state.StateSaver import com.merseyside.utils.ext.putSerialize import com.merseyside.utils.reflection.ReflectionUtils import com.merseyside.utils.requestPermissions @@ -88,11 +88,11 @@ abstract class VMFragment @CallSuper override fun performInjection(bundle: Bundle?, vararg params: Any) { - loadKoinModules(getKoinModules()) + loadKoinModules(getKoinModules(bundle, params)) viewModel = provideViewModel(bundle, params) } - open fun getKoinModules(): List { + open fun getKoinModules(bundle: Bundle?, vararg params: Any): List { return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } } @@ -126,10 +126,10 @@ abstract class VMFragment override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - if (viewModel is StateViewModel) { + if (viewModel is StateSaver) { val bundle = SavedState() - (viewModel as StateViewModel).onSaveState(bundle) + (viewModel as StateSaver).onSaveState(bundle) outState.putSerialize( INSTANCE_STATE_KEY, bundle.getAll(), MapSerializer(String.serializer(), String.serializer()) @@ -137,18 +137,6 @@ abstract class VMFragment } } - override fun onViewStateRestored(savedInstanceState: Bundle?) { - val savedState = SavedState().apply { - savedInstanceState?.getSerialize( - INSTANCE_STATE_KEY, MapSerializer(String.serializer(), String.serializer()) - )?.let { addAll(it) } - } - if (viewModel is StateViewModel) { - (viewModel as StateViewModel).onRestoreState(savedState) - } - super.onViewStateRestored(savedInstanceState) - } - override fun updateLanguage(context: Context) { super.updateLanguage(context) //viewModel.updateLanguage(context) diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt index e1193b5..86bebe5 100644 --- a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -1,7 +1,9 @@ package com.merseyside.merseyLib.archy.core.presentation.di import android.os.Bundle +import com.merseyside.merseyLib.archy.core.presentation.ext.toSavedState import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY +import com.merseyside.merseyLib.kotlin.extensions.log import com.merseyside.merseyLib.utils.core.state.SavedState import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer @@ -10,39 +12,16 @@ import com.merseyside.utils.ext.getSerialize import com.merseyside.utils.ext.isNotNullAndEmpty import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier +import org.koin.core.qualifier.named import org.koin.core.scope.Scope -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { - val bundle = parametersHolder.getOrNull() - - return if (bundle.isNotNullAndEmpty()) { - SavedState().apply { - addAll( - bundle.getSerialize( - INSTANCE_STATE_KEY, - MapSerializer(String.serializer(), String.serializer()) - ) ?: throw IllegalArgumentException() - ) - - } - } else null - +actual fun getSavedState(parametersHolder: ParametersHolder): SavedState { + return parametersHolder.getOrNull().toSavedState() } actual fun Scope.getSavedState( - qualifier: Qualifier?, - parametersHolder: ParametersDefinition? -): SavedState? { - val bundle = getOrNull(qualifier) - return if (bundle.isNotNullAndEmpty()) { - SavedState().apply { - addAll( - bundle.getSerialize( - INSTANCE_STATE_KEY, - MapSerializer(String.serializer(), String.serializer()) - ) ?: throw IllegalArgumentException() - ) + qualifier: Qualifier +): SavedState { + return getOrNull(qualifier).toSavedState() +} - } - } else null -} \ No newline at end of file diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt new file mode 100644 index 0000000..306292c --- /dev/null +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt @@ -0,0 +1,21 @@ +package com.merseyside.merseyLib.archy.core.presentation.ext + +import android.os.Bundle +import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.utils.ext.getSerialize +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer + +fun Bundle?.toSavedState(): SavedState { + return SavedState().apply { + if (this@toSavedState != null) { + addAll( + this@toSavedState.getSerialize( + INSTANCE_STATE_KEY, + MapSerializer(String.serializer(), String.serializer()) + ) ?: throw IllegalArgumentException() + ) + } + } +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt index 2365ca1..7effc18 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt @@ -6,6 +6,7 @@ import org.koin.core.definition.Definition import org.koin.core.instance.InstanceFactory import org.koin.core.module.Module import org.koin.core.qualifier.Qualifier +import org.koin.dsl.ScopeDSL inline fun Module.viewModel( qualifier: Qualifier? = null, @@ -14,16 +15,24 @@ inline fun Module.viewModel( return factory(qualifier, definition) } -inline fun Module.stateViewModel( +@Deprecated("Use stateViewModel", + ReplaceWith("ScopeDSL.stateViewModel") +) +inline fun Module.oldStateViewModel( qualifier: Qualifier? = null, noinline viewModelDefinition: Definition ): Pair> { return factory(qualifier) { - viewModelDefinition(it).apply { - getSavedState(it)?.let { savedState -> - onRestoreState(savedState) - } - } + viewModelDefinition(it) + } +} + +inline fun Module.stateViewModel( + qualifier: Qualifier? = null, + noinline viewModelDefinition: StateDefinition +): Pair> { + return factory(qualifier) { + viewModelDefinition(getSavedState(it), it) } } \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt index 6be252e..a459216 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -6,9 +6,8 @@ import org.koin.core.parameter.ParametersHolder import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope -expect fun getSavedState(parametersHolder: ParametersHolder): SavedState? +expect fun getSavedState(parametersHolder: ParametersHolder): SavedState expect fun Scope.getSavedState( - qualifier: Qualifier? = null, - parametersHolder: ParametersDefinition? = null -): SavedState? \ No newline at end of file + qualifier: Qualifier = stateQualifier +): SavedState \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt index d562948..5a0c384 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt @@ -6,8 +6,9 @@ import dev.icerock.moko.mvvm.viewmodel.ViewModel import org.koin.core.definition.Definition import org.koin.core.instance.InstanceFactory import org.koin.core.module.Module -import org.koin.core.parameter.ParametersDefinition +import org.koin.core.parameter.ParametersHolder import org.koin.core.qualifier.Qualifier +import org.koin.core.qualifier.named import org.koin.core.scope.Scope import org.koin.dsl.ScopeDSL @@ -18,27 +19,36 @@ inline fun ScopeDSL.viewModel( return factory(qualifier, definition) } -@Deprecated("Use newStateViewModel", - ReplaceWith("ScopeDSL.newStateViewModel") +@Deprecated("Use stateViewModel", + ReplaceWith("ScopeDSL.stateViewModel") ) -inline fun ScopeDSL.stateViewModel( +inline fun ScopeDSL.oldStateViewModel( qualifier: Qualifier? = null, noinline viewModelDefinition: Definition ): Pair> { return factory(qualifier) { - viewModelDefinition(it).apply { - getSavedState(it)?.let { state -> - onRestoreState(state) - } - } + viewModelDefinition(it) + } +} + +inline fun ScopeDSL.stateViewModel( + qualifier: Qualifier? = null, + noinline viewModelDefinition: StateDefinition +): Pair> { + return factory(qualifier) { + viewModelDefinition(getSavedState(it), it) } } + inline fun ScopeDSL.state( state: T?, - qualifier: Qualifier? = null + qualifier: Qualifier = stateQualifier ) { state?.let { scoped(qualifier) { state } } } +typealias StateDefinition = Scope.(SavedState, ParametersHolder) -> T + +val stateQualifier = named("saved_state_qualifier") diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt index d9a577d..86035c8 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/BaseViewModel.kt @@ -6,6 +6,7 @@ import com.merseyside.merseyLib.utils.core.ext.getString import com.merseyside.merseyLib.utils.core.ext.getStringNull import com.merseyside.merseyLib.utils.core.mvvm.MutableSingleEvent import dev.icerock.moko.mvvm.livedata.LiveData +import dev.icerock.moko.mvvm.livedata.MutableLiveData import dev.icerock.moko.mvvm.viewmodel.ViewModel import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.CoroutineScope @@ -19,7 +20,7 @@ abstract class BaseViewModel protected constructor() : ViewModel() { internal val scope: CoroutineScope get() { return viewModelScope } - private val mutProgress = MutableSingleEvent(false) + private val mutProgress = MutableLiveData(false) val isInProgress: LiveData = mutProgress protected var progress: Boolean diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt index 8ac5503..d1002b6 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt @@ -1,13 +1,11 @@ package com.merseyside.merseyLib.archy.core.presentation.model -import com.merseyside.merseyLib.utils.core.state.DummySavedState import com.merseyside.merseyLib.utils.core.state.SavedState import com.merseyside.merseyLib.utils.core.state.StateSaver +abstract class StateViewModel: BaseViewModel(), StateSaver { -abstract class StateViewModel( - override val savedState: SavedState = DummySavedState() -): BaseViewModel(), StateSaver { + abstract val savedState: SavedState companion object { const val INSTANCE_STATE_KEY = "instance_state" diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt index 522e576..39df4f7 100644 --- a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt @@ -6,13 +6,12 @@ import org.koin.core.parameter.ParametersHolder import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { +actual fun getSavedState(parametersHolder: ParametersHolder): SavedState { TODO() } actual inline fun Scope.getSavedState( - qualifier: Qualifier?, - noinline parametersHolder: ParametersDefinition? -): SavedState? { + qualifier: Qualifier +): SavedState { TODO() } \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/InstanceStateManager.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/InstanceStateManager.kt new file mode 100644 index 0000000..2dd6f76 --- /dev/null +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/InstanceStateManager.kt @@ -0,0 +1,7 @@ +package com.merseyside.merseyLib.utils.core.state + +interface InstanceStateManager { + fun saveState(savedState: SavedState, instance: Instance) + fun restoreState(savedState: SavedState): Instance + fun hasSavedState(savedState: SavedState): Boolean +} \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt index eaa4f61..9cf78f2 100644 --- a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt @@ -1,10 +1,6 @@ package com.merseyside.merseyLib.utils.core.state interface StateSaver { - val savedState: SavedState? - @Deprecated("Pass saved state in constructor") - fun onRestoreState(restoredState: SavedState) fun onSaveState(savedState: SavedState) - } \ No newline at end of file From 752d94e504aed8b3d88c1acc315f9d45ebb7911e Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Tue, 31 May 2022 20:12:00 +0700 Subject: [PATCH 09/13] Fix get koin modules --- .../merseyLib/archy/android/presentation/fragment/VMFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 38fc910..961d7f9 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -92,7 +92,7 @@ abstract class VMFragment viewModel = provideViewModel(bundle, *params) } - open fun getKoinModules(): List { + open fun getKoinModules(bundle: Bundle?, vararg params: Any): List { return emptyList().also { Logger.logInfo( "VMFragment", From 7e77fc25548e0b153f974d6bdb340b5d6492bc94 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Tue, 31 May 2022 20:16:00 +0700 Subject: [PATCH 10/13] Another koin modules fix --- .../archy/android/presentation/fragment/VMFragment.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 961d7f9..48fd583 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -88,16 +88,13 @@ abstract class VMFragment @CallSuper override fun performInjection(bundle: Bundle?, vararg params: Any) { - loadKoinModules(getKoinModules()) + loadKoinModules(getKoinModules(bundle, params)) viewModel = provideViewModel(bundle, *params) } open fun getKoinModules(bundle: Bundle?, vararg params: Any): List { return emptyList().also { - Logger.logInfo( - "VMFragment", - "Empty fragment's koin modules" - ) + Logger.logInfo("VMFragment", "Empty fragment's koin modules") } } From 2479061e52a10b5ac17c3635f5adc8b24ed97272 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Thu, 2 Jun 2022 20:10:56 +0700 Subject: [PATCH 11/13] Huge refactoring, add koin state holder, rewrite state saving/restoring --- .../archy/android/di/SharedScopeExt.kt | 2 +- .../di/state/AndroidKoinStateHolder.kt | 45 ++++++++++++++++ .../presentation/activity/VMActivity.kt | 31 ++++------- .../presentation/fragment/VMEventsFragment.kt | 14 ++--- .../presentation/fragment/VMFragment.kt | 26 ++------- .../archy/core/di/state/BundleExt.kt | 23 ++++++++ .../core/di/state/KoinParametersState.kt | 12 +++++ .../archy/core/di/state/SavedStateExt.kt | 23 ++++++++ .../core/presentation/di/KoinSavedState.kt | 27 ---------- .../archy/core/presentation/ext/BundleExt.kt | 21 -------- .../di/KoinStateViewModelOf.kt | 0 .../di/SharedScopeHolder.kt | 2 +- .../archy/core/di/dsl/KoinModuleExt.kt | 29 ++++++++++ .../archy/core/di/dsl/KoinScopeDSLExt.kt | 30 +++++++++++ .../core/di/ext/KoinSavedStateModuleExt.kt | 3 ++ .../core/di/ext/KoinSavedStateScopeExt.kt | 25 +++++++++ .../core/di/state/KoinParametersState.kt | 9 ++++ .../core/di/state/KoinSavedStateUtils.kt | 16 ++++++ .../archy/core/di/state/KoinStateHolder.kt | 41 ++++++++++++++ .../core/presentation/di/KoinModuleExt.kt | 38 ------------- .../core/presentation/di/KoinSavedState.kt | 13 ----- .../core/presentation/di/KoinScopedExt.kt | 54 ------------------- .../core/presentation/model/StateViewModel.kt | 13 ----- .../{presentation/di => di/ext}/KoinExt.kt | 2 +- .../core/di/state/KoinParametersState.kt | 8 +++ .../core/presentation/di/KoinSavedState.kt | 17 ------ .../merseyLib/utils/core/state/SavedState.kt | 45 ++++++++-------- .../merseyLib/utils/core/state/StateExt.kt | 7 +++ .../merseyLib/utils/core/state/StateSaver.kt | 2 +- 29 files changed, 317 insertions(+), 261 deletions(-) create mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/state/AndroidKoinStateHolder.kt create mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/BundleExt.kt create mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt create mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/SavedStateExt.kt delete mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt delete mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt rename archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/{presentation => }/di/KoinStateViewModelOf.kt (100%) rename archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/{presentation => }/di/SharedScopeHolder.kt (71%) create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinModuleExt.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinScopeDSLExt.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateModuleExt.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateScopeExt.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinSavedStateUtils.kt create mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinStateHolder.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt rename archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/{presentation/di => di/ext}/KoinExt.kt (93%) create mode 100644 archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt delete mode 100644 archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt create mode 100644 utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateExt.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt index 632da5d..735a127 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/SharedScopeExt.kt @@ -1,7 +1,7 @@ package com.merseyside.merseyLib.archy.android.di import androidx.fragment.app.Fragment -import com.merseyside.merseyLib.archy.core.presentation.di.SharedScopeHolder +import com.merseyside.merseyLib.archy.core.di.SharedScopeHolder import org.koin.core.scope.ScopeID fun Fragment.findParentSharedScopeHolder(scopeID: ScopeID? = null): SharedScopeHolder? { diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/state/AndroidKoinStateHolder.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/state/AndroidKoinStateHolder.kt new file mode 100644 index 0000000..31cff38 --- /dev/null +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/di/state/AndroidKoinStateHolder.kt @@ -0,0 +1,45 @@ +package com.merseyside.merseyLib.archy.android.di.state + +import android.os.Bundle +import com.merseyside.merseyLib.archy.core.di.state.KoinStateHolder +import com.merseyside.merseyLib.archy.core.di.state.toSavedState +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.archy.core.di.state.toBundle +import org.koin.android.scope.AndroidScopeComponent +import org.koin.core.context.loadKoinModules +import org.koin.core.qualifier.Qualifier +import org.koin.dsl.module + +interface AndroidKoinScopeState : AndroidScopeComponent { + + fun initKoinState(outState: Bundle?, toScope: Qualifier) { + loadKoinModules(getKoinStateModule(outState, toScope)) + } + + fun saveKoinState(outState: Bundle) { + val koinState = getKoinStateHolder().performSaveState() + outState.putBundle(koinStateKey, koinState.toBundle()) + } + + private fun getKoinSavedState(outState: Bundle?): SavedState { + return outState?.getBundle(koinStateKey).toSavedState() + } + + private fun getKoinStateModule(outState: Bundle?, toScope: Qualifier) = module { + scope(toScope) { + scoped { KoinStateHolder(getKoin(), getKoinSavedState(outState)) } + } + } + +// private fun getKoinStateScope(): Scope { +// return getKoin().getOrCreateScope( +// scopeId = linkableScope.id, +// qualifier = qualifier +// ) +// } + + fun getKoinStateHolder(): KoinStateHolder = scope.get() + +} + +private const val koinStateKey = "koin_state" diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt index 9b9b2a8..59fc5f3 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt @@ -4,21 +4,18 @@ import android.content.Context import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.archy.presentation.activity.BaseBindingActivity +import com.merseyside.merseyLib.archy.core.di.state.getStateKey import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.utils.core.state.SavedState -import com.merseyside.utils.ext.putSerialize +import com.merseyside.merseyLib.archy.core.di.state.saveState +import com.merseyside.merseyLib.utils.core.state.StateSaver import com.merseyside.utils.reflection.ReflectionUtils import com.merseyside.utils.requestPermissions -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer import kotlin.reflect.KClass -abstract class VMActivity - : BaseBindingActivity() { +abstract class VMActivity + : BaseBindingActivity() { - protected abstract val viewModel: M + protected abstract val viewModel: Model private val messageObserver = { message: BaseViewModel.TextMessage? -> if (message != null) { @@ -65,17 +62,7 @@ abstract class VMActivity override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - - if (viewModel is StateViewModel) { - val savedState = SavedState() - - (viewModel as StateViewModel).onSaveState(savedState) - outState.putSerialize( - INSTANCE_STATE_KEY, - savedState.getAll(), - MapSerializer(String.serializer(), String.serializer()) - ) - } + (viewModel as? StateSaver)?.saveState(outState, getStateKey(getViewModelClass())) } private fun observeViewModel() { @@ -113,11 +100,11 @@ abstract class VMActivity } } - protected fun getViewModelClass(): KClass { + protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, VMActivity::class.java, 1 - ).kotlin as KClass + ).kotlin as KClass } } diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt index b1174a1..f29b1fb 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt @@ -3,6 +3,7 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel +import com.merseyside.merseyLib.kotlin.extensions.log import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain @@ -20,15 +21,10 @@ abstract class VMEventsFragment>() - ) - } + return super.provideViewModel( + bundle, + *params, + eventsDispatcherOnMain>() ) } } \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 48fd583..5414ac9 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -8,17 +8,13 @@ import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding import com.google.android.material.snackbar.Snackbar import com.merseyside.archy.presentation.fragment.BaseBindingFragment +import com.merseyside.merseyLib.archy.core.di.state.getStateKey import com.merseyside.merseyLib.archy.core.presentation.model.BaseViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY import com.merseyside.merseyLib.kotlin.Logger -import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.archy.core.di.state.saveState import com.merseyside.merseyLib.utils.core.state.StateSaver -import com.merseyside.utils.ext.putSerialize import com.merseyside.utils.reflection.ReflectionUtils import com.merseyside.utils.requestPermissions -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.core.context.loadKoinModules import org.koin.core.module.Module @@ -101,12 +97,7 @@ abstract class VMFragment protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { return getViewModel( clazz = getViewModelClass(), - parameters = { - parametersOf( - *params, - bundle - ) - } + parameters = { parametersOf(*params, bundle) } ) } @@ -127,16 +118,7 @@ abstract class VMFragment override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - - if (viewModel is StateSaver) { - val bundle = SavedState() - - (viewModel as StateSaver).onSaveState(bundle) - outState.putSerialize( - INSTANCE_STATE_KEY, bundle.getAll(), - MapSerializer(String.serializer(), String.serializer()) - ) - } + (viewModel as? StateSaver)?.saveState(outState, getStateKey(getViewModelClass())) } override fun updateLanguage(context: Context) { diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/BundleExt.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/BundleExt.kt new file mode 100644 index 0000000..a29bfe5 --- /dev/null +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/BundleExt.kt @@ -0,0 +1,23 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import android.os.Bundle +import com.merseyside.merseyLib.utils.core.state.SavedState + +fun Bundle?.toSavedState(): SavedState { + val savedState = SavedState() + if (this != null) { + val keys = this@toSavedState.keySet() + keys.forEach { key -> + val value = get(key) + value?.let { + val newValue = if (value is Bundle) { + value.toSavedState() + } else value + + savedState.put(key, newValue) + } + } + } + + return savedState +} \ No newline at end of file diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt new file mode 100644 index 0000000..bf94c73 --- /dev/null +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt @@ -0,0 +1,12 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import android.os.Bundle +import com.merseyside.merseyLib.kotlin.extensions.log +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersHolder + +actual fun getSavedStateFromParams(parametersHolder: ParametersHolder, key: String): SavedState? { + return parametersHolder.getOrNull() + ?.getBundle(key) + ?.toSavedState() +} \ No newline at end of file diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/SavedStateExt.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/SavedStateExt.kt new file mode 100644 index 0000000..58e6b27 --- /dev/null +++ b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/SavedStateExt.kt @@ -0,0 +1,23 @@ +@file:JvmName("StateExtAndroid") +package com.merseyside.merseyLib.archy.core.di.state + +import android.os.Bundle +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.utils.core.state.StateSaver +import com.merseyside.merseyLib.utils.core.state.forEach +import com.merseyside.utils.ext.put + +fun SavedState.toBundle(): Bundle { + return Bundle().apply { + forEach { key, value -> + if (value is SavedState) put(key, value.toBundle()) + else this.put(key, value) + } + } +} + +inline fun T.saveState(outState: Bundle, key: String) { + val state = SavedState() + onSaveState(state) + outState.putBundle(key, state.toBundle()) +} \ No newline at end of file diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt deleted file mode 100644 index 86bebe5..0000000 --- a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import android.os.Bundle -import com.merseyside.merseyLib.archy.core.presentation.ext.toSavedState -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.kotlin.extensions.log -import com.merseyside.merseyLib.utils.core.state.SavedState -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer -import org.koin.core.parameter.ParametersHolder -import com.merseyside.utils.ext.getSerialize -import com.merseyside.utils.ext.isNotNullAndEmpty -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.qualifier.Qualifier -import org.koin.core.qualifier.named -import org.koin.core.scope.Scope - -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState { - return parametersHolder.getOrNull().toSavedState() -} - -actual fun Scope.getSavedState( - qualifier: Qualifier -): SavedState { - return getOrNull(qualifier).toSavedState() -} - diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt deleted file mode 100644 index 306292c..0000000 --- a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/ext/BundleExt.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.ext - -import android.os.Bundle -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.utils.core.state.SavedState -import com.merseyside.utils.ext.getSerialize -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer - -fun Bundle?.toSavedState(): SavedState { - return SavedState().apply { - if (this@toSavedState != null) { - addAll( - this@toSavedState.getSerialize( - INSTANCE_STATE_KEY, - MapSerializer(String.serializer(), String.serializer()) - ) ?: throw IllegalArgumentException() - ) - } - } -} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinStateViewModelOf.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/KoinStateViewModelOf.kt similarity index 100% rename from archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinStateViewModelOf.kt rename to archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/KoinStateViewModelOf.kt diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/SharedScopeHolder.kt similarity index 71% rename from archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt rename to archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/SharedScopeHolder.kt index 83c50ba..cdf3b79 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/SharedScopeHolder.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/SharedScopeHolder.kt @@ -1,4 +1,4 @@ -package com.merseyside.merseyLib.archy.core.presentation.di +package com.merseyside.merseyLib.archy.core.di import org.koin.core.scope.Scope import org.koin.core.scope.ScopeID diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinModuleExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinModuleExt.kt new file mode 100644 index 0000000..7b71d14 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinModuleExt.kt @@ -0,0 +1,29 @@ +package com.merseyside.merseyLib.archy.core.di.dsl + +import com.merseyside.merseyLib.archy.core.di.ext.getSavedStateFromParams +import com.merseyside.merseyLib.archy.core.di.state.StateDefinition +import com.merseyside.merseyLib.utils.core.state.StateSaver +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import org.koin.core.definition.Definition +import org.koin.core.instance.InstanceFactory +import org.koin.core.module.Module +import org.koin.core.qualifier.Qualifier + +inline fun Module.viewModel( + qualifier: Qualifier? = null, + noinline definition: Definition +): Pair> { + return factory(qualifier, definition) +} + +inline fun Module.stateViewModel( + qualifier: Qualifier? = null, + noinline viewModelDefinition: StateDefinition +): Pair> where T : ViewModel, T : StateSaver { + return factory(qualifier) { paramsHolder -> + viewModelDefinition( + getSavedStateFromParams(paramsHolder), + paramsHolder + ) + } +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinScopeDSLExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinScopeDSLExt.kt new file mode 100644 index 0000000..c602caf --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/dsl/KoinScopeDSLExt.kt @@ -0,0 +1,30 @@ +package com.merseyside.merseyLib.archy.core.di.dsl + +import com.merseyside.merseyLib.archy.core.di.ext.getSavedStateFromParams +import com.merseyside.merseyLib.archy.core.di.state.StateDefinition +import com.merseyside.merseyLib.utils.core.state.StateSaver +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import org.koin.core.definition.Definition +import org.koin.core.instance.InstanceFactory +import org.koin.core.module.Module +import org.koin.core.qualifier.Qualifier +import org.koin.dsl.ScopeDSL + +inline fun ScopeDSL.viewModel( + qualifier: Qualifier? = null, + noinline definition: Definition +): Pair> { + return factory(qualifier, definition) +} + +inline fun ScopeDSL.stateViewModel( + qualifier: Qualifier? = null, + noinline viewModelDefinition: StateDefinition +): Pair> where T : ViewModel, T : StateSaver { + return factory(qualifier) { paramsHolder -> + viewModelDefinition( + getSavedStateFromParams(paramsHolder), + paramsHolder + ) + } +} diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateModuleExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateModuleExt.kt new file mode 100644 index 0000000..1f67e3b --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateModuleExt.kt @@ -0,0 +1,3 @@ +package com.merseyside.merseyLib.archy.core.di.ext + + diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateScopeExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateScopeExt.kt new file mode 100644 index 0000000..850be71 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinSavedStateScopeExt.kt @@ -0,0 +1,25 @@ +package com.merseyside.merseyLib.archy.core.di.ext + +import com.merseyside.merseyLib.archy.core.di.state.KoinStateHolder +import com.merseyside.merseyLib.archy.core.di.state.getStateKey +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.utils.core.state.StateSaver +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope +import com.merseyside.merseyLib.archy.core.di.state.getSavedStateFromParams +import com.merseyside.merseyLib.utils.core.state.DummySavedState + +inline fun getSavedStateFromParams(paramsHolder: ParametersHolder): SavedState { + return getSavedStateFromParams(paramsHolder, getStateKey()) ?: DummySavedState() +} + +inline fun Scope.getSavedStateFromStateHolder(): SavedState { + val stateHolder = getKoinStateHolder() ?: throw NullPointerException() + return stateHolder.getSavedState() +} + +fun Scope.addStateSaver(stateSaver: StateSaver) { + getKoinStateHolder()?.addStateSaver(stateSaver) +} + +inline fun Scope.getKoinStateHolder(): KoinStateHolder? = getOrNull() \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt new file mode 100644 index 0000000..edc8ca5 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt @@ -0,0 +1,9 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersHolder + +expect fun getSavedStateFromParams( + parametersHolder: ParametersHolder, + key: String +): SavedState? \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinSavedStateUtils.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinSavedStateUtils.kt new file mode 100644 index 0000000..38d1521 --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinSavedStateUtils.kt @@ -0,0 +1,16 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope +import kotlin.reflect.KClass + +typealias StateDefinition = Scope.(SavedState, ParametersHolder) -> T + +fun getStateKey(clazz: KClass<*>): String { + return "${clazz.simpleName}_state_key" +} + +inline fun getStateKey(): String { + return "${T::class.simpleName}_state_key" +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinStateHolder.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinStateHolder.kt new file mode 100644 index 0000000..cc541ed --- /dev/null +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinStateHolder.kt @@ -0,0 +1,41 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import com.merseyside.merseyLib.utils.core.state.DummySavedState +import com.merseyside.merseyLib.utils.core.state.SavedState +import com.merseyside.merseyLib.utils.core.state.StateSaver +import org.koin.core.Koin +import org.koin.core.logger.Level + +class KoinStateHolder(val koin: Koin, val rootSavedState: SavedState) { + + private val stateSavers: MutableList = mutableListOf() + + fun addStateSaver(stateSaver: StateSaver) { + stateSavers.add(stateSaver) + koin.logger.log(Level.DEBUG) { " +++ Add $stateSaver state saver" } + } + + fun performSaveState(): SavedState { + stateSavers.forEach { saver -> + val newState = SavedState().apply { saver.onSaveState(this) } + rootSavedState.put(adoptStateKey(saver), newState) + koin.logger.log(Level.DEBUG) { " +++ Add to saved state by $saver: $newState" } + } + + return rootSavedState + } + + inline fun getSavedState(key: String = getStateKey()): SavedState { + koin.logger.log(Level.DEBUG) { " +++ Getting saved state by key $key" } + return (rootSavedState.getSavedState(key) ?: DummySavedState()).also { + koin.logger.log(Level.DEBUG) { " Result is $it" } + } + } + + companion object { + fun adoptStateKey(obj: Any): String { + return getStateKey(obj::class) + } + } + +} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt deleted file mode 100644 index 7effc18..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinModuleExt.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel -import dev.icerock.moko.mvvm.viewmodel.ViewModel -import org.koin.core.definition.Definition -import org.koin.core.instance.InstanceFactory -import org.koin.core.module.Module -import org.koin.core.qualifier.Qualifier -import org.koin.dsl.ScopeDSL - -inline fun Module.viewModel( - qualifier: Qualifier? = null, - noinline definition: Definition -): Pair> { - return factory(qualifier, definition) -} - -@Deprecated("Use stateViewModel", - ReplaceWith("ScopeDSL.stateViewModel") -) -inline fun Module.oldStateViewModel( - qualifier: Qualifier? = null, - noinline viewModelDefinition: Definition -): Pair> { - - return factory(qualifier) { - viewModelDefinition(it) - } -} - -inline fun Module.stateViewModel( - qualifier: Qualifier? = null, - noinline viewModelDefinition: StateDefinition -): Pair> { - return factory(qualifier) { - viewModelDefinition(getSavedState(it), it) - } -} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt deleted file mode 100644 index a459216..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.utils.core.state.SavedState -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.parameter.ParametersHolder -import org.koin.core.qualifier.Qualifier -import org.koin.core.scope.Scope - -expect fun getSavedState(parametersHolder: ParametersHolder): SavedState - -expect fun Scope.getSavedState( - qualifier: Qualifier = stateQualifier -): SavedState \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt deleted file mode 100644 index 5a0c384..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinScopedExt.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.archy.core.presentation.model.StateViewModel -import com.merseyside.merseyLib.utils.core.state.SavedState -import dev.icerock.moko.mvvm.viewmodel.ViewModel -import org.koin.core.definition.Definition -import org.koin.core.instance.InstanceFactory -import org.koin.core.module.Module -import org.koin.core.parameter.ParametersHolder -import org.koin.core.qualifier.Qualifier -import org.koin.core.qualifier.named -import org.koin.core.scope.Scope -import org.koin.dsl.ScopeDSL - -inline fun ScopeDSL.viewModel( - qualifier: Qualifier? = null, - noinline definition: Definition -): Pair> { - return factory(qualifier, definition) -} - -@Deprecated("Use stateViewModel", - ReplaceWith("ScopeDSL.stateViewModel") -) -inline fun ScopeDSL.oldStateViewModel( - qualifier: Qualifier? = null, - noinline viewModelDefinition: Definition -): Pair> { - - return factory(qualifier) { - viewModelDefinition(it) - } -} - -inline fun ScopeDSL.stateViewModel( - qualifier: Qualifier? = null, - noinline viewModelDefinition: StateDefinition -): Pair> { - return factory(qualifier) { - viewModelDefinition(getSavedState(it), it) - } -} - - -inline fun ScopeDSL.state( - state: T?, - qualifier: Qualifier = stateQualifier -) { - state?.let { scoped(qualifier) { state } } -} - -typealias StateDefinition = Scope.(SavedState, ParametersHolder) -> T - -val stateQualifier = named("saved_state_qualifier") diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt deleted file mode 100644 index d1002b6..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/model/StateViewModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.model - -import com.merseyside.merseyLib.utils.core.state.SavedState -import com.merseyside.merseyLib.utils.core.state.StateSaver - -abstract class StateViewModel: BaseViewModel(), StateSaver { - - abstract val savedState: SavedState - - companion object { - const val INSTANCE_STATE_KEY = "instance_state" - } -} \ No newline at end of file diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinExt.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinExt.kt similarity index 93% rename from archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinExt.kt rename to archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinExt.kt index 747d0c3..a8a7011 100644 --- a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinExt.kt +++ b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/ext/KoinExt.kt @@ -1,4 +1,4 @@ -package com.merseyside.merseyLib.archy.core.presentation.di +package com.merseyside.merseyLib.archy.core.di.ext import kotlinx.cinterop.ObjCClass import kotlinx.cinterop.getOriginalKotlinClass diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt new file mode 100644 index 0000000..540753a --- /dev/null +++ b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/di/state/KoinParametersState.kt @@ -0,0 +1,8 @@ +package com.merseyside.merseyLib.archy.core.di.state + +import com.merseyside.merseyLib.utils.core.state.SavedState +import org.koin.core.parameter.ParametersHolder + +actual fun getSavedStateFromParams(parametersHolder: ParametersHolder, key: String): SavedState? { + TODO() +} \ No newline at end of file diff --git a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt deleted file mode 100644 index 39df4f7..0000000 --- a/archy-core/src/iosMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import com.merseyside.merseyLib.utils.core.state.SavedState -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.parameter.ParametersHolder -import org.koin.core.qualifier.Qualifier -import org.koin.core.scope.Scope - -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState { - TODO() -} - -actual inline fun Scope.getSavedState( - qualifier: Qualifier -): SavedState { - TODO() -} \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt index ce33174..261cd9e 100644 --- a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/SavedState.kt @@ -4,31 +4,23 @@ import com.merseyside.merseyLib.kotlin.serialization.deserialize import com.merseyside.merseyLib.kotlin.serialization.serialize import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer open class SavedState { - val map: MutableMap = HashMap() + internal val container: MutableMap = HashMap() - fun addAll(map: Map) { + fun addAll(map: Map) { if (map.isNotEmpty()) { - this.map.putAll(map) + this.container.putAll(map) } } - fun getAll(): Map = map - fun contains(key: String): Boolean { - return map.contains(key) - } - - fun put(key: String, value: String) { - map[key] = value + return container.contains(key) } fun put(key: String, value: Any) { - map[key] = value.toString() + container[key] = value } inline fun putSerializable(key: String, value: T) { @@ -44,27 +36,38 @@ open class SavedState { } fun getString(key: String): String? { - return map[key] + return container[key] as? String } fun getBool(key: String): Boolean? { - return map[key]?.toBooleanStrictOrNull() + return container[key] as? Boolean } fun getInt(key: String): Int? { - return map[key]?.toIntOrNull() + return container[key] as? Int } fun getLong(key: String): Long? { - return map[key]?.toLongOrNull() + return container[key] as? Long } fun getFloat(key: String): Float? { - return map[key]?.toFloatOrNull() + return container[key] as? Float + } + + fun get(key: String): Any? { + return container[key] + } + + fun getSavedState(key: String): SavedState? { + return if (contains(key)) { + val value = get(key) + value as? SavedState ?: throw IllegalArgumentException("Value by $key key is not SavedState! \n $value") + } else null } inline fun getSerializable(key: String): T? { - return map[key]?.deserialize() + return get(key)?.deserialize() } @Throws(NullPointerException::class) @@ -76,11 +79,11 @@ open class SavedState { key: String, deserializationStrategy: DeserializationStrategy ): T? { - return map[key]?.deserialize(deserializationStrategy) + return get(key)?.deserialize(deserializationStrategy) } override fun toString(): String { - return map.serialize(MapSerializer(String.serializer(), String.serializer())) + return container.map { "${it.key} : ${it.value}"}.joinToString(separator = "\n") } } diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateExt.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateExt.kt new file mode 100644 index 0000000..3e202bc --- /dev/null +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateExt.kt @@ -0,0 +1,7 @@ +package com.merseyside.merseyLib.utils.core.state + +import com.merseyside.merseyLib.kotlin.extensions.forEachEntry + +fun SavedState.forEach(block: (String, Any) -> Unit) { + container.forEachEntry(block) +} \ No newline at end of file diff --git a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt index 9cf78f2..ae0e2d3 100644 --- a/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt +++ b/utils-core/src/commonMain/kotlin/com/merseyside/merseyLib/utils/core/state/StateSaver.kt @@ -1,6 +1,6 @@ package com.merseyside.merseyLib.utils.core.state interface StateSaver { - + val savedState: SavedState fun onSaveState(savedState: SavedState) } \ No newline at end of file From 11ad3fe2852576d3d40967f2891456e328f470df Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Wed, 8 Jun 2022 16:00:41 +0700 Subject: [PATCH 12/13] Separate views logic on base and events --- .../presentation/activity/VMActivity.kt | 87 +++---- .../presentation/activity/VMEventsActivity.kt | 72 +++++- .../android/presentation/dialog/VMDialog.kt | 86 ++++--- .../presentation/dialog/VMEventsDialog.kt | 69 +++++- .../presentation/fragment/NewVMFragment.kt | 215 ------------------ .../presentation/fragment/VMEventsFragment.kt | 55 ++++- .../presentation/fragment/VMFragment.kt | 126 +--------- .../core/presentation/di/KoinSavedState.kt | 48 ---- .../presentation/viewModel/BaseViewModel.kt | 182 +-------------- .../presentation/viewModel/EventsViewModel.kt | 2 +- .../viewModel/NewBaseViewModel.kt | 99 -------- .../merseyside/sample/view/MainActivity.kt | 15 +- 12 files changed, 277 insertions(+), 779 deletions(-) delete mode 100644 archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt delete mode 100644 archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt delete mode 100644 archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt index 2dbf0dc..2a86d70 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMActivity.kt @@ -2,53 +2,29 @@ package com.merseyside.merseyLib.archy.android.presentation.activity import android.content.Context import android.os.Bundle +import androidx.annotation.CallSuper import androidx.databinding.ViewDataBinding import com.merseyside.archy.presentation.activity.BaseBindingActivity import com.merseyside.merseyLib.archy.core.di.state.getStateKey import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel import com.merseyside.merseyLib.archy.core.di.state.saveState +import com.merseyside.merseyLib.kotlin.Logger import com.merseyside.merseyLib.utils.core.state.StateSaver import com.merseyside.utils.reflection.ReflectionUtils -import com.merseyside.utils.requestPermissions +import org.koin.androidx.viewmodel.ext.android.getViewModel +import org.koin.core.context.loadKoinModules +import org.koin.core.module.Module +import org.koin.core.parameter.parametersOf import kotlin.reflect.KClass abstract class VMActivity : BaseBindingActivity() { - protected abstract val viewModel: Model - - private val messageObserver = { message: BaseViewModel.TextMessage? -> - if (message != null) { - if (message.isError) { - showErrorMsg(message) - } else { - showMsg(message) - } - } - } - - private val loadingObserver = { isLoading: Boolean -> this.loadingObserver(isLoading) } - private val alertDialogModel = { model: BaseViewModel.AlertDialogModel? -> - model?.apply { - showAlertDialog(title, message, positiveButtonText, negativeButtonText, onPositiveClick, onNegativeClick, isCancelable) - } - - Unit - } - - private val permissionObserver = { pair: Pair, Int>? -> - if (pair != null) { - requestPermissions(*pair.first, requestCode = pair.second) - } - } + protected lateinit var viewModel: Model override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setBindingVariable() - - //viewModel.updateLanguage(this) - - observeViewModel() } abstract fun getBindingVariable(): Int @@ -60,22 +36,32 @@ abstract class VMActivity } } - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - (viewModel as? StateSaver)?.saveState(outState, getStateKey(getViewModelClass())) + protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return getViewModel( + clazz = getViewModelClass(), + parameters = { parametersOf(bundle, *params) } + ) + } + + @CallSuper + override fun performInjection(bundle: Bundle?, vararg params: Any) { + loadKoinModules(getKoinModules(bundle, *params)) + viewModel = provideViewModel(bundle, *params) } - private fun observeViewModel() { - viewModel.apply { - messageLiveEvent.ld().observe(this@VMActivity, messageObserver) - isInProgress.ld().observe(this@VMActivity, loadingObserver) - alertDialogLiveEvent.ld().observe(this@VMActivity, alertDialogModel) - grantPermissionLiveEvent.ld().observe(this@VMActivity, permissionObserver) + open fun getKoinModules(bundle: Bundle?, vararg params: Any): List { + return emptyList().also { + Logger.logInfo("VMFragment", "Empty fragment's koin modules") } } - override fun handleError(throwable: Throwable) { - viewModel.onError(throwable) + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + (viewModel as? StateSaver)?.saveState(outState, getStateKey(getViewModelClass())) + } + + override fun handleError(throwable: Throwable): Boolean { + return viewModel.onError(throwable) } override fun updateLanguage(context: Context) { @@ -84,22 +70,7 @@ abstract class VMActivity protected abstract fun loadingObserver(isLoading: Boolean) - private fun showErrorMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showErrorMsg(textMessage.msg) - } else { - showErrorMsg(textMessage.msg, null, textMessage.actionMsg!!, textMessage.onClick) - } - } - - private fun showMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showMsg(textMessage.msg) - } else { - showMsg(textMessage.msg, null, textMessage.actionMsg!!, textMessage.onClick) - } - } - + @Suppress("UNCHECKED_CAST") protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt index 72c0c0f..76a8eff 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/activity/VMEventsActivity.kt @@ -3,15 +3,81 @@ package com.merseyside.merseyLib.archy.android.presentation.activity import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.EventsViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.Alert +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.TextMessage +import com.merseyside.merseyLib.kotlin.extensions.isNotNullAndEmpty +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner +import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain -abstract class VMEventsActivity - : VMActivity() where M : BaseViewModel, M: EventsDispatcherOwner { +abstract class VMEventsActivity + : VMActivity(), EventsViewModel.BaseEventsListener + where Model : EventsViewModel, + Listener : EventsViewModel.BaseEventsListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @Suppress("UNCHECKED_CAST") - viewModel.eventsDispatcher.bind(this, this as Listener) + (viewModel as EventsDispatcherOwner).eventsDispatcher.bind( + this, + this as Listener + ) + } + + override fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return super.provideViewModel( + bundle, + *params, + eventsDispatcherOnMain>() + ) + } + + override fun onAlert(alert: Alert) { + with(alert) { + showAlertDialog( + title, + message, + positiveButtonText, + negativeButtonText, + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ) + } + } + + override fun onError(throwable: Throwable): Boolean { + return this.handleError(throwable) + } + + override fun onMessage(message: TextMessage) { + if (message.isError) { + showErrorMsg(message) + } else { + showMsg(message) + } + } + + private fun showErrorMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showErrorMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showErrorMsg(textMessage.msg) + } + } + } + + private fun showMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showMsg(textMessage.msg) + } + } } } \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt index 3a8bd3e..d526f4b 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMDialog.kt @@ -2,36 +2,23 @@ package com.merseyside.merseyLib.archy.android.presentation.dialog import android.app.Dialog import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import androidx.databinding.ViewDataBinding import com.merseyside.archy.presentation.dialog.BaseBindingDialog import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.TextMessage +import com.merseyside.merseyLib.kotlin.Logger +import com.merseyside.merseyLib.kotlin.extensions.isNotNullAndEmpty import com.merseyside.utils.reflection.ReflectionUtils +import org.koin.androidx.viewmodel.ext.android.getViewModel +import org.koin.core.context.loadKoinModules +import org.koin.core.module.Module +import org.koin.core.parameter.parametersOf import kotlin.reflect.KClass -abstract class VMDialog : BaseBindingDialog() { +abstract class VMDialog + : BaseBindingDialog() { - protected lateinit var viewModel: M - - private val errorObserver = { throwable: Throwable? -> - throwable?.let { - this.handleError(it) - } - Unit - } - - private val messageObserver = { message: BaseViewModel.TextMessage? -> - message?.let { - if (it.isError) { - showErrorMsg(it) - } else { - showMsg(it) - } - } - Unit - } + protected lateinit var viewModel: Model abstract fun getBindingVariable(): Int @@ -50,40 +37,51 @@ abstract class VMDialog : BaseBindingDia return dialog } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - viewModel.apply { - errorLiveEvent.ld().observe(this@VMDialog, errorObserver) - messageLiveEvent.ld().observe(this@VMDialog, messageObserver) + override fun performInjection(bundle: Bundle?, vararg params: Any) { + loadKoinModules(getKoinModules()) + viewModel = provideViewModel(bundle, params) + } + + open fun getKoinModules(): List { + return emptyList().also { + Logger.logInfo("VMFragment", "Empty fragment's koin modules") } - return super.onCreateView(inflater, container, savedInstanceState) } - private fun showMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showMsg(textMessage.msg) - } else { - showMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) + protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return getViewModel( + clazz = getViewModelClass(), + parameters = { parametersOf(bundle, *params) } + ) + } + + private fun showErrorMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showErrorMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showErrorMsg(textMessage.msg) + } } } - private fun showErrorMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showErrorMsg(textMessage.msg) - } else { - showErrorMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) + private fun showMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showMsg(textMessage.msg) + } } } - protected open fun getPersistentClass(): KClass { + @Suppress("UNCHECKED_CAST") + protected open fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, VMDialog::class.java, 1 - ).kotlin as KClass + ).kotlin as KClass } } \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt index 354bb9f..690a458 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/dialog/VMEventsDialog.kt @@ -2,12 +2,18 @@ package com.merseyside.merseyLib.archy.android.presentation.dialog import android.os.Bundle import androidx.databinding.ViewDataBinding -import com.merseyside.merseyLib.archy.android.presentation.fragment.VMFragment -import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.EventsViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.Alert +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.TextMessage +import com.merseyside.merseyLib.kotlin.extensions.isNotNullAndEmpty +import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner +import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain -abstract class VMEventsDialog - : VMFragment() where M : BaseViewModel { +abstract class VMEventsDialog : + VMDialog(), EventsViewModel.BaseEventsListener + where Model : EventsViewModel, + Listener : EventsViewModel.BaseEventsListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -15,4 +21,59 @@ abstract class VMEventsDialog @Suppress("UNCHECKED_CAST") (viewModel as EventsDispatcherOwner).eventsDispatcher.bind(this, this as Listener) } + + override fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { + return super.provideViewModel( + bundle, + *params, + eventsDispatcherOnMain>() + ) + } + + override fun onAlert(alert: Alert) { + with(alert) { + showAlertDialog( + title, + message, + positiveButtonText, + negativeButtonText, + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ) + } + } + + override fun onError(throwable: Throwable): Boolean { + return this.handleError(throwable) + } + + override fun onMessage(message: TextMessage) { + if (message.isError) { + showErrorMsg(message) + } else { + showMsg(message) + } + } + + private fun showErrorMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showErrorMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showErrorMsg(textMessage.msg) + } + } + } + + private fun showMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showMsg(textMessage.msg) + } + } + } } \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt deleted file mode 100644 index d1adf2c..0000000 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/NewVMFragment.kt +++ /dev/null @@ -1,215 +0,0 @@ -package com.merseyside.merseyLib.archy.android.presentation.fragment - -import android.content.Context -import android.os.Bundle -import android.view.View -import androidx.annotation.StringRes -import androidx.databinding.ViewDataBinding -import com.google.android.material.snackbar.Snackbar -import com.merseyside.archy.presentation.fragment.BaseBindingFragment -import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel -import com.merseyside.merseyLib.archy.core.presentation.viewModel.NewBaseViewModel -import com.merseyside.merseyLib.kotlin.Logger -import com.merseyside.merseyLib.utils.core.state.SavedState -import com.merseyside.utils.ext.getSerialize -import com.merseyside.utils.ext.putSerialize -import com.merseyside.utils.reflection.ReflectionUtils -import com.merseyside.utils.requestPermissions -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer -import org.koin.androidx.viewmodel.ext.android.getViewModel -import org.koin.core.context.loadKoinModules -import org.koin.core.module.Module -import org.koin.core.parameter.parametersOf -import kotlin.reflect.KClass - -abstract class NewVMFragment - : BaseBindingFragment() { - - protected lateinit var viewModel: Model - - private val messageObserver = { message: BaseViewModel.TextMessage? -> - if (message != null) { - if (message.isError) { - showErrorMsg(message) - } else { - showMsg(message) - } - } - } - - private val errorObserver = { throwable: Throwable? -> - if (throwable != null) { - this.handleError(throwable) - } - } - - private val progressObserver = { isLoading: Boolean? -> - this.loadingObserver(isLoading ?: false) - } - - private val alertDialogModelObserver = { model: BaseViewModel.AlertDialogModel? -> - model?.apply { - showAlertDialog( - title, - message, - positiveButtonText, - negativeButtonText, - onPositiveClick, - onNegativeClick, - isSingleAction, - isCancelable - ) - } - Unit - } - - private val permissionObserver = { pair: Pair, Int>? -> - if (pair != null) { - requestPermissions(*pair.first, requestCode = pair.second) - } - } - - abstract fun getBindingVariable(): Int - - open fun initDataBinding(binding: Binding) { - binding.apply { - setVariable(getBindingVariable(), viewModel) - executePendingBindings() - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(false) - } - - override fun performInjection(bundle: Bundle?, vararg params: Any) { - loadKoinModules(getKoinModules()) - viewModel = provideViewModel(bundle, params) - } - - open fun getKoinModules(): List { - return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } - } - - protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { - return getViewModel( - clazz = getViewModelClass(), - parameters = { - parametersOf( - *params, - bundle - ) - } - ) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - - initDataBinding(requireBinding()) - - viewModel.apply { - errorLiveEvent.ld().observe(viewLifecycleOwner, errorObserver) - messageLiveEvent.ld().observe(viewLifecycleOwner, messageObserver) - isInProgress.ld().observe(viewLifecycleOwner, progressObserver) - alertDialogLiveEvent.ld().observe(viewLifecycleOwner, alertDialogModelObserver) - grantPermissionLiveEvent.ld().observe(viewLifecycleOwner, permissionObserver) - } - - super.onViewCreated(view, savedInstanceState) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - - if (viewModel is StateViewModel) { - val bundle = SavedState() - - (viewModel as StateViewModel).onSaveState(bundle) - outState.putSerialize( - INSTANCE_STATE_KEY, bundle.getAll(), - MapSerializer(String.serializer(), String.serializer()) - ) - } - } - - override fun onViewStateRestored(savedInstanceState: Bundle?) { - val savedState = SavedState().apply { - savedInstanceState?.getSerialize( - INSTANCE_STATE_KEY, MapSerializer(String.serializer(), String.serializer()) - )?.let { addAll(it) } - } - if (viewModel is StateViewModel) { - (viewModel as StateViewModel).onRestoreState(savedState) - } - super.onViewStateRestored(savedInstanceState) - } - - override fun updateLanguage(context: Context) { - super.updateLanguage(context) - //viewModel.updateLanguage(context) - } - - protected open fun loadingObserver(isLoading: Boolean) {} - - fun showErrorMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showErrorMsg(textMessage.msg) - } else { - showErrorMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) - } - } - - private fun showMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showMsg(textMessage.msg) - } else { - showMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) - } - } - - fun showInteractSnack( - @StringRes text: Int, - @StringRes clickButtonText: Int, - onClick: () -> Unit - ) { - Snackbar.make( - requireView(), - getString(text), - Snackbar.LENGTH_INDEFINITE - ).setAction( - getString(clickButtonText) - ) { - onClick() - }.show() - } - - protected fun showProgress() { - viewModel.showProgress() - } - - protected fun hideProgress() { - viewModel.hideProgress() - } - - override fun onDestroyView() { - super.onDestroyView() - - viewModel.apply { - errorLiveEvent.removeObserver(errorObserver) - messageLiveEvent.removeObserver(messageObserver) - isInProgress.removeObserver(progressObserver) - alertDialogLiveEvent.removeObserver(alertDialogModelObserver) - grantPermissionLiveEvent.removeObserver(permissionObserver) - } - } - - protected fun getViewModelClass(): KClass { - return ReflectionUtils.getGenericParameterClass( - this.javaClass, - NewVMFragment::class.java, - 1 - ).kotlin as KClass - } -} \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt index 10157d7..5ca1958 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMEventsFragment.kt @@ -3,15 +3,17 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.os.Bundle import androidx.databinding.ViewDataBinding import com.merseyside.merseyLib.archy.core.presentation.viewModel.EventsViewModel +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.Alert +import com.merseyside.merseyLib.archy.core.presentation.viewModel.entity.TextMessage +import com.merseyside.merseyLib.kotlin.extensions.isNotNullAndEmpty import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner import dev.icerock.moko.mvvm.dispatcher.eventsDispatcherOnMain abstract class VMEventsFragment : - NewVMFragment(), EventsViewModel.BaseEventsListener + VMFragment(), EventsViewModel.BaseEventsListener where Model : EventsViewModel, - Listener : EventsViewModel.BaseEventsListener -{ + Listener : EventsViewModel.BaseEventsListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -30,4 +32,51 @@ abstract class VMEventsFragment : eventsDispatcherOnMain>() ) } + + override fun onAlert(alert: Alert) { + with(alert) { + showAlertDialog( + title, + message, + positiveButtonText, + negativeButtonText, + onPositiveClick, + onNegativeClick, + isSingleAction, + isCancelable + ) + } + } + + override fun onError(throwable: Throwable): Boolean { + return this.handleError(throwable) + } + + override fun onMessage(message: TextMessage) { + if (message.isError) { + showErrorMsg(message) + } else { + showMsg(message) + } + } + + private fun showErrorMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showErrorMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showErrorMsg(textMessage.msg) + } + } + } + + private fun showMsg(textMessage: TextMessage) { + with(textMessage) { + if (actionMsg.isNotNullAndEmpty()) { + showMsg(textMessage.msg, null, actionMsg, textMessage.onClick) + } else { + showMsg(textMessage.msg) + } + } + } } \ No newline at end of file diff --git a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt index 4fa87d5..1d332b3 100644 --- a/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt +++ b/archy-android/src/main/java/com/merseyside/merseyLib/archy/android/presentation/fragment/VMFragment.kt @@ -3,18 +3,14 @@ package com.merseyside.merseyLib.archy.android.presentation.fragment import android.content.Context import android.os.Bundle import android.view.View -import androidx.annotation.CallSuper -import androidx.annotation.StringRes import androidx.databinding.ViewDataBinding -import com.google.android.material.snackbar.Snackbar import com.merseyside.archy.presentation.fragment.BaseBindingFragment import com.merseyside.merseyLib.archy.core.di.state.getStateKey +import com.merseyside.merseyLib.archy.core.di.state.saveState import com.merseyside.merseyLib.archy.core.presentation.viewModel.BaseViewModel import com.merseyside.merseyLib.kotlin.Logger -import com.merseyside.merseyLib.archy.core.di.state.saveState import com.merseyside.merseyLib.utils.core.state.StateSaver import com.merseyside.utils.reflection.ReflectionUtils -import com.merseyside.utils.requestPermissions import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.core.context.loadKoinModules import org.koin.core.module.Module @@ -24,49 +20,7 @@ import kotlin.reflect.KClass abstract class VMFragment : BaseBindingFragment() { - lateinit var viewModel: Model - - private val messageObserver = { message: BaseViewModel.TextMessage? -> - if (message != null) { - if (message.isError) { - showErrorMsg(message) - } else { - showMsg(message) - } - } - } - - private val errorObserver = { throwable: Throwable? -> - if (throwable != null) { - this.handleError(throwable) - } - } - - private val progressObserver = { isLoading: Boolean? -> - this.loadingObserver(isLoading ?: false) - } - - private val alertDialogModelObserver = { model: BaseViewModel.AlertDialogModel? -> - model?.apply { - showAlertDialog( - title, - message, - positiveButtonText, - negativeButtonText, - onPositiveClick, - onNegativeClick, - isSingleAction, - isCancelable - ) - } - Unit - } - - private val permissionObserver = { pair: Pair, Int>? -> - if (pair != null) { - requestPermissions(*pair.first, requestCode = pair.second) - } - } + protected lateinit var viewModel: Model abstract fun getBindingVariable(): Int @@ -82,16 +36,13 @@ abstract class VMFragment setHasOptionsMenu(false) } - @CallSuper override fun performInjection(bundle: Bundle?, vararg params: Any) { - loadKoinModules(getKoinModules(bundle, params)) - viewModel = provideViewModel(bundle, *params) + loadKoinModules(getKoinModules()) + viewModel = provideViewModel(bundle, params) } - open fun getKoinModules(bundle: Bundle?, vararg params: Any): List { - return emptyList().also { - Logger.logInfo("VMFragment", "Empty fragment's koin modules") - } + open fun getKoinModules(): List { + return emptyList().also { Logger.logInfo("VMFragment", "Empty fragment's koin modules") } } protected open fun provideViewModel(bundle: Bundle?, vararg params: Any): Model { @@ -102,17 +53,7 @@ abstract class VMFragment } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - initDataBinding(requireBinding()) - - viewModel.apply { - errorLiveEvent.ld().observe(viewLifecycleOwner, errorObserver) - messageLiveEvent.ld().observe(viewLifecycleOwner, messageObserver) - isInProgress.ld().observe(viewLifecycleOwner, progressObserver) - alertDialogLiveEvent.ld().observe(viewLifecycleOwner, alertDialogModelObserver) - grantPermissionLiveEvent.ld().observe(viewLifecycleOwner, permissionObserver) - } - super.onViewCreated(view, savedInstanceState) } @@ -126,60 +67,7 @@ abstract class VMFragment //viewModel.updateLanguage(context) } - protected open fun loadingObserver(isLoading: Boolean) {} - - fun showErrorMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showErrorMsg(textMessage.msg) - } else { - showErrorMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) - } - } - - private fun showMsg(textMessage: BaseViewModel.TextMessage) { - if (textMessage.actionMsg.isNullOrEmpty()) { - showMsg(textMessage.msg) - } else { - showMsg(textMessage.msg, null, textMessage.actionMsg, textMessage.onClick) - } - } - - fun showInteractSnack( - @StringRes text: Int, - @StringRes clickButtonText: Int, - onClick: () -> Unit - ) { - Snackbar.make( - requireView(), - getString(text), - Snackbar.LENGTH_INDEFINITE - ).setAction( - getString(clickButtonText) - ) { - onClick() - }.show() - } - - protected fun showProgress() { - viewModel.showProgress() - } - - protected fun hideProgress() { - viewModel.hideProgress() - } - - override fun onDestroyView() { - super.onDestroyView() - - viewModel.apply { - errorLiveEvent.removeObserver(errorObserver) - messageLiveEvent.removeObserver(messageObserver) - isInProgress.removeObserver(progressObserver) - alertDialogLiveEvent.removeObserver(alertDialogModelObserver) - grantPermissionLiveEvent.removeObserver(permissionObserver) - } - } - + @Suppress("UNCHECKED_CAST") protected fun getViewModelClass(): KClass { return ReflectionUtils.getGenericParameterClass( this.javaClass, diff --git a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt b/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt deleted file mode 100644 index eb3b0ec..0000000 --- a/archy-core/src/androidMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/di/KoinSavedState.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.di - -import android.os.Bundle -import com.merseyside.merseyLib.archy.core.presentation.viewModel.StateViewModel.Companion.INSTANCE_STATE_KEY -import com.merseyside.merseyLib.utils.core.state.SavedState -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer -import org.koin.core.parameter.ParametersHolder -import com.merseyside.utils.ext.getSerialize -import com.merseyside.utils.ext.isNotNullAndEmpty -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.qualifier.Qualifier -import org.koin.core.scope.Scope - -actual fun getSavedState(parametersHolder: ParametersHolder): SavedState? { - val bundle = parametersHolder.getOrNull() - - return if (bundle.isNotNullAndEmpty()) { - SavedState().apply { - addAll( - bundle.getSerialize( - INSTANCE_STATE_KEY, - MapSerializer(String.serializer(), String.serializer()) - ) ?: throw IllegalArgumentException() - ) - - } - } else null - -} - -actual fun Scope.getSavedState( - qualifier: Qualifier?, - parametersHolder: ParametersDefinition? -): SavedState? { - val bundle = getOrNull(qualifier) - return if (bundle.isNotNullAndEmpty()) { - SavedState().apply { - addAll( - bundle.getSerialize( - INSTANCE_STATE_KEY, - MapSerializer(String.serializer(), String.serializer()) - ) ?: throw IllegalArgumentException() - ) - - } - } else null -} \ No newline at end of file diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt index d1bab4a..5c09cb4 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/BaseViewModel.kt @@ -1,27 +1,20 @@ package com.merseyside.merseyLib.archy.core.presentation.viewModel -import com.merseyside.merseyLib.kotlin.Logger import com.merseyside.merseyLib.kotlin.coroutines.ext.mapState -import com.merseyside.merseyLib.utils.core.ext.getString -import com.merseyside.merseyLib.utils.core.ext.getStringNull -import com.merseyside.merseyLib.utils.core.mvvm.MutableSingleEvent -import dev.icerock.moko.mvvm.livedata.LiveData -import dev.icerock.moko.mvvm.livedata.MutableLiveData import dev.icerock.moko.mvvm.viewmodel.ViewModel -import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.* abstract class BaseViewModel protected constructor() : ViewModel() { - internal val scope: CoroutineScope - get() { return viewModelScope } +// internal val scope: CoroutineScope +// get() { return viewModelScope } - private val mutProgress = MutableLiveData(false) - val isInProgress: LiveData = mutProgress + private val mutProgress = MutableStateFlow(false) + val isInProgress = mutProgress.asStateFlow() + + private val mutProgressText = MutableStateFlow(null) + val progressText = mutProgressText.asStateFlow() protected var progress: Boolean get() = mutProgress.value @@ -29,177 +22,21 @@ abstract class BaseViewModel protected constructor() : ViewModel() { mutProgress.value = value } - private val mutProgressText = MutableSingleEvent(null) - val progressText: LiveData = mutProgressText - - private val mutErrorLiveEvent = MutableSingleEvent(null) - val errorLiveEvent: LiveData = mutErrorLiveEvent - - private val mutMessageLiveEvent = MutableSingleEvent(null) - val messageLiveEvent: LiveData = mutMessageLiveEvent - - private val mutAlertDialogLiveEvent = MutableSingleEvent(null) - val alertDialogLiveEvent: LiveData = mutAlertDialogLiveEvent - - private val mutGrantPermissionLiveEvent = MutableSingleEvent, Int>?>(null) - val grantPermissionLiveEvent: LiveData, Int>?> = mutGrantPermissionLiveEvent - - private val mutQueryRestoreEvent = MutableSingleEvent(null) - val queryRestoreEvent: LiveData = mutQueryRestoreEvent - - data class TextMessage( - val isError: Boolean = false, - var msg: String = "", - var actionMsg: String? = null, - val onClick: () -> Unit = {} - ) - - data class AlertDialogModel( - val title: String? = null, - val message: String? = null, - val positiveButtonText: String? = null, - val negativeButtonText: String? = null, - val onPositiveClick: () -> Unit = {}, - val onNegativeClick: () -> Unit = {}, - val isSingleAction: Boolean? = null, - val isCancelable: Boolean? = null - ) - - /** - * @return true if - * @param throwable have been handled - **/ - open fun handleError(throwable: Throwable): Boolean { - mutErrorLiveEvent.value = throwable - return true - } - - protected fun showMsg(id: StringResource, vararg args: String) { - showMsg(getString(id, *args)) - } - - protected fun showErrorMsg(id: StringResource, vararg args: String) { - showErrorMsg(getString(id, *args)) - } - - protected fun showMsg(msg: String) { - Logger.log(this, msg) - val textMessage = TextMessage( - isError = false, - msg = msg - ) - - mutMessageLiveEvent.value = textMessage - } - - protected fun showErrorMsg(msg: String) { - Logger.logErr(this, msg) - val textMessage = - TextMessage( - isError = true, - msg = msg - ) - - mutMessageLiveEvent.value = textMessage - } - - protected fun showMsg(msg: String, actionMsg: String, onClick: () -> Unit = {}) { - Logger.log(this, msg) - val textMessage = - TextMessage( - isError = false, - msg = msg, - actionMsg = actionMsg, - onClick = onClick - ) - - mutMessageLiveEvent.value = textMessage - } - - protected fun showErrorMsg(msg: String, actionMsg: String, onClick: () -> Unit = {}) { - Logger.logErr(this, msg) - val textMessage = - TextMessage( - isError = true, - msg = msg, - actionMsg = actionMsg, - onClick = onClick - ) - - mutMessageLiveEvent.value = textMessage - } - - open fun onError(throwable: Throwable) {} + open fun onError(throwable: Throwable): Boolean = false fun showProgress(text: String? = null) { - Logger.log(this, text ?: "Empty") - - progress = true mutProgressText.value = text - progress = true } fun hideProgress() { if (progress) { - progress = false mutProgressText.value = null - progress = false } } - fun showAlertDialog( - title: String? = null, - message: String? = null, - positiveButtonText: String? = null, - negativeButtonText: String? = null, - onPositiveClick: () -> Unit = {}, - onNegativeClick: () -> Unit = {}, - isSingleAction: Boolean? = null, - isCancelable: Boolean? = null - ) { - mutAlertDialogLiveEvent.value = - AlertDialogModel( - title, - message, - positiveButtonText, - negativeButtonText, - onPositiveClick, - onNegativeClick, - isSingleAction, - isCancelable - ) - } - - fun showAlertDialog( - titleRes: StringResource? = null, - messageRes: StringResource? = null, - positiveButtonTextRes: StringResource? = null, - negativeButtonTextRes: StringResource? = null, - onPositiveClick: () -> Unit = {}, - onNegativeClick: () -> Unit = {}, - isSingleAction: Boolean? = null, - isCancelable: Boolean? = null - ) { - - showAlertDialog( - getStringNull(titleRes), - getStringNull(messageRes), - getStringNull(positiveButtonTextRes), - getStringNull(negativeButtonTextRes), - onPositiveClick, - onNegativeClick, - isSingleAction, - isCancelable - ) - } - - fun restoreQuery(query: String) { - mutQueryRestoreEvent.value = query - } - open fun onBack(): Boolean { return true } @@ -207,7 +44,6 @@ abstract class BaseViewModel protected constructor() : ViewModel() { - fun StateFlow.mapState( transform: (data: T) -> K ): StateFlow { diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt index 4dc041c..c50fa50 100644 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt +++ b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/EventsViewModel.kt @@ -8,7 +8,7 @@ import com.merseyside.merseyLib.utils.core.ext.getStringNull import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher import dev.icerock.moko.resources.StringResource -abstract class EventsViewModel : NewBaseViewModel() { +abstract class EventsViewModel : BaseViewModel() { abstract val eventsDispatcher: EventsDispatcher diff --git a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt b/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt deleted file mode 100644 index 346d373..0000000 --- a/archy-core/src/commonMain/kotlin/com/merseyside/merseyLib/archy/core/presentation/viewModel/NewBaseViewModel.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.merseyside.merseyLib.archy.core.presentation.viewModel - -import com.merseyside.merseyLib.kotlin.coroutines.ext.mapState -import dev.icerock.moko.mvvm.viewmodel.ViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* - -abstract class NewBaseViewModel protected constructor() : ViewModel() { - -// internal val scope: CoroutineScope -// get() { return viewModelScope } - - private val mutProgress = MutableStateFlow(false) - val isInProgress = mutProgress.asStateFlow() - - private val mutProgressText = MutableStateFlow(null) - val progressText = mutProgressText.asStateFlow() - - protected var progress: Boolean - get() = mutProgress.value - set(value) { - mutProgress.value = value - } - - open fun onError(throwable: Throwable) {} - - - fun showProgress(text: String? = null) { - mutProgressText.value = text - progress = true - } - - fun hideProgress() { - if (progress) { - mutProgressText.value = null - progress = false - } - } - - open fun onBack(): Boolean { - return true - } - - - - - fun StateFlow.mapState( - transform: (data: T) -> K - ): StateFlow { - return mapState( - scope = viewModelScope, - transform = transform - ) - } - - fun StateFlow.mapState( - initialValue: K, - transform: suspend (data: T) -> K - ): StateFlow { - return mapState( - scope = viewModelScope, - initialValue = initialValue, - transform = transform - ) - } - - fun combineState( - flow1: StateFlow, - flow2: StateFlow, - scope: CoroutineScope = viewModelScope, - sharingStarted: SharingStarted = SharingStarted.Eagerly, - transform: (T1, T2) -> R - ): StateFlow = combine(flow1, flow2) { - o1, o2 -> transform.invoke(o1, o2) - }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value)) - - fun combineState( - flow1: StateFlow, - flow2: StateFlow, - flow3: StateFlow, - scope: CoroutineScope = viewModelScope, - sharingStarted: SharingStarted = SharingStarted.Eagerly, - transform: (T1, T2, T3) -> R - ): StateFlow = combine(flow1, flow2, flow3) { - o1, o2, o3 -> transform.invoke(o1, o2, o3) - }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value, flow3.value)) - - fun combineState( - flow1: StateFlow, - flow2: StateFlow, - flow3: StateFlow, - flow4: StateFlow, - scope: CoroutineScope = viewModelScope, - sharingStarted: SharingStarted = SharingStarted.Eagerly, - transform: (T1, T2, T3, T4) -> R - ): StateFlow = combine(flow1, flow2, flow3, flow4) { - o1, o2, o3, o4 -> transform.invoke(o1, o2, o3, o4) - }.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value, flow3.value, flow4.value)) -} \ No newline at end of file diff --git a/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/MainActivity.kt b/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/MainActivity.kt index a6ca99a..e90d0af 100644 --- a/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/MainActivity.kt +++ b/sample/androidApp/src/main/kotlin/com/merseyside/sample/view/MainActivity.kt @@ -13,18 +13,9 @@ class MainActivity : BaseBindingActivity() { private val test by inject() - override fun getFragmentContainer(): Int? { - return null - } - - override fun getLayoutId(): Int { - return R.layout.activity_main - } - - override fun getToolbar(): Toolbar? { - return null - } - + override fun getFragmentContainer() = null + override fun getLayoutId() = R.layout.activity_main + override fun getToolbar() = null override fun performInjection(bundle: Bundle?, vararg params: Any) {} override fun onCreate(savedInstanceState: Bundle?) { From 1da978869df4dcdd35e9c71a8b483c87989a5055 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Fri, 17 Jun 2022 16:15:35 +0700 Subject: [PATCH 13/13] ++ver 1.2.0 Build refactroing --- archy-android/build.gradle.kts | 1 + archy-android/src/main/AndroidManifest.xml | 3 +-- archy-core/build.gradle.kts | 1 + archy-core/src/androidMain/AndroidManifest.xml | 2 +- buildSrc/build.gradle.kts | 2 ++ buildSrc/settings.gradle.kts | 2 +- buildSrc/src/main/kotlin/publication/Metadata.kt | 2 +- gradle.properties | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- sample/ios-app/Podfile.lock | 4 ++-- sample/ios-app/Pods/Local Podspecs/mpp_library.podspec.json | 2 +- sample/ios-app/Pods/Manifest.lock | 4 ++-- sample/mpp-library/mpp_library.podspec | 2 +- settings.gradle.kts | 4 ++-- utils-core/build.gradle.kts | 1 + utils-core/src/androidMain/AndroidManifest.xml | 2 +- utils-core/utils_core.podspec | 2 +- 17 files changed, 23 insertions(+), 19 deletions(-) diff --git a/archy-android/build.gradle.kts b/archy-android/build.gradle.kts index 61cc3aa..9748ff9 100644 --- a/archy-android/build.gradle.kts +++ b/archy-android/build.gradle.kts @@ -12,6 +12,7 @@ plugins { } android { + namespace = "com.merseyside.merseyLib.archy.android" compileSdk = Application.compileSdk defaultConfig { diff --git a/archy-android/src/main/AndroidManifest.xml b/archy-android/src/main/AndroidManifest.xml index ddebb62..15e7c2a 100644 --- a/archy-android/src/main/AndroidManifest.xml +++ b/archy-android/src/main/AndroidManifest.xml @@ -1,2 +1 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/archy-core/build.gradle.kts b/archy-core/build.gradle.kts index 758f730..7787742 100644 --- a/archy-core/build.gradle.kts +++ b/archy-core/build.gradle.kts @@ -14,6 +14,7 @@ plugins { } android { + namespace = "com.merseyside.merseyLib.archy.core" compileSdk = Application.compileSdk defaultConfig { diff --git a/archy-core/src/androidMain/AndroidManifest.xml b/archy-core/src/androidMain/AndroidManifest.xml index 7ea17e8..9b65eb0 100644 --- a/archy-core/src/androidMain/AndroidManifest.xml +++ b/archy-core/src/androidMain/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index f73a397..357835a 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,4 +1,6 @@ plugins { + kotlin("jvm") version "1.7.0" + kotlin("plugin.serialization") version "1.7.0" `kotlin-dsl` } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index acde143..7b51682 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -16,7 +16,7 @@ dependencyResolutionManagement { gradlePluginPortal() } - val catalogVersions = "1.4.9" + val catalogVersions = "1.5.0" val group = "io.github.merseyside" versionCatalogs { diff --git a/buildSrc/src/main/kotlin/publication/Metadata.kt b/buildSrc/src/main/kotlin/publication/Metadata.kt index d7063a6..f279c23 100644 --- a/buildSrc/src/main/kotlin/publication/Metadata.kt +++ b/buildSrc/src/main/kotlin/publication/Metadata.kt @@ -1,4 +1,4 @@ object Metadata { const val groupId = "io.github.merseyside" - const val version = "1.4.9" + const val version = "1.5.0" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9f53a03..2d3da42 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,12 +15,12 @@ org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX android.enableJetifier=false -# Kotlin code style for this project: "official" or "obsolete": +android.disableAutomaticComponentCreation=true + kotlin.code.style=official -build.localAndroidDependencies=true +build.localAndroidDependencies=false build.localKotlinExtLibrary=false kotlin.mpp.stability.nowarn=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ea8d929..ce68548 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Feb 04 15:25:14 NOVT 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/sample/ios-app/Podfile.lock b/sample/ios-app/Podfile.lock index a143e4c..6068879 100644 --- a/sample/ios-app/Podfile.lock +++ b/sample/ios-app/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - mpp_library (1.4.9) + - mpp_library (1.5.0) - Reachability (3.2) DEPENDENCIES: @@ -15,7 +15,7 @@ EXTERNAL SOURCES: :path: "../mpp-library" SPEC CHECKSUMS: - mpp_library: 5fd5c55682e489edcb3d98bd6456f9251806df3f + mpp_library: 9fd016ffedab0ef0e9d8776c2cea976110e4691c Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 PODFILE CHECKSUM: 984fdf42b2cd57b5e14197719552245436d87e71 diff --git a/sample/ios-app/Pods/Local Podspecs/mpp_library.podspec.json b/sample/ios-app/Pods/Local Podspecs/mpp_library.podspec.json index eb73c50..27ac3ed 100644 --- a/sample/ios-app/Pods/Local Podspecs/mpp_library.podspec.json +++ b/sample/ios-app/Pods/Local Podspecs/mpp_library.podspec.json @@ -1,6 +1,6 @@ { "name": "mpp_library", - "version": "1.4.9", + "version": "1.5.0", "homepage": "https://github.com/Merseyside/mersey-kmp-library", "source": { "http": "" diff --git a/sample/ios-app/Pods/Manifest.lock b/sample/ios-app/Pods/Manifest.lock index a143e4c..6068879 100644 --- a/sample/ios-app/Pods/Manifest.lock +++ b/sample/ios-app/Pods/Manifest.lock @@ -1,5 +1,5 @@ PODS: - - mpp_library (1.4.9) + - mpp_library (1.5.0) - Reachability (3.2) DEPENDENCIES: @@ -15,7 +15,7 @@ EXTERNAL SOURCES: :path: "../mpp-library" SPEC CHECKSUMS: - mpp_library: 5fd5c55682e489edcb3d98bd6456f9251806df3f + mpp_library: 9fd016ffedab0ef0e9d8776c2cea976110e4691c Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 PODFILE CHECKSUM: 984fdf42b2cd57b5e14197719552245436d87e71 diff --git a/sample/mpp-library/mpp_library.podspec b/sample/mpp-library/mpp_library.podspec index 67a07da..865ca38 100644 --- a/sample/mpp-library/mpp_library.podspec +++ b/sample/mpp-library/mpp_library.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'mpp_library' - spec.version = '1.4.9' + spec.version = '1.5.0' spec.homepage = 'https://github.com/Merseyside/mersey-kmp-library' spec.source = { :http=> ''} spec.authors = '' diff --git a/settings.gradle.kts b/settings.gradle.kts index f5b90f5..086bfa9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,7 @@ enableFeaturePreview("VERSION_CATALOGS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") -private val isLocalAndroidDependencies = true +private val isLocalAndroidDependencies = false private val isLocalKotlinExtLibrary = false dependencyResolutionManagement { @@ -10,7 +10,7 @@ dependencyResolutionManagement { mavenLocal() } - val catalogVersions = "1.4.9" + val catalogVersions = "1.5.0" val group = "io.github.merseyside" versionCatalogs { val multiplatformLibs by creating { diff --git a/utils-core/build.gradle.kts b/utils-core/build.gradle.kts index fc56eda..9a587eb 100644 --- a/utils-core/build.gradle.kts +++ b/utils-core/build.gradle.kts @@ -14,6 +14,7 @@ plugins { } android { + namespace = "com.merseyside.merseyLib.utils.core" compileSdk = Application.compileSdk defaultConfig { diff --git a/utils-core/src/androidMain/AndroidManifest.xml b/utils-core/src/androidMain/AndroidManifest.xml index de940c7..b507194 100644 --- a/utils-core/src/androidMain/AndroidManifest.xml +++ b/utils-core/src/androidMain/AndroidManifest.xml @@ -1,3 +1,3 @@ - + diff --git a/utils-core/utils_core.podspec b/utils-core/utils_core.podspec index 6255301..4024727 100644 --- a/utils-core/utils_core.podspec +++ b/utils-core/utils_core.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'utils_core' - spec.version = '1.4.9' + spec.version = '1.5.0' spec.homepage = 'https://github.com/Merseyside/mersey-kmp-library/tree/master/utils-core' spec.source = { :http=> ''} spec.authors = ''