Skip to content
This repository has been archived by the owner on Mar 2, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2 from cheonjaewoong/nullable-notnull-property
Browse files Browse the repository at this point in the history
Add not-null property delegate
  • Loading branch information
cheonjaeung authored Nov 29, 2022
2 parents 151cb21 + 56e0e62 commit d6da8dc
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 140 deletions.
3 changes: 2 additions & 1 deletion savedstate-ktx/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="io.woong.savedstate.test">
<application>
<activity android:name="io.woong.savedstate.PropertyDelegatorTest$TestActivity" />
<activity android:name="io.woong.savedstate.NullablePropertyDelegatorTest$TestActivity" />
<activity android:name="io.woong.savedstate.NotNullPropertyDelegatorTest$TestActivity" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.woong.savedstate

import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.test.core.app.ApplicationProvider
import androidx.test.core.app.launchActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
public class NotNullPropertyDelegatorTest {
private lateinit var context: Context

@Before
public fun init() {
context = ApplicationProvider.getApplicationContext()
}

@Test
public fun notNullWithInitialValue() {
val scenario = launchActivity<TestActivity>()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
assertThat(viewModel.notNullValue).isEqualTo("a")
assertThat(viewModel.notNullVariable).isEqualTo("b")

viewModel.notNullVariable = "c"
assertThat(viewModel.notNullVariable).isEqualTo("c")
}
}

@Test
public fun stateSaving() {
val scenario = launchActivity<TestActivity>()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
viewModel.notNullVariable = "bbb"

assertThat(viewModel.notNullValue).isEqualTo("a")
assertThat(viewModel.notNullVariable).isEqualTo("bbb")
}
scenario.recreate()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
assertThat(viewModel.notNullValue).isEqualTo("a")
assertThat(viewModel.notNullVariable).isEqualTo("bbb")
}
}

public class TestActivity : ComponentActivity() {
public val viewModel: TestViewModel by viewModels()
}

public class TestViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
public val notNullValue: String by savedStateHandle.notNull("a")
public var notNullVariable: String by savedStateHandle.notNull("b")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.woong.savedstate

import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.test.core.app.ApplicationProvider
import androidx.test.core.app.launchActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
public class NullablePropertyDelegatorTest {
private lateinit var context: Context

@Before
public fun init() {
context = ApplicationProvider.getApplicationContext()
}

@Test
public fun simplestDelegate() {
val scenario = launchActivity<TestActivity>()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
assertThat(viewModel.simpleValue).isNull()
assertThat(viewModel.simpleVariable).isNull()

viewModel.simpleVariable = "test"
assertThat(viewModel.simpleVariable).isEqualTo("test")
}
}

@Test
public fun nullableWithInitialValue() {
val scenario = launchActivity<TestActivity>()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
assertThat(viewModel.initializedValue).isEqualTo("a")
assertThat(viewModel.initializedVariable).isEqualTo("b")

viewModel.initializedVariable = "c"
assertThat(viewModel.initializedVariable).isEqualTo("c")
}
}

@Test
public fun stateSaving() {
val scenario = launchActivity<TestActivity>()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
viewModel.simpleVariable = "aaa"
viewModel.initializedVariable = "bbb"

assertThat(viewModel.simpleValue).isNull()
assertThat(viewModel.simpleVariable).isEqualTo("aaa")
assertThat(viewModel.initializedValue).isEqualTo("a")
assertThat(viewModel.initializedVariable).isEqualTo("bbb")
}
scenario.recreate()
scenario.onActivity { activity ->
val viewModel = activity.viewModel
assertThat(viewModel.simpleValue).isNull()
assertThat(viewModel.simpleVariable).isEqualTo("aaa")
assertThat(viewModel.initializedValue).isEqualTo("a")
assertThat(viewModel.initializedVariable).isEqualTo("bbb")
}
}

public class TestActivity : ComponentActivity() {
public val viewModel: TestViewModel by viewModels()
}

public class TestViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
public val simpleValue: String? by savedStateHandle
public var simpleVariable: String? by savedStateHandle

public val initializedValue: String? by savedStateHandle.nullable("a")
public var initializedVariable: String? by savedStateHandle.nullable("b")
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.woong.savedstate

import androidx.lifecycle.SavedStateHandle
import kotlin.reflect.KProperty

/**
* Returns a property delegate for reading and writing a not-null value into [SavedStateHandle]
* with initial value.
*
* Reading the property equals to read value from [SavedStateHandle]
* and writing equals to write value to [SavedStateHandle].
*
* To define not-null property delegate, use `by` keyword of Kotlin:
* The property must not-null type.
*
* ```
* class ExampleViewModel(savedStateHandle: SavedStateHandle) {
* // This code equals to below code:
* // var foo: String
* // get() = savedStateHandle["foo"]!!
* // set(value) { savedStateHandle["foo"] = value }
* //
* // init {
* // if (!savedStateHandle.contains("foo")) {
* // savedStateHandle["foo"] = "init"
* // }
* // }
* var foo: String by savedStateHandle.notNull("init")
* }
* ```
*
* @param initialValue The initial value of this property.
*/
public fun <T> SavedStateHandle.notNull(initialValue: T): NotNullPropertyDelegateProvider<T> {
return NotNullPropertyDelegateProvider(savedStateHandle = this, initialValue)
}

public class NotNullPropertyDelegateProvider<T>(
private val savedStateHandle: SavedStateHandle,
private val initialValue: T
) {
public operator fun provideDelegate(self: Any?, property: KProperty<*>): NotNullPropertyDelegate<T> {
val key = property.name
if (!savedStateHandle.contains(key)) {
savedStateHandle[key] = initialValue
}
return NotNullPropertyDelegate(savedStateHandle, key)
}
}

public class NotNullPropertyDelegate<T>(
private val savedStateHandle: SavedStateHandle,
private val key: String
) {
public operator fun getValue(self: Any?, property: KProperty<*>): T {
return savedStateHandle[key]!!
}

public operator fun setValue(self: Any?, property: KProperty<*>, value: T) {
savedStateHandle[key] = value
}
}
Loading

0 comments on commit d6da8dc

Please sign in to comment.