diff --git a/dachlatten-compose/build.gradle.kts b/dachlatten-compose/build.gradle.kts index 4d7eff8..cad801b 100644 --- a/dachlatten-compose/build.gradle.kts +++ b/dachlatten-compose/build.gradle.kts @@ -1,9 +1,21 @@ plugins { id("android-library-base") id("android-library-unit-test") + id("android-library-robolectric-test") id("android-library-release") } dependencies { implementation(libs.compose.ui) } + +android { + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() + } + + buildFeatures { + compose = true + } +} + diff --git a/dachlatten-compose/src/main/kotlin/de/sipgate/dachlatten/compose/AndroidHandlerFunc.kt b/dachlatten-compose/src/main/kotlin/de/sipgate/dachlatten/compose/AndroidHandlerFunc.kt new file mode 100644 index 0000000..8fd9c48 --- /dev/null +++ b/dachlatten-compose/src/main/kotlin/de/sipgate/dachlatten/compose/AndroidHandlerFunc.kt @@ -0,0 +1,42 @@ +package de.sipgate.dachlatten.compose + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +// This provides a parameterless handlerFunc that can be +// passed around and invoked from anywhere. It will keep a +// reference to the Context given during its creation. +// Warning: +// Depending on the situation this can easily leak the Context! + +typealias AndroidClickHandler = context (ContextProvider) () -> Unit + +@Composable +inline fun withContext(crossinline target: AndroidClickHandler): ClickHandler { + return LocalContext.current.withContext(target) +} + +@Composable +inline fun withContext( + crossinline target: context (ContextProvider) + (T) -> R, +): (T) -> R = LocalContext.current.withContext(target) + +inline fun Context.withContext( + crossinline target: context (ContextProvider) + (T) -> R, +): (T) -> R = { param -> + with(ContextProviderImpl(this)) { + target.invoke(this, param) + } +} + +inline fun Context.withContext( + crossinline target: context (ContextProvider) + () -> R, +): () -> R = { + with(ContextProviderImpl(this)) { + target(this) + } +} diff --git a/dachlatten-compose/src/test/kotlin/AndroidHandlerFuncTest.kt b/dachlatten-compose/src/test/kotlin/AndroidHandlerFuncTest.kt new file mode 100644 index 0000000..d20a53c --- /dev/null +++ b/dachlatten-compose/src/test/kotlin/AndroidHandlerFuncTest.kt @@ -0,0 +1,38 @@ +package de.sipgate.dachlatten.compose + +import org.junit.Test +import org.junit.jupiter.api.Assertions +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class AndroidHandlerFuncTest { + + @Test + fun testAndroidHandlerFuncWillReceiveAContext() { + val context = RuntimeEnvironment.getApplication().applicationContext + val handlerFunc = context.withContext(::someFunctionThatAccessesTheAndroidContext) + + handlerFunc.invoke() + } + + @Test + fun testAndroidHandlerFuncWillReceiveAContextAndReturnValueIsPassedBack() { + val context = RuntimeEnvironment.getApplication().applicationContext + val handlerFunc = context.withContext(::someFunctionThatAccessesTheAndroidContextAndReturnsSomething) + + val result = handlerFunc.invoke() + Assertions.assertTrue(result.isNotEmpty()) + } + + context (ContextProvider) + private fun someFunctionThatAccessesTheAndroidContext() { + context.packageName + } + + context (ContextProvider) + private fun someFunctionThatAccessesTheAndroidContextAndReturnsSomething(): String { + return context.packageName + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b9cb3d..2fe9530 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,8 @@ turbine = "1.0.0" annotation-jvm = "1.7.0" androidx-lifecycle = "2.6.2" robolectric = "4.10.3" -compose = "1.5.3" +compose = "1.5.4" +compose-compiler = "1.5.4" [libraries] coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }