diff --git a/bottom-navigation-base/build.gradle b/bottom-navigation-base/build.gradle index 40be017..f97eb55 100644 --- a/bottom-navigation-base/build.gradle +++ b/bottom-navigation-base/build.gradle @@ -20,7 +20,8 @@ android { } dependencies { - api project(":navigation-base") + implementation project(":logging") + implementation project(":navigation-base") implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationActivity.kt b/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationActivity.kt index 86a185e..779602f 100644 --- a/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationActivity.kt +++ b/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationActivity.kt @@ -1,4 +1,4 @@ -package ru.touchin.roboswag.bottom_navigation_fragment +package ru.touchin.roboswag.bottom_navigation_base import android.os.Parcelable import androidx.annotation.IdRes @@ -6,13 +6,14 @@ import androidx.fragment.app.FragmentManager import ru.touchin.roboswag.navigation_base.FragmentNavigation import ru.touchin.roboswag.navigation_base.activities.NavigationActivity -abstract class BaseBottomNavigationActivity< - TNavigation : FragmentNavigation, - TNavigationFragment : BaseBottomNavigationFragment<*>, - TNavigationContainer : BaseNavigationContainerFragment<*, TNavigation>> : NavigationActivity() { +abstract class BaseBottomNavigationActivity : NavigationActivity() + where TNavigation : FragmentNavigation, + TNavigationFragment : BaseBottomNavigationFragment<*>, + TNavigationContainer : BaseNavigationContainerFragment<*, TNavigation> +{ val innerNavigation: TNavigation - get() = getNavigationContainer(supportFragmentManager)?.navigation ?: navigation + get() = getNavigationContainer(supportFragmentManager)?.navigation ?: navigation /** * Navigates to the given navigation tab. diff --git a/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationController.kt b/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationController.kt index ec69c76..8f831ef 100644 --- a/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationController.kt +++ b/bottom-navigation-base/src/main/java/ru/touchin/roboswag/bottom_navigation_base/BaseBottomNavigationController.kt @@ -1,4 +1,4 @@ -package ru.touchin.roboswag.bottom_navigation_fragment +package ru.touchin.roboswag.bottom_navigation_base import android.content.Context import android.os.Bundle @@ -13,16 +13,17 @@ import androidx.core.view.children import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import ru.touchin.roboswag.core.utils.ShouldNotHappenException -import ru.touchin.roboswag.navigation_base.fragments.BaseFragment +import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment abstract class BaseBottomNavigationController( private val tabs: SparseArray, private val context: Context, private val fragmentManager: FragmentManager, - @IdRes private val defaultTabId: Int = 0, // If it zero back press with empty fragment back stack would close the app - @IdRes private val contentContainerViewId: Int, @LayoutRes private val contentContainerLayoutId: Int, - private val wrapWithNavigationContainer: Boolean = false + @IdRes private val contentContainerViewId: Int, + @IdRes private val defaultTabId: Int = 0, // If it zero back press with empty fragment back stack would close the app + private val wrapWithNavigationContainer: Boolean = false, + private val onReselectListener: (() -> Unit)? = null ) { private var callback: FragmentManager.FragmentLifecycleCallbacks? = null @@ -111,7 +112,9 @@ abstract class BaseBottomNavigationController> = BaseNavigationContainerFragment::class.java - protected open fun onTabReselected() = Unit + protected open fun onTabReselected() { + onReselectListener?.invoke() + } protected open fun isTabClass(tab: TNavigationTab, fragment: Fragment?) = if (wrapWithNavigationContainer) { (fragment as BaseNavigationContainerFragment<*, *>).getContainedClass() @@ -130,7 +133,7 @@ abstract class BaseBottomNavigationController, @@ -14,31 +13,13 @@ open class BaseNavigationTab( val saveStateOnSwitching: Boolean = true ) { - /** - * It is value as class body property instead of value as constructor parameter to specify - * custom getter of this field which returns copy of Parcelable every time it be called. - * This is necessary to avoid modifying this value if it would be a value as constructor parameter - * and every getting of this value would return the same instance. - */ - val state = state - get() = field.copy() - - private fun Parcelable.copy(): Parcelable = - if (this is EmptyState) { - EmptyState - } else { - val parcel = Parcel.obtain() - - parcel.writeParcelable(this, 0) - parcel.setDataPosition(0) - - val result = parcel.readParcelable( - javaClass.classLoader ?: Thread.currentThread().contextClassLoader - ) ?: throw IllegalStateException("Failed to copy tab state") - - parcel.recycle() - - result - } + /** + * It is value as class body property instead of value as constructor parameter to specify + * custom getter of this field which returns copy of Parcelable every time it be called. + * This is necessary to avoid modifying this value if it would be a value as constructor parameter + * and every getting of this value would return the same instance. + */ + val state = state + get() = field.copy() } diff --git a/bottom-navigation-fragment/build.gradle b/bottom-navigation-fragment/build.gradle index 87c56a9..69410ca 100644 --- a/bottom-navigation-fragment/build.gradle +++ b/bottom-navigation-fragment/build.gradle @@ -20,8 +20,8 @@ android { } dependencies { - api project(":navigation-base") - api project(":bottom-navigation-base") + implementation project(":navigation-base") + implementation project(":bottom-navigation-base") implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationActivity.kt b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationActivity.kt index 214ea8f..1eae1ca 100644 --- a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationActivity.kt +++ b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationActivity.kt @@ -1,5 +1,6 @@ package ru.touchin.roboswag.bottom_navigation_fragment +import ru.touchin.roboswag.bottom_navigation_base.BaseBottomNavigationActivity import ru.touchin.roboswag.navigation_base.FragmentNavigation abstract class BottomNavigationActivity : diff --git a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationController.kt b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationController.kt index eeb51ff..de42784 100644 --- a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationController.kt +++ b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationController.kt @@ -5,21 +5,23 @@ import android.util.SparseArray import androidx.annotation.IdRes import androidx.annotation.LayoutRes import androidx.fragment.app.FragmentManager +import ru.touchin.roboswag.bottom_navigation_base.BaseBottomNavigationController class BottomNavigationController( context: Context, fragments: SparseArray, fragmentManager: FragmentManager, wrapWithNavigationContainer: Boolean = false, - @IdRes private val defaultTabId: Int = 0, // If it zero back press with empty fragment back stack would close the app - @IdRes private val contentContainerViewId: Int, @LayoutRes private val contentContainerLayoutId: Int, - private val onReselectListener: (() -> Unit)? = null + @IdRes private val contentContainerViewId: Int, + @IdRes private val defaultTabId: Int = 0, // If it zero back press with empty fragment back stack would close the app + onReselectListener: (() -> Unit)? = null ) : BaseBottomNavigationController( tabs = fragments, context = context, fragmentManager = fragmentManager, defaultTabId = defaultTabId, + onReselectListener = onReselectListener, contentContainerViewId = contentContainerViewId, contentContainerLayoutId = contentContainerLayoutId, wrapWithNavigationContainer = wrapWithNavigationContainer @@ -27,8 +29,4 @@ class BottomNavigationController( override fun getNavigationContainerClass() = NavigationContainerFragment::class.java - override fun onTabReselected() { - onReselectListener?.invoke() - } - } diff --git a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationFragment.kt b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationFragment.kt index 5445473..9a2c20e 100644 --- a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationFragment.kt +++ b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/BottomNavigationFragment.kt @@ -1,5 +1,7 @@ package ru.touchin.roboswag.bottom_navigation_fragment +import ru.touchin.roboswag.bottom_navigation_base.BaseBottomNavigationFragment + abstract class BottomNavigationFragment : BaseBottomNavigationFragment() { override fun createNavigationController() = BottomNavigationController( diff --git a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationContainerFragment.kt b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationContainerFragment.kt index 7d8356b..200b7ee 100644 --- a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationContainerFragment.kt +++ b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationContainerFragment.kt @@ -1,10 +1,12 @@ package ru.touchin.roboswag.bottom_navigation_fragment import android.os.Parcelable +import ru.touchin.roboswag.bottom_navigation_base.BaseNavigationContainerFragment import ru.touchin.roboswag.navigation_base.FragmentNavigation -import ru.touchin.roboswag.navigation_base.fragments.BaseFragment +import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment -class NavigationContainerFragment : BaseNavigationContainerFragment, FragmentNavigation>() { +class NavigationContainerFragment : + BaseNavigationContainerFragment, FragmentNavigation>() { override val navigation by lazy { FragmentNavigation( diff --git a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationTab.kt b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationTab.kt index 1a78e34..81e4c6e 100644 --- a/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationTab.kt +++ b/bottom-navigation-fragment/src/main/java/ru/touchin/roboswag/bottom_navigation_fragment/NavigationTab.kt @@ -1,10 +1,11 @@ package ru.touchin.roboswag.bottom_navigation_fragment import android.os.Parcelable -import ru.touchin.roboswag.navigation_base.fragments.BaseFragment +import ru.touchin.roboswag.bottom_navigation_base.BaseNavigationTab +import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment class NavigationTab( - override val cls: Class>, + override val cls: Class>, state: Parcelable, saveStateOnSwitching: Boolean = true ) : BaseNavigationTab(cls, state, saveStateOnSwitching) diff --git a/lifecycle-viewcontroller/.gitignore b/lifecycle-viewcontroller/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/lifecycle-viewcontroller/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lifecycle-viewcontroller/build.gradle b/lifecycle-viewcontroller/build.gradle new file mode 100644 index 0000000..7ceb229 --- /dev/null +++ b/lifecycle-viewcontroller/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion versions.compileSdk + + defaultConfig { + minSdkVersion 16 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation project(":lifecycle") + implementation project(":navigation-viewcontroller") + + compileOnly "javax.inject:javax.inject:1" + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + implementation "androidx.appcompat:appcompat:$versions.appcompat" + + implementation "androidx.fragment:fragment:$versions.fragment" + implementation "androidx.fragment:fragment-ktx:$versions.fragment" + + implementation "androidx.lifecycle:lifecycle-extensions:$versions.lifecycle" +} diff --git a/lifecycle-viewcontroller/src/main/AndroidManifest.xml b/lifecycle-viewcontroller/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d3c02f7 --- /dev/null +++ b/lifecycle-viewcontroller/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/extensions/ViewModelLazy.kt b/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/extensions/ViewModelLazy.kt new file mode 100644 index 0000000..f402d47 --- /dev/null +++ b/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/extensions/ViewModelLazy.kt @@ -0,0 +1,37 @@ +package ru.touchin.lifecycle_viewcontroller.extensions + +import androidx.annotation.MainThread +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner +import ru.touchin.lifecycle_viewcontroller.viewmodel.LifecycleViewModelProviders +import ru.touchin.roboswag.navigation_viewcontroller.viewcontrollers.ViewController +import androidx.fragment.app.activityViewModels as androidActivityViewModels +import androidx.fragment.app.viewModels as androidViewModels + +@MainThread +inline fun ViewController<*, *>.viewModels( + noinline ownerProducer: () -> ViewModelStoreOwner = { this.fragment }, + noinline factoryProducer: () -> ViewModelProvider.Factory = { LifecycleViewModelProviders.getViewModelFactory(this) } +) = this.fragment.androidViewModels(ownerProducer, factoryProducer) + +@MainThread +inline fun ViewController<*, *>.parentViewModels( + noinline ownerProducer: () -> ViewModelStoreOwner = { this.fragment.parentFragment!! }, + noinline factoryProducer: () -> ViewModelProvider.Factory = { + LifecycleViewModelProviders.getViewModelFactory(this.fragment.parentFragment!!) + } +) = viewModels(ownerProducer, factoryProducer) + +@MainThread +inline fun ViewController<*, *>.targetViewModels( + noinline ownerProducer: () -> ViewModelStoreOwner = { this.fragment.targetFragment!! }, + noinline factoryProducer: () -> ViewModelProvider.Factory = { + LifecycleViewModelProviders.getViewModelFactory(this.fragment.targetFragment!!) + } +) = viewModels(ownerProducer, factoryProducer) + +@MainThread +inline fun ViewController<*, *>.activityViewModels( + noinline factoryProducer: () -> ViewModelProvider.Factory = { LifecycleViewModelProviders.getViewModelFactory(activity) } +) = this.fragment.androidActivityViewModels(factoryProducer) diff --git a/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/viewmodel/LifecycleViewModelProviders.kt b/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/viewmodel/LifecycleViewModelProviders.kt new file mode 100644 index 0000000..7d93a36 --- /dev/null +++ b/lifecycle-viewcontroller/src/main/java/ru/touchin/lifecycle_viewcontroller/viewmodel/LifecycleViewModelProviders.kt @@ -0,0 +1,30 @@ +package ru.touchin.lifecycle_viewcontroller.viewmodel + +import android.app.Activity +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import ru.touchin.lifecycle.viewmodel.BaseLifecycleViewModelProviders +import ru.touchin.lifecycle.viewmodel.ViewModelFactoryProvider +import ru.touchin.roboswag.navigation_viewcontroller.viewcontrollers.ViewController + +object LifecycleViewModelProviders : BaseLifecycleViewModelProviders() { + + /** + * Returns ViewModelProvider.Factory instance from current lifecycleOwner. + * Search #ViewModelFactoryProvider are produced according to priorities: + * 1. View controller; + * 2. Fragment; + * 3. Parent fragment recursively; + * 4. Activity; + * 5. Application. + */ + override fun getViewModelFactory(provider: Any): ViewModelProvider.Factory = + when (provider) { + is ViewModelFactoryProvider -> provider.viewModelFactory + is ViewController<*, *> -> getViewModelFactory(provider.fragment) + is Fragment -> getViewModelFactory(provider.parentFragment ?: provider.requireActivity()) + is Activity -> getViewModelFactory(provider.application) + else -> throw IllegalArgumentException("View model factory not found.") + } + +} diff --git a/lifecycle/build.gradle b/lifecycle/build.gradle index 2ad19c2..6ca35ff 100644 --- a/lifecycle/build.gradle +++ b/lifecycle/build.gradle @@ -19,8 +19,6 @@ android { } dependencies { - api project(":navigation-viewcontroller") - compileOnly "javax.inject:javax.inject:1" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/BaseLifecycleViewModelProviders.kt b/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/BaseLifecycleViewModelProviders.kt new file mode 100644 index 0000000..f03c55b --- /dev/null +++ b/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/BaseLifecycleViewModelProviders.kt @@ -0,0 +1,47 @@ +package ru.touchin.lifecycle.viewmodel + +import android.app.Activity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModelProvider + +abstract class BaseLifecycleViewModelProviders { + + /** + * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given + * {@code lifecycleOwner} is alive. More detailed explanation is in {@link ViewModel}. + *

+ * It uses the given {@link Factory} to instantiate new ViewModels. + * + * @param lifecycleOwner a lifecycle owner, in whose scope ViewModels should be retained (ViewController, Fragment, Activity) + * @param factory a {@code Factory} to instantiate new ViewModels + * @return a ViewModelProvider instance + */ + open fun of( + lifecycleOwner: LifecycleOwner, + factory: ViewModelProvider.Factory = LifecycleViewModelProviders.getViewModelFactory(lifecycleOwner) + ): ViewModelProvider = + when (lifecycleOwner) { + is Fragment -> ViewModelProvider(lifecycleOwner, factory) + is FragmentActivity -> ViewModelProvider(lifecycleOwner, factory) + else -> throw IllegalArgumentException("Not supported LifecycleOwner.") + } + + /** + * Returns ViewModelProvider.Factory instance from current lifecycleOwner. + * Search #ViewModelFactoryProvider are produced according to priorities: + * 1. Fragment; + * 2. Parent fragment recursively; + * 3. Activity; + * 4. Application. + */ + open fun getViewModelFactory(provider: Any): ViewModelProvider.Factory = + when (provider) { + is ViewModelFactoryProvider -> provider.viewModelFactory + is Fragment -> LifecycleViewModelProviders.getViewModelFactory(provider.parentFragment ?: provider.requireActivity()) + is Activity -> LifecycleViewModelProviders.getViewModelFactory(provider.application) + else -> throw IllegalArgumentException("View model factory not found.") + } + +} diff --git a/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/LifecycleViewModelProviders.kt b/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/LifecycleViewModelProviders.kt index 8bdb97b..438cdc4 100644 --- a/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/LifecycleViewModelProviders.kt +++ b/lifecycle/src/main/java/ru/touchin/lifecycle/viewmodel/LifecycleViewModelProviders.kt @@ -1,49 +1,3 @@ package ru.touchin.lifecycle.viewmodel -import android.app.Activity -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelProviders -import ru.touchin.roboswag.navigation_viewcontroller.viewcontrollers.ViewController - -object LifecycleViewModelProviders { - - /** - * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given - * {@code lifecycleOwner} is alive. More detailed explanation is in {@link ViewModel}. - *

- * It uses the given {@link Factory} to instantiate new ViewModels. - * - * @param lifecycleOwner a lifecycle owner, in whose scope ViewModels should be retained (ViewController, Fragment, Activity) - * @param factory a {@code Factory} to instantiate new ViewModels - * @return a ViewModelProvider instance - */ - fun of(lifecycleOwner: LifecycleOwner, factory: ViewModelProvider.Factory = getViewModelFactory(lifecycleOwner)): ViewModelProvider = - when (lifecycleOwner) { - is ViewController<*, *> -> ViewModelProviders.of(lifecycleOwner.fragment, factory) - is Fragment -> ViewModelProviders.of(lifecycleOwner, factory) - is FragmentActivity -> ViewModelProviders.of(lifecycleOwner, factory) - else -> throw IllegalArgumentException("Not supported LifecycleOwner.") - } - - /** - * Returns ViewModelProvider.Factory instance from current lifecycleOwner. - * Search #ViewModelFactoryProvider are produced according to priorities: - * 1. View controller; - * 2. Fragment; - * 3. Parent fragment recursively; - * 4. Activity; - * 5. Application. - */ - fun getViewModelFactory(provider: Any): ViewModelProvider.Factory = - when (provider) { - is ViewModelFactoryProvider -> provider.viewModelFactory - is ViewController<*, *> -> getViewModelFactory(provider.fragment) - is Fragment -> getViewModelFactory(provider.parentFragment ?: provider.requireActivity()) - is Activity -> getViewModelFactory(provider.application) - else -> throw IllegalArgumentException("View model factory not found.") - } - -} +object LifecycleViewModelProviders : BaseLifecycleViewModelProviders() diff --git a/navigation-base/build.gradle b/navigation-base/build.gradle index 2a000a0..ed356be 100644 --- a/navigation-base/build.gradle +++ b/navigation-base/build.gradle @@ -21,12 +21,12 @@ android { } dependencies { - api project(":utils") - api project(":logging") + implementation project(":utils") + implementation project(":logging") - api 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.multidex:multidex:2.0.1' - api 'net.danlew:android.joda:2.10.2' + implementation 'net.danlew:android.joda:2.10.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/FragmentNavigation.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/FragmentNavigation.kt index 0cb1043..5a1df6c 100644 --- a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/FragmentNavigation.kt +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/FragmentNavigation.kt @@ -28,7 +28,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import ru.touchin.roboswag.core.log.Lc -import ru.touchin.roboswag.navigation_base.fragments.FragmentWithState +import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment import kotlin.reflect.KClass /** @@ -169,14 +169,14 @@ open class FragmentNavigation( * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. */ fun push( - fragmentClass: KClass>, + fragmentClass: KClass>, state: T, addToStack: Boolean = true, backStackName: String? = null, tag: String? = null, transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { - push(fragmentClass.java, FragmentWithState.args(state), addToStack, backStackName, tag, transactionSetup) + push(fragmentClass.java, StatefulFragment.args(state), addToStack, backStackName, tag, transactionSetup) } /** @@ -216,14 +216,14 @@ open class FragmentNavigation( * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. */ fun pushForResult( - fragmentClass: KClass>, + fragmentClass: KClass>, targetFragment: Fragment, targetRequestCode: Int, state: T, tag: String? = null, transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { - pushForResult(fragmentClass.java, targetFragment, targetRequestCode, FragmentWithState.args(state), tag, transactionSetup) + pushForResult(fragmentClass.java, targetFragment, targetRequestCode, StatefulFragment.args(state), tag, transactionSetup) } /** @@ -270,13 +270,13 @@ open class FragmentNavigation( * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. */ fun setInitial( - fragmentClass: KClass>, + fragmentClass: KClass>, state: T, tag: String? = null, transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { beforeSetInitialActions() - setAsTop(fragmentClass.java, FragmentWithState.args(state), false, tag, transactionSetup) + setAsTop(fragmentClass.java, StatefulFragment.args(state), false, tag, transactionSetup) } /** diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/extensions/Parcelable.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/extensions/Parcelable.kt new file mode 100644 index 0000000..5c4f26c --- /dev/null +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/extensions/Parcelable.kt @@ -0,0 +1,47 @@ +package ru.touchin.roboswag.navigation_base.extensions + +import android.os.Parcel +import android.os.Parcelable +import ru.touchin.roboswag.navigation_base.fragments.EmptyState + +// This method used to check unique state of each fragment. +// If two fragments share same class for state, you should not pass state instance of current fragment to the one you transition to +fun Parcelable.reserialize(): T { + var parcel = Parcel.obtain() + + parcel.writeParcelable(this, 0) + + val serializableBytes = parcel.marshall() + + parcel.recycle() + + parcel = Parcel.obtain().apply { + unmarshall(serializableBytes, 0, serializableBytes.size) + setDataPosition(0) + } + + val result = parcel.readParcelable(Thread.currentThread().contextClassLoader) + ?: throw IllegalStateException("It must not be null") + + parcel.recycle() + + return result +} + +fun Parcelable.copy(): Parcelable = + if (this is EmptyState) { + EmptyState + } else { + val parcel = Parcel.obtain() + + parcel.writeParcelable(this, 0) + parcel.setDataPosition(0) + + val result = parcel.readParcelable( + javaClass.classLoader ?: Thread.currentThread().contextClassLoader + ) ?: throw IllegalStateException("Failed to copy tab state") + + parcel.recycle() + + result + } diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/FragmentWithState.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/StatefulFragment.kt similarity index 55% rename from navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/FragmentWithState.kt rename to navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/StatefulFragment.kt index dedf118..4e46f3b 100644 --- a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/FragmentWithState.kt +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/fragments/StatefulFragment.kt @@ -1,13 +1,13 @@ package ru.touchin.roboswag.navigation_base.fragments import android.os.Bundle -import android.os.Parcel import android.os.Parcelable import androidx.annotation.LayoutRes import androidx.fragment.app.FragmentActivity import ru.touchin.roboswag.navigation_base.BuildConfig +import ru.touchin.roboswag.navigation_base.extensions.reserialize -open class FragmentWithState( +open class StatefulFragment( @LayoutRes layoutRes: Int ) : BaseFragment(layoutRes) { @@ -16,20 +16,6 @@ open class FragmentWithState( fun args(state: Parcelable?): Bundle = Bundle().also { it.putParcelable(BASE_FRAGMENT_STATE_EXTRA, state) } - // This method used to check unique state of each fragment. - // If two fragments share same class for state, you should not pass state instance of current fragment to the one you transition to - private fun reserialize(parcelable: T): T { - var parcel = Parcel.obtain() - parcel.writeParcelable(parcelable, 0) - val serializableBytes = parcel.marshall() - parcel.recycle() - parcel = Parcel.obtain() - parcel.unmarshall(serializableBytes, 0, serializableBytes.size) - parcel.setDataPosition(0) - val result = parcel.readParcelable(Thread.currentThread().contextClassLoader) ?: throw IllegalStateException("It must not be null") - parcel.recycle() - return result - } } protected lateinit var state: TState @@ -43,7 +29,7 @@ open class FragmentWithState( ?: throw IllegalStateException("Fragment state can't be null") if (BuildConfig.DEBUG) { - state = reserialize(state) + state = state.reserialize() } } diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt index 13cec6d..ed13fbf 100644 --- a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt @@ -9,12 +9,11 @@ import androidx.lifecycle.LifecycleObserver import ru.touchin.roboswag.components.utils.UiUtils import ru.touchin.roboswag.navigation_base.activities.BaseActivity import ru.touchin.roboswag.navigation_base.activities.OnBackPressedListener -import ru.touchin.roboswag.navigation_base.fragments.BaseFragment -import ru.touchin.roboswag.navigation_base.fragments.FragmentWithState +import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment abstract class KeyboardResizeableFragment( @LayoutRes layoutRes: Int -) : FragmentWithState( +) : StatefulFragment( layoutRes ) { diff --git a/navigation-viewcontroller/build.gradle b/navigation-viewcontroller/build.gradle index 59cbb01..e40ef52 100644 --- a/navigation-viewcontroller/build.gradle +++ b/navigation-viewcontroller/build.gradle @@ -21,13 +21,13 @@ android { } dependencies { - api project(":utils") - api project(":logging") - api project(":navigation-base") + implementation project(":utils") + implementation project(":logging") + implementation project(":navigation-base") - api 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.multidex:multidex:2.0.1' - api 'net.danlew:android.joda:2.10.2' + implementation 'net.danlew:android.joda:2.10.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"