From edbd2c7713a0ba4e7e7a3ba6d59d16861ea4eb23 Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Sun, 17 Sep 2023 21:56:55 -0600 Subject: - [shared] Implement network state monitoring - [android] UI reacts to network state - [ios] UI reacts to network state --- shared/src/androidMain/AndroidManifest.xml | 4 +- .../TrackerMap/controllers/NetworkController.kt | 55 ++++++++++++++++++++++ .../TrackerMap/client/infrastructure/ApiClient.kt | 2 +- .../client/infrastructure/SessionManager.kt | 2 + .../TrackerMap/controllers/NetworkController.kt | 8 ++++ .../TrackerMap/controllers/SessionController.kt | 18 ++----- .../TrackerMap/controllers/NetworkController.kt | 25 ++++++++++ 7 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt create mode 100644 shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt create mode 100644 shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt (limited to 'shared') diff --git a/shared/src/androidMain/AndroidManifest.xml b/shared/src/androidMain/AndroidManifest.xml index 568741e..f0f34af 100644 --- a/shared/src/androidMain/AndroidManifest.xml +++ b/shared/src/androidMain/AndroidManifest.xml @@ -1,2 +1,4 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt b/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt new file mode 100644 index 0000000..eecd7de --- /dev/null +++ b/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt @@ -0,0 +1,55 @@ +package mx.trackermap.TrackerMap.controllers + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.os.Build +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import mx.trackermap.TrackerMap.Injectable + +actual class NetworkController(context: Context): Injectable { + private val networkRequest = NetworkRequest.Builder().build() + private val connectivityManager: ConnectivityManager + private val _networkAvailable = MutableStateFlow(null) + actual val networkAvailable = _networkAvailable.asStateFlow() + + private val networkCallback = object: ConnectivityManager.NetworkCallback() { + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities + ) { + super.onCapabilitiesChanged(network, networkCapabilities) + _networkAvailable.value = checkNetworkAccess(networkCapabilities) + } + + override fun onLost(network: Network) { + super.onLost(network) + _networkAvailable.value = false + } + } + + init { + connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + _networkAvailable.value = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + checkNetworkAccess(connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)) + } else { + connectivityManager.activeNetworkInfo?.isConnected == true + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + connectivityManager.registerDefaultNetworkCallback(networkCallback) + } else { + connectivityManager.registerNetworkCallback(networkRequest, networkCallback) + } + } + + private fun checkNetworkAccess(capabilities: NetworkCapabilities?) = + capabilities != null + && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } else true +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt index 937b2dd..8238f7e 100644 --- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt @@ -168,7 +168,7 @@ open class ApiClient( } } - if (sessionManager.token.isNotEmpty()) { + if (sessionManager.hasSession) { request.headers["Cookie"] = sessionManager.token } val response: HttpResponse = client.request(request) diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/SessionManager.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/SessionManager.kt index caf2da1..71ae5d0 100644 --- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/SessionManager.kt +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/SessionManager.kt @@ -28,6 +28,8 @@ class SessionManager( settings[ACCESS_TOKEN_KEY] = token } + val hasSession: Boolean get() = settings.hasKey(ACCESS_TOKEN_KEY) + fun clearSession() { settings.remove(ACCESS_TOKEN_KEY) settings.remove(SERVER_URL_KEY) diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt new file mode 100644 index 0000000..08dcc87 --- /dev/null +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt @@ -0,0 +1,8 @@ +package mx.trackermap.TrackerMap.controllers + +import kotlinx.coroutines.flow.StateFlow +import mx.trackermap.TrackerMap.Injectable + +expect class NetworkController: Injectable { + val networkAvailable: StateFlow +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt index a63bba2..5cfdf96 100644 --- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt @@ -25,13 +25,15 @@ import kotlinx.serialization.json.JsonPrimitive import mx.trackermap.TrackerMap.Injectable import mx.trackermap.TrackerMap.client.apis.SessionApi import mx.trackermap.TrackerMap.client.apis.UsersApi +import mx.trackermap.TrackerMap.client.infrastructure.SessionManager import mx.trackermap.TrackerMap.client.models.SessionBody import mx.trackermap.TrackerMap.client.models.User @DelicateCoroutinesApi class SessionController( + private val sessionManager: SessionManager, private val sessionApi: SessionApi, - private val usersApi: UsersApi + private val usersApi: UsersApi, ): Injectable { sealed class LoginState { object Nothing: LoginState() @@ -46,6 +48,7 @@ class SessionController( val loginStateFlow = MutableStateFlow(null) val userFlow = MutableStateFlow(null) + val hasSession: Boolean get() = sessionManager.hasSession fun getSession() { loginStateFlow.value = LoginState.Loading @@ -59,19 +62,6 @@ class SessionController( } } - fun restoreSession() { - loginStateFlow.value = LoginState.Loading - GlobalScope.launch { - try { - userFlow.value = sessionApi.sessionGet() - loginStateFlow.value = LoginState.Success - } catch (e: Exception) { - e.printStackTrace() - loginStateFlow.value = LoginState.Nothing - } - } - } - fun login(body: SessionBody) { val url = body.url.trim() val email = body.email.trim() diff --git a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt new file mode 100644 index 0000000..d112c11 --- /dev/null +++ b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/controllers/NetworkController.kt @@ -0,0 +1,25 @@ +package mx.trackermap.TrackerMap.controllers + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import mx.trackermap.TrackerMap.Injectable +import platform.Network.* +import platform.darwin.* + +actual class NetworkController: Injectable { + private val monitor = nw_path_monitor_create() + private val queue: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND.toLong(), 0u) + private val _networkAvailable = MutableStateFlow(null) + actual val networkAvailable = _networkAvailable.asStateFlow() + + private val updateHandler: nw_path_monitor_update_handler_t = { path: nw_path_t -> + val status = nw_path_get_status(path) + _networkAvailable.value = status in arrayOf(nw_path_status_satisfied, nw_path_status_satisfiable) + } + + init { + nw_path_monitor_set_update_handler(monitor, updateHandler) + nw_path_monitor_set_queue(monitor, queue) + nw_path_monitor_start(monitor) + } +} \ No newline at end of file -- cgit v1.2.3