Skip to content

Commit

Permalink
v1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamKwokX committed Sep 9, 2023
1 parent b3e1abf commit ea0421f
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 108 deletions.
2 changes: 1 addition & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ tasks.withType(KotlinCompile).configureEach{
// publish
mavenPublishing {
def artifactId = "mvb-android"
def version = "1.0.0"
def version = "1.0.1"
def isSnapshot = false

if (isSnapshot) version += "-SNAPSHOT"
Expand Down
40 changes: 13 additions & 27 deletions api/src/main/java/pers/shawxingkwok/mvb/android/MVBData.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package pers.shawxingkwok.mvb.android

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.savedstate.SavedStateRegistryOwner
import java.lang.NullPointerException
import pers.shawxingkwok.ktutil.fastLazy
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KMutableProperty
import kotlin.reflect.KProperty

@Suppress("UNCHECKED_CAST")
public open class MVBData<LVS, T> internal constructor(
private val thisRef: LVS,
public open class MVBData<LV, T> internal constructor(
private val thisRef: LV,
private val initialize: (() -> T)? = null
)
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
where LV: LifecycleOwner, LV: ViewModelStoreOwner
{
internal val actionsOnDelegate = mutableListOf<(LVS, KProperty<*>, String, () -> T) -> Unit>()
internal val actionsOnDelegate = mutableListOf<(LV, KProperty<*>, String, () -> T) -> Unit>()

internal lateinit var key: String
private set
Expand All @@ -26,38 +24,26 @@ public open class MVBData<LVS, T> internal constructor(
@Volatile
private var isInitialized: Boolean = false

public operator fun provideDelegate(thisRef: LVS, property: KProperty<*>) : ReadWriteProperty<LVS, T>{
internal val vm by fastLazy(thisRef::getMVBVm)

public operator fun provideDelegate(thisRef: LV, property: KProperty<*>) : ReadWriteProperty<LV, T>{
val isMutable = property is KMutableProperty<*>

val propPath = thisRef.javaClass.canonicalName!! + "." + property.name
val propPath = thisRef::class.qualifiedName + "." + property.name
key = propPath

require(isMutable || initialize != null){
"$propPath can't be immutable with a null `initialize`."
}

var t: T? = null

key = MVBData::class.qualifiedName + "#" + propPath

val vm by lazy(thisRef.savedStateRegistry) {
try {
ViewModelProvider(thisRef)[MVBViewModel::class.java]
} catch (e: IllegalStateException) {
error("Mvb values are kept in a viewModel which is not accessible at the moment.\n$e")
}
}

return object : ReadWriteProperty<LVS, T>{
override fun getValue(thisRef: LVS, property: KProperty<*>): T {
return object : ReadWriteProperty<LV, T>{
override fun getValue(thisRef: LV, property: KProperty<*>): T {
if (!isInitialized)
synchronized(this){
if (isInitialized) return@synchronized

check(thisRef.savedStateRegistry.isRestored){
"All mvb properties must be called after `super.onCreate(savedInstanceState)` " +
"in ${thisRef.javaClass.canonicalName}."
}

val saver = saver
try {
when{
Expand Down Expand Up @@ -90,7 +76,7 @@ public open class MVBData<LVS, T> internal constructor(
return t as T
}

override fun setValue(thisRef: LVS, property: KProperty<*>, value: T) {
override fun setValue(thisRef: LV, property: KProperty<*>, value: T) {
isInitialized = true

t = value
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package pers.shawxingkwok.mvb.android

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import java.util.concurrent.ConcurrentHashMap

internal class MVBViewModel : ViewModel() {
internal class MVBViewModel(val state: SavedStateHandle) : ViewModel() {
private object NULL

private val data = ConcurrentHashMap<String, Any>()
Expand Down
10 changes: 3 additions & 7 deletions api/src/main/java/pers/shawxingkwok/mvb/android/mvbScope.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package pers.shawxingkwok.mvb.android

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewModelScope
import androidx.savedstate.SavedStateRegistryOwner
import kotlinx.coroutines.CoroutineScope

/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#mvbscope).
*/
public val <LVS> LVS.mvbScope: CoroutineScope
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
public val <LV> LV.mvbScope: CoroutineScope
where LV: LifecycleOwner, LV: ViewModelStoreOwner
get() =
synchronized(savedStateRegistry){
ViewModelProvider(this)[MVBViewModel::class.java].viewModelScope
}
getMVBVm().viewModelScope
9 changes: 4 additions & 5 deletions api/src/main/java/pers/shawxingkwok/mvb/android/observe.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package pers.shawxingkwok.mvb.android

import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistryOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
Expand All @@ -12,11 +11,11 @@ import kotlinx.coroutines.launch
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#observe).
*/
public fun <LVS, T, F: Flow<T>, M: MVBData<LVS, F>> M.observe(
public fun <LV, T, F: Flow<T>, M: MVBData<LV, F>> M.observe(
repeatOnResumed: Boolean = false,
act: suspend CoroutineScope.(T) -> Unit,
): M
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
also { m ->
val state = if (repeatOnResumed) Lifecycle.State.RESUMED else Lifecycle.State.STARTED
Expand All @@ -33,8 +32,8 @@ public fun <LVS, T, F: Flow<T>, M: MVBData<LVS, F>> M.observe(
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#observe).
*/
public fun <LVS, T, L: LiveData<T>, M: MVBData<LVS, L>> M.observe(act: (T) -> Unit): M
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
public fun <LV, T, L: LiveData<T>, M: MVBData<LV, L>> M.observe(act: (T) -> Unit): M
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
also { m ->
m.actionsOnDelegate += { lifecycleOwner, _, _, getValue ->
Expand Down
5 changes: 2 additions & 3 deletions api/src/main/java/pers/shawxingkwok/mvb/android/rmb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ package pers.shawxingkwok.mvb.android

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelStoreOwner
import androidx.savedstate.SavedStateRegistryOwner

/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#rmb).
*/
public fun <LVS, T> LVS.rmb(initialize: (() -> T)? = null): MVBData<LVS, T>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
public fun <LV, T> LV.rmb(initialize: (() -> T)? = null): MVBData<LV, T>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
MVBData(this, initialize)
95 changes: 44 additions & 51 deletions api/src/main/java/pers/shawxingkwok/mvb/android/save.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,47 @@
package pers.shawxingkwok.mvb.android

import android.os.*
import androidx.core.os.bundleOf
import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistryOwner
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import java.io.Serializable
import java.util.*
import kotlin.reflect.KClass

@Suppress("NAME_SHADOWING")
public class SavableMVBData<LVS, T, C> @PublishedApi internal constructor(
public class SavableMVBData<LV, T, C> @PublishedApi internal constructor(
public var parcelableComponent: KClass<out Parcelable>?,
@PublishedApi internal var savedType: KClass<C & Any>,
thisRef: LVS,
thisRef: LV,
initialize: (() -> T)?,
)
: MVBData<LVS, T>(thisRef, initialize)
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: MVBData<LV, T>(thisRef, initialize)
where LV: LifecycleOwner, LV: ViewModelStoreOwner
{
@PublishedApi internal var convert: ((Any?) -> Any?)? = null
@PublishedApi internal var recover: ((Any?) -> Any?)? = null

@Suppress("UNCHECKED_CAST")
override val saver by lazy(Saver.CREATOR){
val bundle = thisRef.savedStateRegistry.consumeRestoredStateForKey(key)

if (bundle != null) {
Saver.parcelableLoader = (parcelableComponent ?: savedType.parcelableComponent)?.java?.classLoader
Saver.arrayClass = savedType.java.takeIf { it.isArray } as Class<Array<*>>?
Saver.recover = recover

// update [convert] to remove any possible references to old [thisRef].
val saver =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
bundle.getParcelable("", Saver::class.java)!!
else
bundle.getParcelable("")!!

Saver.recover = null
saver.convert = convert
saver
}else
Saver(UNINITIALIZED, convert)
}

init {
actionsOnDelegate += { thisRef, _, key, _ ->
thisRef.savedStateRegistry.registerSavedStateProvider(key){
Bundle().also { it.putParcelable("", saver) }
val state = vm.state
when(val bundle = state.get<Bundle>(key)){
null -> Saver(UNINITIALIZED, convert).also { state[key] = bundleOf("" to it) }
else -> {
Saver.parcelableLoader = (parcelableComponent ?: savedType.parcelableComponent)?.java?.classLoader
Saver.arrayClass = savedType.java.takeIf { it.isArray } as Class<Array<*>>?
Saver.recover = recover

// update [convert] to remove any possible references to old [thisRef].
val saver =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
bundle.getParcelable("", Saver::class.java)!!
else
bundle.getParcelable("")!!

Saver.recover = null
saver.convert = convert
saver
}
}
}
Expand All @@ -74,12 +67,12 @@ internal val KClass<*>.parcelableComponent: KClass<out Parcelable>? get(){
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#save).
*/
public inline fun <LVS, reified T> LVS.save(
public inline fun <LV, reified T> LV.save(
parcelableComponent: KClass<out Parcelable>? = null,
noinline initialize: (() -> T)? = null,
)
: SavableMVBData<LVS, T, T>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: SavableMVBData<LV, T, T>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
SavableMVBData(
parcelableComponent = parcelableComponent.also { require(it?.isParcelableType ?: true) },
Expand All @@ -91,14 +84,14 @@ public inline fun <LVS, reified T> LVS.save(
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#transform).
*/
public inline fun <LVS, T, C, reified D> SavableMVBData<LVS, T, C>.transform(
public inline fun <LV, T, C, reified D> SavableMVBData<LV, T, C>.transform(
noinline convert: (C) -> D,
noinline recover: (D) -> C,
)
: SavableMVBData<LVS, T, D>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: SavableMVBData<LV, T, D>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
(this as SavableMVBData<LVS, T, D>).also {
(this as SavableMVBData<LV, T, D>).also {
it.savedType = D::class as KClass<D & Any>

it.convert = when (val oldConvert = it.convert) {
Expand All @@ -115,14 +108,14 @@ public inline fun <LVS, T, C, reified D> SavableMVBData<LVS, T, C>.transform(
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#save).
*/
public inline fun <LVS, reified T> LVS.saveMutableStateFlow(
public inline fun <LV, reified T> LV.saveMutableStateFlow(
parcelableComponent: KClass<out Parcelable>? = null,
noinline initialize: () -> T,
)
: SavableMVBData<LVS, MutableStateFlow<T>, T>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: SavableMVBData<LV, MutableStateFlow<T>, T>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
save<LVS, MutableStateFlow<T>>(
save<LV, MutableStateFlow<T>>(
parcelableComponent = parcelableComponent,
initialize = { MutableStateFlow(initialize()) }
)
Expand All @@ -134,20 +127,20 @@ public inline fun <LVS, reified T> LVS.saveMutableStateFlow(
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#save).
*/
public inline fun <LVS, reified T> LVS.saveMutableSharedFlow(
public inline fun <LV, reified T> LV.saveMutableSharedFlow(
parcelableComponent: KClass<out Parcelable>? = null,
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
)
: SavableMVBData<LVS, MutableSharedFlow<T>, List<T>>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: SavableMVBData<LV, MutableSharedFlow<T>, List<T>>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
save<LVS, MutableSharedFlow<T>>(
save<LV, MutableSharedFlow<T>>(
parcelableComponent = parcelableComponent,
initialize = { MutableSharedFlow(replay, extraBufferCapacity, onBufferOverflow) }
)
.transform<LVS, MutableSharedFlow<T>, MutableSharedFlow<T>, List<T>>(
.transform<LV, MutableSharedFlow<T>, MutableSharedFlow<T>, List<T>>(
convert = { it.replayCache },
recover = { cache ->
val flow = MutableSharedFlow<T>(replay, extraBufferCapacity, onBufferOverflow)
Expand All @@ -171,14 +164,14 @@ public inline fun <LVS, reified T> LVS.saveMutableSharedFlow(
/**
* See [doc](https://shawxingkwok.github.io/ITWorks/docs/multiplatform/mvb/android/#save).
*/
public inline fun <LVS, reified T> LVS.saveMutableLiveData(
public inline fun <LV, reified T> LV.saveMutableLiveData(
parcelableComponent: KClass<out Parcelable>? = null,
noinline initialize: (() -> T)? = null,
)
: SavableMVBData<LVS, MutableLiveData<T>, Any?>
where LVS: LifecycleOwner, LVS: ViewModelStoreOwner, LVS: SavedStateRegistryOwner
: SavableMVBData<LV, MutableLiveData<T>, Any?>
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
save<LVS, MutableLiveData<T>>(
save<LV, MutableLiveData<T>>(
parcelableComponent = parcelableComponent,
initialize = {
if (initialize == null)
Expand Down
16 changes: 16 additions & 0 deletions api/src/main/java/pers/shawxingkwok/mvb/android/vm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package pers.shawxingkwok.mvb.android

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner

internal fun <LV> LV.getMVBVm(): MVBViewModel
where LV: LifecycleOwner, LV: ViewModelStoreOwner
=
synchronized(lifecycle) {
try {
ViewModelProvider(this)[MVBViewModel::class.java]
} catch (e: IllegalStateException) {
error("Mvb values are kept in a viewModel which is not accessible at the moment.\n$e")
}
}
11 changes: 6 additions & 5 deletions api/src/test/java/pers/shawxingkwok/mvb/ExampleUnitTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package pers.shawxingkwok.mvb

import org.junit.Test

import org.junit.Assert.*
import pers.shawxingkwok.ktutil.KReadOnlyProperty
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

internal class ExampleUnitTest{
@Test
fun start(){
println(null + "i")
}
}
Loading

0 comments on commit ea0421f

Please sign in to comment.