From 21d8afca5da185e2b8cbcff6f91729e968181f57 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 10:50:23 -0400 Subject: [PATCH 01/11] use a SharedFlow for status updates Maintain backwards compatibility by introducing a new property --- .../dev/jordond/connectivity/Connectivity.kt | 22 +++++++---- .../internal/DefaultConnectivity.kt | 37 ++++++++++++++----- .../connectivity/internal/HttpConnectivity.kt | 32 ++++++++++++++-- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt index e7bdadd..493becc 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt @@ -7,28 +7,34 @@ import dev.jordond.connectivity.internal.DefaultConnectivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map /** * The Connectivity interface provides a way to monitor the network connectivity status. * - * @property updates A [StateFlow] representing the current connectivity status. + * @property statusUpdates A [SharedFlow] representing the current connectivity status. + * @property isActive A [StateFlow] representing whether the connectivity monitoring is active. + * @property updates A [StateFlow] representing the current connectivity status and whether the monitoring is active. */ public interface Connectivity { + public val statusUpdates: SharedFlow + + public val isActive: StateFlow + public val updates: StateFlow /** * A [Flow] representing status updates when the connectivity monitoring is active. - * - * This is a convenience method for filtering the [updates] [StateFlow] for active updates. Since - * the default value is [Status.Disconnected], and the real value won't be available until the - * monitoring starts, this flow will only emit updates when the monitoring is active. */ + @Deprecated( + message = "Use statusUpdates instead. Will be removed in a future release.", + replaceWith = ReplaceWith("statusUpdates"), + level = DeprecationLevel.WARNING, + ) public val activeUpdates: Flow - get() = updates.filter { it.isActive }.map { it.status } + get() = statusUpdates /** * Gets the current connectivity status. diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt index aa3c77a..25308ee 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt @@ -1,14 +1,22 @@ package dev.jordond.connectivity.internal import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.Connectivity.Update import dev.jordond.connectivity.ConnectivityOptions import dev.jordond.connectivity.ConnectivityProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -20,8 +28,22 @@ internal class DefaultConnectivity( private var job: Job? = null - private val _updates = MutableStateFlow(Connectivity.Update.default) - override val updates: StateFlow = _updates.asStateFlow() + private val _statusUpdates = MutableSharedFlow( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) + override val statusUpdates: SharedFlow = _statusUpdates.asSharedFlow() + + private val _isActive = MutableStateFlow(false) + override val isActive: StateFlow = _isActive.asStateFlow() + + override val updates: StateFlow = combine(statusUpdates, isActive) { status, isActive -> + Update(isActive, status) + }.stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = Update(isActive = false, Connectivity.Status.Disconnected) + ) init { if (options.autoStart) { @@ -36,12 +58,9 @@ internal class DefaultConnectivity( override fun start() { job?.cancel() job = launch { - _updates.update { update -> - Connectivity.Update(isActive = true, status = update.status) - } - + _isActive.update { true } provider.monitor().collect { status -> - _updates.update { Connectivity.Update(isActive = true, status) } + _statusUpdates.emit(status) } } } @@ -49,8 +68,6 @@ internal class DefaultConnectivity( override fun stop() { job?.cancel() job = null - _updates.update { update -> - Connectivity.Update(isActive = false, status = update.status) - } + _isActive.update { false } } } \ No newline at end of file diff --git a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt index 53a0899..786c0b4 100644 --- a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt +++ b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt @@ -1,6 +1,7 @@ package dev.jordond.connectivity.internal import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.Connectivity.Update import dev.jordond.connectivity.HttpConnectivityOptions import dev.jordond.connectivity.PollResult import io.ktor.client.HttpClient @@ -11,10 +12,17 @@ import io.ktor.http.isSuccess import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -27,8 +35,22 @@ internal class HttpConnectivity( private var job: Job? = null - private val _updates = MutableStateFlow(Connectivity.Update.default) - override val updates: StateFlow = _updates.asStateFlow() + private val _statusUpdates = MutableSharedFlow( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) + override val statusUpdates: SharedFlow = _statusUpdates.asSharedFlow() + + private val _isActive = MutableStateFlow(value = false) + override val isActive: StateFlow = _isActive.asStateFlow() + + override val updates: StateFlow = combine(statusUpdates, isActive) { status, isActive -> + Update(isActive, status) + }.stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = Update(isActive = false, Connectivity.Status.Disconnected) + ) init { if (httpOptions.options.autoStart) { @@ -43,17 +65,19 @@ internal class HttpConnectivity( override fun start() { if (job != null) return poll() + _isActive.update { true } } override fun stop() { job?.cancel() job = null + _isActive.update { false } } internal fun forcePoll() { launch { checkConnection().also { status -> - _updates.update { Connectivity.Update(it.isActive, status) } + _statusUpdates.emit(status) } } } @@ -62,7 +86,7 @@ internal class HttpConnectivity( job = launch { while (isActive) { val status = checkConnection() - _updates.update { Connectivity.Update(isActive = true, status = status) } + _statusUpdates.emit(status) delay(httpOptions.pollingIntervalMs) } } From c92770fb03a5fb43f2d1cb2caf0ecbd150f477ab Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 10:59:52 -0400 Subject: [PATCH 02/11] Update README.MD --- README.MD | 66 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/README.MD b/README.MD index 2b1c24d..ad0a1eb 100644 --- a/README.MD +++ b/README.MD @@ -11,7 +11,7 @@ network connectivity using native APIs on Android and Apple devices, or by makin specified hosts. You can also -view the generated KDoc at [docs.connectivity.jordond.dev](https://connectivity.jordond.dev) +view the generated KDoc at [connectivity.jordond.dev](https://connectivity.jordond.dev) ## Table of Contents @@ -22,6 +22,7 @@ view the generated KDoc at [docs.connectivity.jordond.dev](https://connectivity. - [Multiplatform - Device](#multiplatform---device) - [All supported platforms](#all-supported-platforms) - [Usage](#usage) + - [Options](#options) - [HTTP monitoring](#http-monitoring) - [Demo](#demo) - [Contributing](#contributing) @@ -135,25 +136,16 @@ you can observe the network connectivity. val connectivity = Connectivity() connectivity.start() coroutineScope.launch { - connectivity.updates.collect { update -> - println("Is active: ${update.isActive}") - - when (update.status) { - is NetworkStatus.Connected -> println("Connected to network") - is NetworkStatus.Disconnected -> println("Disconnected from network") - } + connectivity.statusUpdates.collect { status -> + when (status) { + is NetworkStatus.Connected -> println("Connected to network") + is NetworkStatus.Disconnected -> println("Disconnected from network") } + } } ``` -### Observing network connectivity - -Because the backing field for `Connectivity.updates` is a `StateFlow`, it requires a -initial value to be emitted. This means that the first value emitted will be the *real* current -network status. This is why the `isMonitoring` property is available on the `Connectivity.Update`. - -To get around this, you can use the synchronous `status()` function to get the current network -status: +You can also get the current connectivity status by invoking the suspended `status()` function: ```kotlin val connectivity = Connectivity() @@ -166,22 +158,34 @@ coroutineScope.launch { } ``` -Or you can use the `Connectivity.activeUpdates` property, which is a `Flow - // Handle status + connectivity.updates.collect { update -> + println("Monitoring is active: ${update.isActive}") + + when (update.status) { + is NetworkStatus.Connected -> println("Connected to network") + is NetworkStatus.Disconnected -> println("Disconnected from network") } + } } ``` +**Note:** Because the backing field for `Connectivity.updates` is a `StateFlow`, it requires a +initial value to be emitted. This means that the first value emitted will be the *real* current +network status. This is why the `isActive` property is available on the `Connectivity.Update`. + By default when you construct a `Connectivity` object, it will not automatically start monitoring network connectivity. You can enable this by passing in `ConnectivityOptions`(): +### Options + ```kotlin val connectivity = Connectivity { autoStart = true @@ -225,18 +229,18 @@ You can customize the HTTP monitoring like so: ```kotlin val connectivity = Connectivity { - urls("cloudflare.com", "my-own-domain.com") - port = 80 - pollingIntervalMs = 10.minutes - timeoutMs = 5.seconds - - // Callback for when a poll is completed - onPollResult { result -> - when (result) { - is PollResult.Error -> println("Poll error: ${result.error}") - is PollResult.Response -> println("Poll http response: ${result.response}") - } + urls("cloudflare.com", "my-own-domain.com") // Defaults to ["google.com", "github.com", "bing.com"] + port = 80 // Defaults to 443 + pollingIntervalMs = 10.minutes // Defaults to 5 minutes + timeoutMs = 5.seconds // Defaults to 2 seconds + + // Callback for when a poll is completed + onPollResult { result -> + when (result) { + is PollResult.Error -> println("Poll error: ${result.error}") + is PollResult.Response -> println("Poll http response: ${result.response}") } + } } ``` From 9ab34de6ccc9111bc0514e6822a13433ca71ccb9 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 11:01:51 -0400 Subject: [PATCH 03/11] run apiDump --- connectivity-core/api/android/connectivity-core.api | 2 ++ connectivity-core/api/jvm/connectivity-core.api | 2 ++ 2 files changed, 4 insertions(+) diff --git a/connectivity-core/api/android/connectivity-core.api b/connectivity-core/api/android/connectivity-core.api index 23d9d5d..463f257 100644 --- a/connectivity-core/api/android/connectivity-core.api +++ b/connectivity-core/api/android/connectivity-core.api @@ -1,6 +1,8 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; + public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V diff --git a/connectivity-core/api/jvm/connectivity-core.api b/connectivity-core/api/jvm/connectivity-core.api index 23d9d5d..463f257 100644 --- a/connectivity-core/api/jvm/connectivity-core.api +++ b/connectivity-core/api/jvm/connectivity-core.api @@ -1,6 +1,8 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; + public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V From 5ea4af8983fb9a0e48e0124cc77767f67046a9a9 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 11:07:46 -0400 Subject: [PATCH 04/11] add deprecations --- .../kotlin/dev/jordond/connectivity/Connectivity.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt index 493becc..ecae816 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt @@ -23,6 +23,11 @@ public interface Connectivity { public val isActive: StateFlow + @Deprecated( + message = "Use statusUpdates instead. Will be removed in a future release.", + replaceWith = ReplaceWith("statusUpdates"), + level = DeprecationLevel.WARNING, + ) public val updates: StateFlow /** @@ -60,6 +65,10 @@ public interface Connectivity { * @property status The [Status] of the connectivity. * @constructor Creates an update to the connectivity status. */ + @Deprecated( + message = "This current usage of this class does not provide any additional value. Will be removed in a future release.", + level = DeprecationLevel.WARNING, + ) @Poko public class Update( public val isActive: Boolean, From 48e2a43f7e509910e966b940cd3166fa63383937 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 13:04:49 -0400 Subject: [PATCH 05/11] add artifacts for compose --- connectivity-compose-device/build.gradle.kts | 29 +++++ .../compose/DeviceConnectivityState.kt | 63 ++++++++++ connectivity-compose-http/build.gradle.kts | 29 +++++ .../compose/HttpConnectivityState.kt | 99 +++++++++++++++ connectivity-compose/build.gradle.kts | 26 ++++ .../connectivity/compose/ConnectivityState.kt | 115 ++++++++++++++++++ .../dev/jordond/connectivity/Connectivity.kt | 2 +- .../internal/DefaultConnectivity.kt | 4 +- .../connectivity/internal/HttpConnectivity.kt | 4 +- settings.gradle.kts | 3 + 10 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 connectivity-compose-device/build.gradle.kts create mode 100644 connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt create mode 100644 connectivity-compose-http/build.gradle.kts create mode 100644 connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt create mode 100644 connectivity-compose/build.gradle.kts create mode 100644 connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt diff --git a/connectivity-compose-device/build.gradle.kts b/connectivity-compose-device/build.gradle.kts new file mode 100644 index 0000000..4d627c3 --- /dev/null +++ b/connectivity-compose-device/build.gradle.kts @@ -0,0 +1,29 @@ +import dev.jordond.connectivity.convention.Platforms +import dev.jordond.connectivity.convention.configureMultiplatform + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.multiplatform) + alias(libs.plugins.compose) + alias(libs.plugins.compose.compiler) + alias(libs.plugins.poko) + alias(libs.plugins.dokka) + alias(libs.plugins.publish) + alias(libs.plugins.convention.multiplatform) +} + +configureMultiplatform(Platforms.Mobile) + +kotlin { + sourceSets { + commonMain.dependencies { + implementation(projects.connectivityCore) + implementation(projects.connectivityCompose) + api(projects.connectivityDevice) + + implementation(compose.runtime) + implementation(compose.ui) + implementation(libs.kotlinx.coroutines.core) + } + } +} \ No newline at end of file diff --git a/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt b/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt new file mode 100644 index 0000000..0a353d7 --- /dev/null +++ b/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt @@ -0,0 +1,63 @@ +package dev.jordond.connectivity.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.ConnectivityOptions +import kotlinx.coroutines.CoroutineScope + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @return A [ConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + options: ConnectivityOptions = ConnectivityOptions(), + scope: CoroutineScope = rememberCoroutineScope(), +): ConnectivityState { + val factory = remember(options, scope) { + { Connectivity(options, scope) } + } + + return remember(options, scope, factory) { + ConnectivityState(options, scope, factory) + } +} + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param block A lambda function to configure the [ConnectivityOptions] for the network status monitoring. + * @return A [ConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + scope: CoroutineScope = rememberCoroutineScope(), + block: ConnectivityOptions.Builder.() -> Unit, +): ConnectivityState { + val options = remember(block) { + ConnectivityOptions.build(block) + } + + return rememberConnectivityState(options = options, scope = scope) +} + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param autoStart A Boolean indicating whether to start monitoring the network status automatically. + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @return A [ConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + autoStart: Boolean = false, + scope: CoroutineScope = rememberCoroutineScope(), +): ConnectivityState { + return rememberConnectivityState(ConnectivityOptions(autoStart), scope) +} \ No newline at end of file diff --git a/connectivity-compose-http/build.gradle.kts b/connectivity-compose-http/build.gradle.kts new file mode 100644 index 0000000..83f973e --- /dev/null +++ b/connectivity-compose-http/build.gradle.kts @@ -0,0 +1,29 @@ +import dev.jordond.connectivity.convention.Platforms +import dev.jordond.connectivity.convention.configureMultiplatform + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.multiplatform) + alias(libs.plugins.compose) + alias(libs.plugins.compose.compiler) + alias(libs.plugins.poko) + alias(libs.plugins.dokka) + alias(libs.plugins.publish) + alias(libs.plugins.convention.multiplatform) +} + +configureMultiplatform(Platforms.Compose) + +kotlin { + sourceSets { + commonMain.dependencies { + implementation(projects.connectivityCore) + implementation(projects.connectivityCompose) + api(projects.connectivityHttp) + + implementation(compose.runtime) + implementation(compose.ui) + implementation(libs.kotlinx.coroutines.core) + } + } +} \ No newline at end of file diff --git a/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt b/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt new file mode 100644 index 0000000..000f2a2 --- /dev/null +++ b/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt @@ -0,0 +1,99 @@ +package dev.jordond.connectivity.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.ConnectivityOptions +import dev.jordond.connectivity.HttpConnectivityOptions +import io.ktor.client.HttpClient +import kotlinx.coroutines.CoroutineScope + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param httpClient The [HttpClient] instance to use for network requests. + * @return A [HttpConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + options: HttpConnectivityOptions = HttpConnectivityOptions(), + scope: CoroutineScope = rememberCoroutineScope(), + httpClient: HttpClient = HttpClient() +): HttpConnectivityState { + val factory = remember(options, scope, httpClient) { + { Connectivity(options, scope, httpClient) } + } + + return remember(options, scope, factory) { + HttpConnectivityState(options, scope, factory) + } +} + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param httpClient The [HttpClient] instance to use for network requests. + * @param block A lambda function to configure the [HttpConnectivityOptions] instance. + * @return A [HttpConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + scope: CoroutineScope = rememberCoroutineScope(), + httpClient: HttpClient = HttpClient(), + block: HttpConnectivityOptions.Builder.() -> Unit, +): HttpConnectivityState { + val options = remember(block) { + HttpConnectivityOptions.build(block) + } + + return rememberConnectivityState(options = options, scope = scope, httpClient = httpClient) +} + +/** + * Create and remember a [ConnectivityState] instance for Android and iOS platforms. + * + * @param autoStart A Boolean indicating whether to start monitoring the network status automatically. + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param httpClient The [HttpClient] instance to use for network requests. + * @return A [HttpConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + autoStart: Boolean = false, + scope: CoroutineScope = rememberCoroutineScope(), + httpClient: HttpClient = HttpClient() +): HttpConnectivityState { + return rememberConnectivityState( + scope = scope, + httpClient = httpClient, + block = { this.autoStart = autoStart }, + ) +} + +/** + * Represents the state of network connectivity. + * + * This class provides properties and functions for monitoring the network status and checking + * whether the device is connected to the network or a metered network. + * + * @property options The [HttpConnectivityOptions] to use for configuring the network status monitoring. + * @property scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param factory The lambda function to create a new [Connectivity] instance. + */ +@Stable +public class HttpConnectivityState( + options: HttpConnectivityOptions, + scope: CoroutineScope, + factory: () -> Connectivity, +) : ConnectivityState(options.options, scope, factory) { + + /** + * The [HttpConnectivityOptions] instance used for configuring the network status monitoring. + */ + public val httpOptions: HttpConnectivityOptions = options +} \ No newline at end of file diff --git a/connectivity-compose/build.gradle.kts b/connectivity-compose/build.gradle.kts new file mode 100644 index 0000000..f91819d --- /dev/null +++ b/connectivity-compose/build.gradle.kts @@ -0,0 +1,26 @@ +import dev.jordond.connectivity.convention.Platforms +import dev.jordond.connectivity.convention.configureMultiplatform + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.multiplatform) + alias(libs.plugins.compose) + alias(libs.plugins.compose.compiler) + alias(libs.plugins.poko) + alias(libs.plugins.dokka) + alias(libs.plugins.publish) + alias(libs.plugins.convention.multiplatform) +} + +configureMultiplatform(Platforms.Compose) + +kotlin { + sourceSets { + commonMain.dependencies { + implementation(projects.connectivityCore) + implementation(compose.runtime) + implementation(compose.ui) + implementation(libs.kotlinx.coroutines.core) + } + } +} \ No newline at end of file diff --git a/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt new file mode 100644 index 0000000..081a21c --- /dev/null +++ b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt @@ -0,0 +1,115 @@ +package dev.jordond.connectivity.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.Connectivity.Status.Connected +import dev.jordond.connectivity.Connectivity.Status.Disconnected +import dev.jordond.connectivity.ConnectivityOptions +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + + +/** + * Create and remember a [ConnectivityState] instance. + * + * @param options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param factory The lambda to use for creating the [Connectivity] instance. + * @return A [ConnectivityState] instance. + */ +@Composable +public fun rememberConnectivityState( + options: ConnectivityOptions = ConnectivityOptions(), + scope: CoroutineScope = rememberCoroutineScope(), + factory: () -> Connectivity, +): ConnectivityState { + return remember(options, scope, factory) { + ConnectivityState(options, scope, factory) + } +} + +/** + * Represents the state of network connectivity. + * + * This class provides properties and functions for monitoring the network status and checking + * whether the device is connected to the network or a metered network. + * + * @property options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @property scope The [CoroutineScope] in which to launch the network status monitoring coroutine. + * @param factory The lambda to use for creating the [Connectivity] instance. + */ +@Stable +public open class ConnectivityState( + private val options: ConnectivityOptions = ConnectivityOptions(), + private val scope: CoroutineScope, + factory: () -> Connectivity, +) { + + private val connectivity: Connectivity = factory() + + private var isMonitoring: Boolean by mutableStateOf(options.autoStart) + + /** + * The current network status. + * + * This value will be `null` if the network status has not been checked yet. + * Call [startMonitoring] to start monitoring the status, or [forceCheck] to force a check. + */ + public var status: Connectivity.Status? by mutableStateOf(null) + private set + + /** + * A Boolean indicating whether the device is connected to the network. + */ + public val isConnected: Boolean + get() = status is Connected + + /** + * A Boolean indicating whether the device is connected to a metered network. + */ + public val isMetered: Boolean + get() = status?.let { it is Connected && it.isMetered } ?: false + + /** + * A Boolean indicating whether the device is disconnected from the network. + */ + public val isDisconnected: Boolean + get() = status is Disconnected + + init { + scope.launch { + connectivity.isActive.collect { isMonitoring = it } + } + + scope.launch { + connectivity.statusUpdates.collect { status = it } + } + } + + /** + * Force a check of the current network status. + */ + public fun forceCheck() { + scope.launch { connectivity.status() } + } + + /** + * Start monitoring the network status. + */ + public fun startMonitoring() { + connectivity.start() + } + + /** + * Stop monitoring the network status. + */ + public fun stopMonitoring() { + connectivity.stop() + } +} \ No newline at end of file diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt index ecae816..41e7e01 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt @@ -97,7 +97,7 @@ public interface Connectivity { public companion object { @InternalConnectivityApi - public val default: Update = Update(isActive = false, Status.Disconnected) + public val default: Update = Update(isActive = false, Disconnected) } } diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt index 25308ee..bff2546 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt @@ -52,7 +52,9 @@ internal class DefaultConnectivity( } override suspend fun status(): Connectivity.Status { - return provider.monitor().first() + return provider.monitor().first().also { status -> + _statusUpdates.emit(status) + } } override fun start() { diff --git a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt index 786c0b4..33f98cf 100644 --- a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt +++ b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt @@ -59,7 +59,9 @@ internal class HttpConnectivity( } override suspend fun status(): Connectivity.Status { - return checkConnection() + return checkConnection().also { status -> + _statusUpdates.emit(status) + } } override fun start() { diff --git a/settings.gradle.kts b/settings.gradle.kts index 9762987..e991b3f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,9 @@ include( ":connectivity-apple", ":connectivity-device", ":connectivity-http", + ":connectivity-compose", + ":connectivity-compose-device", + ":connectivity-compose-http", ":connectivity-tools-android", ) From cbe9eaa763359012d26339f2ee101097acbcf01c Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 13:49:09 -0400 Subject: [PATCH 06/11] add compose support for device and http --- connectivity-compose-device/build.gradle.kts | 2 +- .../compose/DeviceConnectivityState.kt | 23 +----- connectivity-compose-http/build.gradle.kts | 2 +- .../compose/HttpConnectivityState.kt | 61 ++------------ .../connectivity/compose/ConnectivityState.kt | 22 ++--- .../connectivity/ConnectivityOptions.kt | 2 +- demo/composeApp/build.gradle.kts | 3 + .../src/commonMain/kotlin/HomeModel.kt | 8 +- .../src/commonMain/kotlin/HomeScreen.kt | 81 +++++++++++++------ .../deviceMain/kotlin/HomeScreen.device.kt | 10 +++ .../src/httpMain/kotlin/HomeScreen.http.kt | 12 +++ 11 files changed, 108 insertions(+), 118 deletions(-) create mode 100644 demo/composeApp/src/deviceMain/kotlin/HomeScreen.device.kt create mode 100644 demo/composeApp/src/httpMain/kotlin/HomeScreen.http.kt diff --git a/connectivity-compose-device/build.gradle.kts b/connectivity-compose-device/build.gradle.kts index 4d627c3..5a495a6 100644 --- a/connectivity-compose-device/build.gradle.kts +++ b/connectivity-compose-device/build.gradle.kts @@ -18,7 +18,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.connectivityCore) - implementation(projects.connectivityCompose) + api(projects.connectivityCompose) api(projects.connectivityDevice) implementation(compose.runtime) diff --git a/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt b/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt index 0a353d7..75a9c43 100644 --- a/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt +++ b/connectivity-compose-device/src/commonMain/kotlin/dev/jordond/connectivity/compose/DeviceConnectivityState.kt @@ -19,12 +19,12 @@ public fun rememberConnectivityState( options: ConnectivityOptions = ConnectivityOptions(), scope: CoroutineScope = rememberCoroutineScope(), ): ConnectivityState { - val factory = remember(options, scope) { - { Connectivity(options, scope) } + val connectivity = remember(options, scope) { + Connectivity(options, scope) } - return remember(options, scope, factory) { - ConnectivityState(options, scope, factory) + return remember(options, scope, connectivity) { + ConnectivityState(connectivity, scope) } } @@ -45,19 +45,4 @@ public fun rememberConnectivityState( } return rememberConnectivityState(options = options, scope = scope) -} - -/** - * Create and remember a [ConnectivityState] instance for Android and iOS platforms. - * - * @param autoStart A Boolean indicating whether to start monitoring the network status automatically. - * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. - * @return A [ConnectivityState] instance. - */ -@Composable -public fun rememberConnectivityState( - autoStart: Boolean = false, - scope: CoroutineScope = rememberCoroutineScope(), -): ConnectivityState { - return rememberConnectivityState(ConnectivityOptions(autoStart), scope) } \ No newline at end of file diff --git a/connectivity-compose-http/build.gradle.kts b/connectivity-compose-http/build.gradle.kts index 83f973e..13fcd06 100644 --- a/connectivity-compose-http/build.gradle.kts +++ b/connectivity-compose-http/build.gradle.kts @@ -18,7 +18,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.connectivityCore) - implementation(projects.connectivityCompose) + api(projects.connectivityCompose) api(projects.connectivityHttp) implementation(compose.runtime) diff --git a/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt b/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt index 000f2a2..c52de06 100644 --- a/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt +++ b/connectivity-compose-http/src/commonMain/kotlin/dev/jordond/connectivity/compose/HttpConnectivityState.kt @@ -1,7 +1,6 @@ package dev.jordond.connectivity.compose import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.jordond.connectivity.Connectivity @@ -16,20 +15,20 @@ import kotlinx.coroutines.CoroutineScope * @param options The [ConnectivityOptions] to use for configuring the network status monitoring. * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. * @param httpClient The [HttpClient] instance to use for network requests. - * @return A [HttpConnectivityState] instance. + * @return A [ConnectivityState] instance. */ @Composable public fun rememberConnectivityState( options: HttpConnectivityOptions = HttpConnectivityOptions(), scope: CoroutineScope = rememberCoroutineScope(), httpClient: HttpClient = HttpClient() -): HttpConnectivityState { - val factory = remember(options, scope, httpClient) { - { Connectivity(options, scope, httpClient) } +): ConnectivityState { + val connectivity = remember(options, scope, httpClient) { + Connectivity(options, scope, httpClient) } - return remember(options, scope, factory) { - HttpConnectivityState(options, scope, factory) + return remember(connectivity, scope) { + ConnectivityState(connectivity, scope) } } @@ -39,61 +38,17 @@ public fun rememberConnectivityState( * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. * @param httpClient The [HttpClient] instance to use for network requests. * @param block A lambda function to configure the [HttpConnectivityOptions] instance. - * @return A [HttpConnectivityState] instance. + * @return A [ConnectivityState] instance. */ @Composable public fun rememberConnectivityState( scope: CoroutineScope = rememberCoroutineScope(), httpClient: HttpClient = HttpClient(), block: HttpConnectivityOptions.Builder.() -> Unit, -): HttpConnectivityState { +): ConnectivityState { val options = remember(block) { HttpConnectivityOptions.build(block) } return rememberConnectivityState(options = options, scope = scope, httpClient = httpClient) -} - -/** - * Create and remember a [ConnectivityState] instance for Android and iOS platforms. - * - * @param autoStart A Boolean indicating whether to start monitoring the network status automatically. - * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. - * @param httpClient The [HttpClient] instance to use for network requests. - * @return A [HttpConnectivityState] instance. - */ -@Composable -public fun rememberConnectivityState( - autoStart: Boolean = false, - scope: CoroutineScope = rememberCoroutineScope(), - httpClient: HttpClient = HttpClient() -): HttpConnectivityState { - return rememberConnectivityState( - scope = scope, - httpClient = httpClient, - block = { this.autoStart = autoStart }, - ) -} - -/** - * Represents the state of network connectivity. - * - * This class provides properties and functions for monitoring the network status and checking - * whether the device is connected to the network or a metered network. - * - * @property options The [HttpConnectivityOptions] to use for configuring the network status monitoring. - * @property scope The [CoroutineScope] in which to launch the network status monitoring coroutine. - * @param factory The lambda function to create a new [Connectivity] instance. - */ -@Stable -public class HttpConnectivityState( - options: HttpConnectivityOptions, - scope: CoroutineScope, - factory: () -> Connectivity, -) : ConnectivityState(options.options, scope, factory) { - - /** - * The [HttpConnectivityOptions] instance used for configuring the network status monitoring. - */ - public val httpOptions: HttpConnectivityOptions = options } \ No newline at end of file diff --git a/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt index 081a21c..f77176a 100644 --- a/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt +++ b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.setValue import dev.jordond.connectivity.Connectivity import dev.jordond.connectivity.Connectivity.Status.Connected import dev.jordond.connectivity.Connectivity.Status.Disconnected -import dev.jordond.connectivity.ConnectivityOptions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -18,19 +17,17 @@ import kotlinx.coroutines.launch /** * Create and remember a [ConnectivityState] instance. * - * @param options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @param connectivity The [Connectivity] instance to use for monitoring the network status. * @param scope The [CoroutineScope] in which to launch the network status monitoring coroutine. - * @param factory The lambda to use for creating the [Connectivity] instance. * @return A [ConnectivityState] instance. */ @Composable public fun rememberConnectivityState( - options: ConnectivityOptions = ConnectivityOptions(), + connectivity: Connectivity, scope: CoroutineScope = rememberCoroutineScope(), - factory: () -> Connectivity, ): ConnectivityState { - return remember(options, scope, factory) { - ConnectivityState(options, scope, factory) + return remember(scope, connectivity) { + ConnectivityState(connectivity, scope) } } @@ -40,20 +37,17 @@ public fun rememberConnectivityState( * This class provides properties and functions for monitoring the network status and checking * whether the device is connected to the network or a metered network. * - * @property options The [ConnectivityOptions] to use for configuring the network status monitoring. + * @property connectivity The [Connectivity] instance to use for monitoring the network status. * @property scope The [CoroutineScope] in which to launch the network status monitoring coroutine. - * @param factory The lambda to use for creating the [Connectivity] instance. */ @Stable public open class ConnectivityState( - private val options: ConnectivityOptions = ConnectivityOptions(), + private val connectivity: Connectivity, private val scope: CoroutineScope, - factory: () -> Connectivity, ) { - private val connectivity: Connectivity = factory() - - private var isMonitoring: Boolean by mutableStateOf(options.autoStart) + public var isMonitoring: Boolean by mutableStateOf(connectivity.isActive.value) + private set /** * The current network status. diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/ConnectivityOptions.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/ConnectivityOptions.kt index a9dce5a..1c34cbf 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/ConnectivityOptions.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/ConnectivityOptions.kt @@ -27,7 +27,7 @@ public open class ConnectivityOptions( */ public class Builder internal constructor() { - private var autoStart: Boolean = DEFAULT_AUTO_START + public var autoStart: Boolean = DEFAULT_AUTO_START /** * Sets the autoStart property of the Builder. diff --git a/demo/composeApp/build.gradle.kts b/demo/composeApp/build.gradle.kts index 6dddf56..fdf894b 100644 --- a/demo/composeApp/build.gradle.kts +++ b/demo/composeApp/build.gradle.kts @@ -42,6 +42,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.connectivityCore) + implementation(projects.connectivityCompose) implementation(libs.kotlinx.coroutines.core) implementation(compose.runtime) @@ -66,6 +67,7 @@ kotlin { appleMain.get().dependsOn(this) dependencies { implementation(projects.connectivityDevice) + implementation(projects.connectivityComposeDevice) } } @@ -83,6 +85,7 @@ kotlin { wasmJsMain.get().dependsOn(this) dependencies { implementation(projects.connectivityHttp) + implementation(projects.connectivityComposeHttp) } } } diff --git a/demo/composeApp/src/commonMain/kotlin/HomeModel.kt b/demo/composeApp/src/commonMain/kotlin/HomeModel.kt index 6ebea01..cd33455 100644 --- a/demo/composeApp/src/commonMain/kotlin/HomeModel.kt +++ b/demo/composeApp/src/commonMain/kotlin/HomeModel.kt @@ -15,7 +15,7 @@ class HomeModel : StateScreenModel(State()) { Logger.i { "Connectivity update: $update" } updateState { state -> state.copy( - active = update.isActive, + monitoring = update.isActive, status = if (update.isActive) update.status else state.status, ) } @@ -24,21 +24,19 @@ class HomeModel : StateScreenModel(State()) { } fun toggle() { - if (state.value.active) stop() else start() + if (state.value.monitoring) stop() else start() } private fun start() { connectivity.start() - updateState { it.copy(active = true) } } private fun stop() { connectivity.stop() - updateState { it.copy(active = false) } } data class State( val status: Connectivity.Status? = null, - val active: Boolean = false, + val monitoring: Boolean = false, ) } \ No newline at end of file diff --git a/demo/composeApp/src/commonMain/kotlin/HomeScreen.kt b/demo/composeApp/src/commonMain/kotlin/HomeScreen.kt index daecec2..046fc6a 100644 --- a/demo/composeApp/src/commonMain/kotlin/HomeScreen.kt +++ b/demo/composeApp/src/commonMain/kotlin/HomeScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button +import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -16,6 +17,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.screen.Screen +import dev.jordond.connectivity.Connectivity +import dev.jordond.connectivity.compose.ConnectivityState class HomeScreen : Screen { @@ -30,24 +33,28 @@ class HomeScreen : Screen { verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxSize(), ) { - Crossfade(state.status) { status -> - if (status == null) { - Text("No status yet") - } else { - val text = if (!status.isConnected) "Disconnected" - else "Connected${if (status.isMetered) " - Metered" else ""}" + ConnectivityContent( + title = "Manual Connectivity", + status = state.status, + isMonitoring = state.monitoring, + onToggle = model::toggle, + ) - Text("Status: $text") - } - } - - Spacer(modifier = Modifier.height(32.dp)) - - Button( - onClick = model::toggle, - modifier = Modifier.padding(8.dp) - ) { - Text("${if (state.active) "Stop" else "Start"} Monitoring") + Card { + val connectivityState = createConnectivityState() + ConnectivityContent( + title = "Compose Connectivity", + status = connectivityState.status, + isMonitoring = connectivityState.isMonitoring, + onToggle = { + if (connectivityState.isMonitoring) { + connectivityState.stopMonitoring() + } else { + connectivityState.startMonitoring() + } + }, + modifier = Modifier.padding(8.dp), + ) } } } @@ -55,14 +62,40 @@ class HomeScreen : Screen { } @Composable -private fun NavButton( - text: String, - onClick: () -> Unit, +internal expect fun createConnectivityState(): ConnectivityState + +@Composable +private fun ConnectivityContent( + title: String, + status: Connectivity.Status?, + isMonitoring: Boolean, + onToggle: () -> Unit, + modifier: Modifier = Modifier, ) { - Button( - onClick = onClick, - modifier = Modifier.padding(8.dp) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = modifier, ) { - Text(text) + Text(title) + Crossfade(status) { status -> + if (status == null) { + Text("No status yet") + } else { + val text = if (!status.isConnected) "Disconnected" + else "Connected${if (status.isMetered) " - Metered" else ""}" + + Text("Status: $text") + } + } + + Spacer(modifier = Modifier.height(32.dp)) + + Button( + onClick = onToggle, + modifier = Modifier.padding(8.dp) + ) { + Text("${if (isMonitoring) "Stop" else "Start"} Monitoring") + } } } \ No newline at end of file diff --git a/demo/composeApp/src/deviceMain/kotlin/HomeScreen.device.kt b/demo/composeApp/src/deviceMain/kotlin/HomeScreen.device.kt new file mode 100644 index 0000000..0645fde --- /dev/null +++ b/demo/composeApp/src/deviceMain/kotlin/HomeScreen.device.kt @@ -0,0 +1,10 @@ +import androidx.compose.runtime.Composable +import dev.jordond.connectivity.compose.ConnectivityState +import dev.jordond.connectivity.compose.rememberConnectivityState + +@Composable +internal actual fun createConnectivityState(): ConnectivityState { + return rememberConnectivityState { + autoStart = true + } +} \ No newline at end of file diff --git a/demo/composeApp/src/httpMain/kotlin/HomeScreen.http.kt b/demo/composeApp/src/httpMain/kotlin/HomeScreen.http.kt new file mode 100644 index 0000000..1c0766d --- /dev/null +++ b/demo/composeApp/src/httpMain/kotlin/HomeScreen.http.kt @@ -0,0 +1,12 @@ +import androidx.compose.runtime.Composable +import dev.jordond.connectivity.compose.ConnectivityState +import dev.jordond.connectivity.compose.rememberConnectivityState + +@Composable +internal actual fun createConnectivityState(): ConnectivityState { + return rememberConnectivityState { + autoStart = true + url("www.google.com") + pollingIntervalMs = 1.minutes + } +} \ No newline at end of file From 039fca4ab0ecf82e43ce95ca73a4540cc10d283d Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 13:54:01 -0400 Subject: [PATCH 07/11] rename isActive to isMonitoring --- README.MD | 4 +-- .../connectivity/compose/ConnectivityState.kt | 4 +-- .../dev/jordond/connectivity/Connectivity.kt | 23 +++++++++++---- .../internal/DefaultConnectivity.kt | 24 ++++++++-------- .../connectivity/internal/HttpConnectivity.kt | 28 +++++++++++-------- 5 files changed, 51 insertions(+), 32 deletions(-) diff --git a/README.MD b/README.MD index ad0a1eb..9a34774 100644 --- a/README.MD +++ b/README.MD @@ -167,7 +167,7 @@ val connectivity = Connectivity() connectivity.start() coroutineScope.launch { connectivity.updates.collect { update -> - println("Monitoring is active: ${update.isActive}") + println("Monitoring is active: ${update.isMonitoring}") when (update.status) { is NetworkStatus.Connected -> println("Connected to network") @@ -179,7 +179,7 @@ coroutineScope.launch { **Note:** Because the backing field for `Connectivity.updates` is a `StateFlow`, it requires a initial value to be emitted. This means that the first value emitted will be the *real* current -network status. This is why the `isActive` property is available on the `Connectivity.Update`. +network status. This is why the `isMonitoring` property is available on the `Connectivity.Update`. By default when you construct a `Connectivity` object, it will not automatically start monitoring network connectivity. You can enable this by passing in `ConnectivityOptions`(): diff --git a/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt index f77176a..7a670c0 100644 --- a/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt +++ b/connectivity-compose/src/commonMain/kotlin/dev/jordond/connectivity/compose/ConnectivityState.kt @@ -46,7 +46,7 @@ public open class ConnectivityState( private val scope: CoroutineScope, ) { - public var isMonitoring: Boolean by mutableStateOf(connectivity.isActive.value) + public var isMonitoring: Boolean by mutableStateOf(connectivity.isMonitoring.value) private set /** @@ -78,7 +78,7 @@ public open class ConnectivityState( init { scope.launch { - connectivity.isActive.collect { isMonitoring = it } + connectivity.isMonitoring.collect { isMonitoring = it } } scope.launch { diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt index 41e7e01..85a7033 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/Connectivity.kt @@ -14,14 +14,15 @@ import kotlinx.coroutines.flow.StateFlow * The Connectivity interface provides a way to monitor the network connectivity status. * * @property statusUpdates A [SharedFlow] representing the current connectivity status. - * @property isActive A [StateFlow] representing whether the connectivity monitoring is active. - * @property updates A [StateFlow] representing the current connectivity status and whether the monitoring is active. + * @property isMonitoring A [StateFlow] representing whether the connectivity monitoring is active. + * @property updates A [StateFlow] representing the current connectivity status and whether + * the monitoring is active. */ public interface Connectivity { public val statusUpdates: SharedFlow - public val isActive: StateFlow + public val isMonitoring: StateFlow @Deprecated( message = "Use statusUpdates instead. Will be removed in a future release.", @@ -61,7 +62,7 @@ public interface Connectivity { /** * Represents an update to the connectivity status. * - * @property isActive A Boolean indicating whether the connectivity monitoring is active. + * @property isMonitoring A Boolean indicating whether the connectivity monitoring is active. * @property status The [Status] of the connectivity. * @constructor Creates an update to the connectivity status. */ @@ -71,10 +72,20 @@ public interface Connectivity { ) @Poko public class Update( - public val isActive: Boolean, + public val isMonitoring: Boolean, public val status: Status, ) { + /** + * A Boolean indicating whether the connectivity monitoring is active. + */ + @Deprecated( + message = "Use isMonitoring instead. Will be removed in a future release.", + replaceWith = ReplaceWith("isMonitoring"), + level = DeprecationLevel.WARNING, + ) + public val isActive: Boolean = isMonitoring + /** * A Boolean indicating whether the device is connected to the network. */ @@ -97,7 +108,7 @@ public interface Connectivity { public companion object { @InternalConnectivityApi - public val default: Update = Update(isActive = false, Disconnected) + public val default: Update = Update(isMonitoring = false, Disconnected) } } diff --git a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt index bff2546..e320a91 100644 --- a/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt +++ b/connectivity-core/src/commonMain/kotlin/dev/jordond/connectivity/internal/DefaultConnectivity.kt @@ -34,16 +34,18 @@ internal class DefaultConnectivity( ) override val statusUpdates: SharedFlow = _statusUpdates.asSharedFlow() - private val _isActive = MutableStateFlow(false) - override val isActive: StateFlow = _isActive.asStateFlow() + private val _isMonitoring = MutableStateFlow(false) + override val isMonitoring: StateFlow = _isMonitoring.asStateFlow() - override val updates: StateFlow = combine(statusUpdates, isActive) { status, isActive -> - Update(isActive, status) - }.stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = Update(isActive = false, Connectivity.Status.Disconnected) - ) + @Deprecated("Use statusUpdates instead", ReplaceWith("statusUpdates")) + override val updates: StateFlow = + combine(statusUpdates, isMonitoring) { status, isMonitoring -> + Update(isMonitoring, status) + }.stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = Update(isMonitoring = false, Connectivity.Status.Disconnected) + ) init { if (options.autoStart) { @@ -60,7 +62,7 @@ internal class DefaultConnectivity( override fun start() { job?.cancel() job = launch { - _isActive.update { true } + _isMonitoring.update { true } provider.monitor().collect { status -> _statusUpdates.emit(status) } @@ -70,6 +72,6 @@ internal class DefaultConnectivity( override fun stop() { job?.cancel() job = null - _isActive.update { false } + _isMonitoring.update { false } } } \ No newline at end of file diff --git a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt index 33f98cf..02dbb97 100644 --- a/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt +++ b/connectivity-http/src/commonMain/kotlin/dev/jordond/connectivity/internal/HttpConnectivity.kt @@ -41,16 +41,22 @@ internal class HttpConnectivity( ) override val statusUpdates: SharedFlow = _statusUpdates.asSharedFlow() - private val _isActive = MutableStateFlow(value = false) - override val isActive: StateFlow = _isActive.asStateFlow() - - override val updates: StateFlow = combine(statusUpdates, isActive) { status, isActive -> - Update(isActive, status) - }.stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = Update(isActive = false, Connectivity.Status.Disconnected) + private val _isMonitoring = MutableStateFlow(value = false) + override val isMonitoring: StateFlow = _isMonitoring.asStateFlow() + + @Deprecated( + message = "Use statusUpdates instead. Will be removed in a future release.", + replaceWith = ReplaceWith("statusUpdates"), + level = DeprecationLevel.WARNING, ) + override val updates: StateFlow = + combine(statusUpdates, isMonitoring) { status, isMonitoring -> + Update(isMonitoring, status) + }.stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = Update(isMonitoring = false, Connectivity.Status.Disconnected) + ) init { if (httpOptions.options.autoStart) { @@ -67,13 +73,13 @@ internal class HttpConnectivity( override fun start() { if (job != null) return poll() - _isActive.update { true } + _isMonitoring.update { true } } override fun stop() { job?.cancel() job = null - _isActive.update { false } + _isMonitoring.update { false } } internal fun forcePoll() { From bbc3fe9da41531d3aa9549a6d588d732f98fff74 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 13:57:03 -0400 Subject: [PATCH 08/11] run apiDump --- .../api/connectivity-compose-device.api | 5 +++++ .../api/android/connectivity-compose-http.api | 5 +++++ .../api/jvm/connectivity-compose-http.api | 5 +++++ .../api/android/connectivity-compose.api | 17 +++++++++++++++++ .../api/jvm/connectivity-compose.api | 17 +++++++++++++++++ .../api/android/connectivity-core.api | 5 ++++- connectivity-core/api/jvm/connectivity-core.api | 5 ++++- 7 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 connectivity-compose-device/api/connectivity-compose-device.api create mode 100644 connectivity-compose-http/api/android/connectivity-compose-http.api create mode 100644 connectivity-compose-http/api/jvm/connectivity-compose-http.api create mode 100644 connectivity-compose/api/android/connectivity-compose.api create mode 100644 connectivity-compose/api/jvm/connectivity-compose.api diff --git a/connectivity-compose-device/api/connectivity-compose-device.api b/connectivity-compose-device/api/connectivity-compose-device.api new file mode 100644 index 0000000..952caf3 --- /dev/null +++ b/connectivity-compose-device/api/connectivity-compose-device.api @@ -0,0 +1,5 @@ +public final class dev/jordond/connectivity/compose/DeviceConnectivityStateKt { + public static final fun rememberConnectivityState (Ldev/jordond/connectivity/ConnectivityOptions;Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; + public static final fun rememberConnectivityState (Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; +} + diff --git a/connectivity-compose-http/api/android/connectivity-compose-http.api b/connectivity-compose-http/api/android/connectivity-compose-http.api new file mode 100644 index 0000000..c099277 --- /dev/null +++ b/connectivity-compose-http/api/android/connectivity-compose-http.api @@ -0,0 +1,5 @@ +public final class dev/jordond/connectivity/compose/HttpConnectivityStateKt { + public static final fun rememberConnectivityState (Ldev/jordond/connectivity/HttpConnectivityOptions;Lkotlinx/coroutines/CoroutineScope;Lio/ktor/client/HttpClient;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; + public static final fun rememberConnectivityState (Lkotlinx/coroutines/CoroutineScope;Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; +} + diff --git a/connectivity-compose-http/api/jvm/connectivity-compose-http.api b/connectivity-compose-http/api/jvm/connectivity-compose-http.api new file mode 100644 index 0000000..c099277 --- /dev/null +++ b/connectivity-compose-http/api/jvm/connectivity-compose-http.api @@ -0,0 +1,5 @@ +public final class dev/jordond/connectivity/compose/HttpConnectivityStateKt { + public static final fun rememberConnectivityState (Ldev/jordond/connectivity/HttpConnectivityOptions;Lkotlinx/coroutines/CoroutineScope;Lio/ktor/client/HttpClient;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; + public static final fun rememberConnectivityState (Lkotlinx/coroutines/CoroutineScope;Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; +} + diff --git a/connectivity-compose/api/android/connectivity-compose.api b/connectivity-compose/api/android/connectivity-compose.api new file mode 100644 index 0000000..af90a84 --- /dev/null +++ b/connectivity-compose/api/android/connectivity-compose.api @@ -0,0 +1,17 @@ +public class dev/jordond/connectivity/compose/ConnectivityState { + public static final field $stable I + public fun (Ldev/jordond/connectivity/Connectivity;Lkotlinx/coroutines/CoroutineScope;)V + public final fun forceCheck ()V + public final fun getStatus ()Ldev/jordond/connectivity/Connectivity$Status; + public final fun isConnected ()Z + public final fun isDisconnected ()Z + public final fun isMetered ()Z + public final fun isMonitoring ()Z + public final fun startMonitoring ()V + public final fun stopMonitoring ()V +} + +public final class dev/jordond/connectivity/compose/ConnectivityStateKt { + public static final fun rememberConnectivityState (Ldev/jordond/connectivity/Connectivity;Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; +} + diff --git a/connectivity-compose/api/jvm/connectivity-compose.api b/connectivity-compose/api/jvm/connectivity-compose.api new file mode 100644 index 0000000..af90a84 --- /dev/null +++ b/connectivity-compose/api/jvm/connectivity-compose.api @@ -0,0 +1,17 @@ +public class dev/jordond/connectivity/compose/ConnectivityState { + public static final field $stable I + public fun (Ldev/jordond/connectivity/Connectivity;Lkotlinx/coroutines/CoroutineScope;)V + public final fun forceCheck ()V + public final fun getStatus ()Ldev/jordond/connectivity/Connectivity$Status; + public final fun isConnected ()Z + public final fun isDisconnected ()Z + public final fun isMetered ()Z + public final fun isMonitoring ()Z + public final fun startMonitoring ()V + public final fun stopMonitoring ()V +} + +public final class dev/jordond/connectivity/compose/ConnectivityStateKt { + public static final fun rememberConnectivityState (Ldev/jordond/connectivity/Connectivity;Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/Composer;II)Ldev/jordond/connectivity/compose/ConnectivityState; +} + diff --git a/connectivity-core/api/android/connectivity-core.api b/connectivity-core/api/android/connectivity-core.api index 463f257..f55e34e 100644 --- a/connectivity-core/api/android/connectivity-core.api +++ b/connectivity-core/api/android/connectivity-core.api @@ -2,7 +2,7 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; - public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isMonitoring ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V @@ -57,6 +57,7 @@ public final class dev/jordond/connectivity/Connectivity$Update { public final fun isConnected ()Z public final fun isDisconnected ()Z public final fun isMetered ()Z + public final fun isMonitoring ()Z public fun toString ()Ljava/lang/String; } @@ -81,6 +82,8 @@ public class dev/jordond/connectivity/ConnectivityOptions { public final class dev/jordond/connectivity/ConnectivityOptions$Builder { public final fun autoStart (Z)Ldev/jordond/connectivity/ConnectivityOptions$Builder; public final fun build ()Ldev/jordond/connectivity/ConnectivityOptions; + public final fun getAutoStart ()Z + public final fun setAutoStart (Z)V } public final class dev/jordond/connectivity/ConnectivityOptions$Companion { diff --git a/connectivity-core/api/jvm/connectivity-core.api b/connectivity-core/api/jvm/connectivity-core.api index 463f257..f55e34e 100644 --- a/connectivity-core/api/jvm/connectivity-core.api +++ b/connectivity-core/api/jvm/connectivity-core.api @@ -2,7 +2,7 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; - public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isMonitoring ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V @@ -57,6 +57,7 @@ public final class dev/jordond/connectivity/Connectivity$Update { public final fun isConnected ()Z public final fun isDisconnected ()Z public final fun isMetered ()Z + public final fun isMonitoring ()Z public fun toString ()Ljava/lang/String; } @@ -81,6 +82,8 @@ public class dev/jordond/connectivity/ConnectivityOptions { public final class dev/jordond/connectivity/ConnectivityOptions$Builder { public final fun autoStart (Z)Ldev/jordond/connectivity/ConnectivityOptions$Builder; public final fun build ()Ldev/jordond/connectivity/ConnectivityOptions; + public final fun getAutoStart ()Z + public final fun setAutoStart (Z)V } public final class dev/jordond/connectivity/ConnectivityOptions$Companion { From 700e5d394f987deb9ae10c2e7c8908bdca06dcf9 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 14:01:42 -0400 Subject: [PATCH 09/11] run apiDump --- connectivity-core/api/android/connectivity-core.api | 5 ++++- connectivity-core/api/jvm/connectivity-core.api | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/connectivity-core/api/android/connectivity-core.api b/connectivity-core/api/android/connectivity-core.api index 463f257..f55e34e 100644 --- a/connectivity-core/api/android/connectivity-core.api +++ b/connectivity-core/api/android/connectivity-core.api @@ -2,7 +2,7 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; - public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isMonitoring ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V @@ -57,6 +57,7 @@ public final class dev/jordond/connectivity/Connectivity$Update { public final fun isConnected ()Z public final fun isDisconnected ()Z public final fun isMetered ()Z + public final fun isMonitoring ()Z public fun toString ()Ljava/lang/String; } @@ -81,6 +82,8 @@ public class dev/jordond/connectivity/ConnectivityOptions { public final class dev/jordond/connectivity/ConnectivityOptions$Builder { public final fun autoStart (Z)Ldev/jordond/connectivity/ConnectivityOptions$Builder; public final fun build ()Ldev/jordond/connectivity/ConnectivityOptions; + public final fun getAutoStart ()Z + public final fun setAutoStart (Z)V } public final class dev/jordond/connectivity/ConnectivityOptions$Companion { diff --git a/connectivity-core/api/jvm/connectivity-core.api b/connectivity-core/api/jvm/connectivity-core.api index 463f257..f55e34e 100644 --- a/connectivity-core/api/jvm/connectivity-core.api +++ b/connectivity-core/api/jvm/connectivity-core.api @@ -2,7 +2,7 @@ public abstract interface class dev/jordond/connectivity/Connectivity { public abstract fun getActiveUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getStatusUpdates ()Lkotlinx/coroutines/flow/SharedFlow; public abstract fun getUpdates ()Lkotlinx/coroutines/flow/StateFlow; - public abstract fun isActive ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun isMonitoring ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun start ()V public abstract fun status (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stop ()V @@ -57,6 +57,7 @@ public final class dev/jordond/connectivity/Connectivity$Update { public final fun isConnected ()Z public final fun isDisconnected ()Z public final fun isMetered ()Z + public final fun isMonitoring ()Z public fun toString ()Ljava/lang/String; } @@ -81,6 +82,8 @@ public class dev/jordond/connectivity/ConnectivityOptions { public final class dev/jordond/connectivity/ConnectivityOptions$Builder { public final fun autoStart (Z)Ldev/jordond/connectivity/ConnectivityOptions$Builder; public final fun build ()Ldev/jordond/connectivity/ConnectivityOptions; + public final fun getAutoStart ()Z + public final fun setAutoStart (Z)V } public final class dev/jordond/connectivity/ConnectivityOptions$Companion { From 0876e0701dcdbc9ce8c44745cda279de336ed90b Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 14:17:24 -0400 Subject: [PATCH 10/11] Update README.MD --- README.MD | 127 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 14 deletions(-) diff --git a/README.MD b/README.MD index 9a34774..8316c9c 100644 --- a/README.MD +++ b/README.MD @@ -24,6 +24,8 @@ view the generated KDoc at [connectivity.jordond.dev](https://connectivity.jordo - [Usage](#usage) - [Options](#options) - [HTTP monitoring](#http-monitoring) +- [Compose](#compose) + - [Multiple Targets](#multiple-targets) - [Demo](#demo) - [Contributing](#contributing) - [License](#license) @@ -39,13 +41,16 @@ Monitor network connectivity: This library is written for Kotlin Multiplatform, and can be used on the following platforms: -| Artifact | Android | iOS | macOS | tvOS | Desktop | Browser (js/wasm) | -|------------------------|:-------:|:---:|:-----:|------|:-------:|:-----------------:| -| `connectivity-core` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| `connectivity-device` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | -| `connectivity-android` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | -| `connectivity-apple` | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | -| `connectivity-http` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Artifact | Android | iOS | macOS | tvOS | Desktop (JVM) | Browser (js/wasm) | +|-------------------------------|:-------:|:---:|:-----:|------|::|:-----------------:| +| `connectivity-core` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| `connectivity-device` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| `connectivity-android` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| `connectivity-apple` | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | +| `connectivity-http` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| `connectivity-compose` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +| `connectivity-compose-device` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +| `connectivity-compose-http` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ## Setup @@ -61,6 +66,9 @@ connectivity-device = { module = "dev.jordond.connectivity:connectivity-device", connectivity-android = { module = "dev.jordond.connectivity:connectivity-android", version.ref = "connectivity" } connectivity-apple = { module = "dev.jordond.connectivity:connectivity-apple", version.ref = "connectivity" } connectivity-http = { module = "dev.jordond.connectivity:connectivity-http", version.ref = "connectivity" } +connectivity-compose = { module = "dev.jordond.connectivity:connectivity-compose", version.ref = "connectivity" } +connectivity-compose-device = { module = "dev.jordond.connectivity:connectivity-compose-device", version.ref = "connectivity" } +connectivity-compose-http = { module = "dev.jordond.connectivity:connectivity-compose-http", version.ref = "connectivity" } ``` ### Single Platform @@ -69,8 +77,12 @@ Here is an example of how to add the dependencies to a single platform project t ```kotlin dependencies { - implementation(libs.connectivity.core) - implementation(libs.connectivity.android) + implementation(libs.connectivity.core) + implementation(libs.connectivity.android) + + // For compose support + implementation(libs.connectivity.compose.device) + } ``` @@ -83,8 +95,11 @@ Android and Apple devices: kotlin { sourceSets { commonMain.dependencies { - implementation(libs.connectivity.core) - implementation(libs.connectivity.device) + implementation(libs.connectivity.core) + implementation(libs.connectivity.device) + + // For compose support + implementation(libs.connectivity.compose.device) } } } @@ -100,7 +115,10 @@ It uses the `connectivity-device` for mobile targets, and `connectivity-http` fo kotlin { sourceSets { commonMain.dependencies { - implementation(libs.connectivity.core) + implementation(libs.connectivity.core) + + // For compose support + implementation(libs.connectivity.compose) } val deviceMain by creating { @@ -108,7 +126,10 @@ kotlin { androidMain.get().dependsOn(this) appleMain.get().dependsOn(this) dependencies { - implementation(libs.connectivity.device) + implementation(libs.connectivity.device) + + // For compose support + implementation(libs.connectivity.compose.device) } } @@ -118,7 +139,10 @@ kotlin { jsMain.get().dependsOn(this) wasmJsMain.get().dependsOn(this) dependencies { - implementation(libs.connectivity.http) + implementation(libs.connectivity.http) + + // For compose support + implementation(libs.connectivity.compose.http) } } } @@ -244,6 +268,81 @@ val connectivity = Connectivity { } ``` +## Compose + +Connectivity also provides support for Compose Multiplatform. To use it you will have to make sure +you add the dependencies for the `connectivity-compose-x` modules. + +Then you can use it like so: + +**Note:** This composable is provided by either `connectivity-compose-device` +or `connectivity-compose-http` artifact. + +```kotlin +@Composable +fun MyApp() { + val state = rememberConnectivityState { + // Optional configurator for ConnectivityOptions + autoStart = true + } + + when (state.status) { + is Connectivity.Status.Connected -> Text("Connected to network") + is Connectivity.Status.Disconnected -> Text("Disconnected from network") + else -> {} + } +} +``` + +### Multiple Targets + +If you need to support both Device and HTTP monitoring in the same project, you will have to do +something similar to [this](#all-supported-platforms). + +Example: + +```kotlin +// commonMain/Platform.kt +expect fun createConnectivity(): Connectivity +``` + +Then define the `actual` functions: + +```kotlin +// deviceMain/Platform.device.kt +actual fun createConnectivityState(): Connectivity { + return Connectivity { + autoStart = true + } +} + +// httpMain/Platform.http.kt +actual fun createConnectivityState(): Connectivity { + return Connectivity { + autoStart = true + urls("cloudflare.com", "my-own-domain.com") + port = 80 + pollingIntervalMs = 10.minutes + timeoutMs = 5.seconds + } +} +``` + +Then it can be used like so: + +```kotlin +@Composable +fun MyApp() { + val state = createConnectivityState() + + when (state.status) { + is Connectivity.Status.Connected -> Text("Connected to network") + is Connectivity.Status.Disconnected -> Text("Disconnected from network") + else -> {} + } +} +``` + ## Demo A demo app is available in the `demo` directory. It is a Compose Multiplatform app that runs on From 4768c9e1520b5cf326559c57abe5b76faeb5b884 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Mon, 10 Jun 2024 14:18:35 -0400 Subject: [PATCH 11/11] fix typo --- README.MD | 1 - 1 file changed, 1 deletion(-) diff --git a/README.MD b/README.MD index 8316c9c..f520f5c 100644 --- a/README.MD +++ b/README.MD @@ -82,7 +82,6 @@ dependencies { // For compose support implementation(libs.connectivity.compose.device) - } ```