diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 9e42a73..6d80981 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -208,5 +208,7 @@ dependencies { // Network information // TODO: When updating this library, check if the todo in RepositoryImpl.startNetwork can be resolved - implementation("com.github.ln-12:multiplatform-connectivity-status:1.1.0") + // TODO: Include this library once this is merged https://github.com/ln-12/multiplatform-connectivity-status/pull/4 + // implementation("com.github.ln-12:multiplatform-connectivity-status:1.1.0") + implementation(project(":connectionstatus")) } \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt index 22192f4..2e95d26 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt @@ -20,7 +20,7 @@ import bou.amine.apps.readerforselfossv2.repository.Repository import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import com.ftinc.scoop.Scoop -import com.github.`ln-12`.library.ConnectivityStatus +import com.github.ln12.library.ConnectivityStatus import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.russhwolf.settings.Settings @@ -33,7 +33,7 @@ class MyApp : MultiDexApplication(), DIAware { override val di by DI.lazy { import(networkModule) bind() with singleton { Repository(instance(), instance(), ConnectivityStatus(applicationContext)) } - bindProvider { AppViewModel(repository = instance()) } + bind() with singleton { AppViewModel(repository = instance()) } } private val repository: Repository by instance() diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt index 2bd2d2e..cc7c21d 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt @@ -10,14 +10,17 @@ import kotlinx.coroutines.launch class AppViewModel(private val repository: Repository) : ViewModel() { private val _toastMessageProvider = MutableSharedFlow() val toastMessageProvider = _toastMessageProvider.asSharedFlow() + private var wasConnected = true init { viewModelScope.launch { repository.isConnectionAvailable.collect { isConnected -> - if (isConnected && repository.connectionMonitored) { + if (isConnected && !wasConnected && repository.connectionMonitored) { _toastMessageProvider.emit("Network connection is now available") - } else if (repository.connectionMonitored){ + wasConnected = true + } else if (!isConnected && wasConnected && repository.connectionMonitored){ _toastMessageProvider.emit("Network connection lost") + wasConnected = false } } } diff --git a/connectionstatus/.gitignore b/connectionstatus/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/connectionstatus/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/connectionstatus/build.gradle.kts b/connectionstatus/build.gradle.kts new file mode 100644 index 0000000..045698e --- /dev/null +++ b/connectionstatus/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.library") + kotlin("multiplatform") +} + +group = "com.github.ln-12" +version = "1.1.0" + +repositories { + google() + mavenCentral() +} + +kotlin { + android { + compilations.all { + kotlinOptions.jvmTarget = "1.8" + } + + publishLibraryVariants("release", "debug") + } + ios() + + sourceSets { + val commonMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + + val androidMain by getting + val androidTest by getting { + dependencies { + implementation(kotlin("test-junit")) + implementation("junit:junit:4.13.2") + } + } + + val iosMain by getting + val iosTest by getting + } +} + +android { + compileSdk = 31 + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + defaultConfig { + minSdk = 21 + targetSdk = 31 + } +} + +// metadata is currently not supported for iOS +// https://youtrack.jetbrains.com/issue/KT-44459#focus=Comments-27-4645829.0-0 +kotlin.metadata { + compilations.matching { it.name == "iosMain" }.all { + compileKotlinTaskProvider.configure { enabled = false } + } +} \ No newline at end of file diff --git a/connectionstatus/src/androidMain/AndroidManifest.xml b/connectionstatus/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..f3a3bcb --- /dev/null +++ b/connectionstatus/src/androidMain/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/connectionstatus/src/androidMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt b/connectionstatus/src/androidMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt new file mode 100644 index 0000000..027abed --- /dev/null +++ b/connectionstatus/src/androidMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt @@ -0,0 +1,85 @@ +package com.github.ln12.library + +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 android.util.Log +import kotlinx.coroutines.flow.MutableStateFlow + +// From library com.github.ln-12:multiplatform-connectivity-status:1.1.0 +// https://github.com/ln-12/multiplatform-connectivity-status +// Copyright 2021 Lorenzo Neumann +// Edited by davidoskky as here: https://github.com/ln-12/multiplatform-connectivity-status/pull/4 +actual class ConnectivityStatus(private val context: Context) { + actual val isNetworkConnected = MutableStateFlow(false) + + private var connectivityManager: ConnectivityManager? = null + private val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + Log.d("Connectivity status", "Connected") + isNetworkConnected.value = true + } + + override fun onLost(network: Network) { + Log.d("Connectivity status", "Disconnected") + isNetworkConnected.value = false + } + } + + actual fun start() { + try { + if (connectivityManager == null) { + connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // API 24 and above + connectivityManager!!.registerDefaultNetworkCallback(networkCallback) + + val currentNetwork = connectivityManager!!.activeNetwork + + if(currentNetwork == null) { + isNetworkConnected.value = false + + Log.d("Connectivity status", "Disconnected") + } + } else { + // API 23 and below + val networkRequest = NetworkRequest.Builder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } + }.build() + + connectivityManager!!.registerNetworkCallback(networkRequest, networkCallback) + + val currentNetwork = connectivityManager!!.activeNetworkInfo + + if(currentNetwork == null || ( + currentNetwork.type != ConnectivityManager.TYPE_ETHERNET && + currentNetwork.type != ConnectivityManager.TYPE_WIFI && + currentNetwork.type != ConnectivityManager.TYPE_MOBILE + )) { + isNetworkConnected.value = false + + Log.d("Connectivity status", "Disconnected") + } + } + + Log.d("Connectivity status", "Started") + } catch (e: Exception) { + Log.d("Connectivity status", "Failed to start: ${e.message.toString()}") + e.printStackTrace() + isNetworkConnected.value = false + } + } + + actual fun stop() { + connectivityManager?.unregisterNetworkCallback(networkCallback) + Log.d("Connectivity status", "Stopped") + } +} \ No newline at end of file diff --git a/connectionstatus/src/commonMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt b/connectionstatus/src/commonMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt new file mode 100644 index 0000000..3b25c82 --- /dev/null +++ b/connectionstatus/src/commonMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt @@ -0,0 +1,12 @@ +package com.github.ln12.library + +import kotlinx.coroutines.flow.MutableStateFlow + +// From library com.github.ln-12:multiplatform-connectivity-status:1.1.0 +// https://github.com/ln-12/multiplatform-connectivity-status +// Copyright 2021 Lorenzo Neumann +expect class ConnectivityStatus { + val isNetworkConnected: MutableStateFlow + fun start() + fun stop() +} \ No newline at end of file diff --git a/connectionstatus/src/iosMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt b/connectionstatus/src/iosMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt new file mode 100644 index 0000000..e3c8970 --- /dev/null +++ b/connectionstatus/src/iosMain/kotlin/com/github/ln12/library/ConnectivityStatus.kt @@ -0,0 +1,67 @@ +package com.github.ln12.library + +import kotlinx.coroutines.flow.MutableStateFlow +import cocoapods.Reachability.* +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import platform.Foundation.NSLog +import platform.darwin.dispatch_async +import platform.darwin.dispatch_get_main_queue +import kotlin.native.concurrent.freeze + + +// From library com.github.ln-12:multiplatform-connectivity-status:1.1.0 +// https://github.com/ln-12/multiplatform-connectivity-status +// Copyright 2021 Lorenzo Neumann +actual class ConnectivityStatus { + actual val isNetworkConnected = MutableStateFlow(false) + + private var reachability: Reachability? = null + + // Swift can't directly use a MutableStateFlow, so the status + // is exposed via a lambda/closure + fun getStatus(success: (Boolean) -> Unit) { + MainScope().launch { + isNetworkConnected.collect { status -> + success(status) + } + } + } + + actual fun start() { + dispatch_async(dispatch_get_main_queue()) { + reachability = Reachability.reachabilityForInternetConnection() + + val reachableCallback = { reach: Reachability? -> + dispatch_async(dispatch_get_main_queue(), { + NSLog("Connected") + + isNetworkConnected.value = true + }.freeze()) + }.freeze() + reachability?.reachableBlock = reachableCallback + + val unreachableCallback = { reach: Reachability? -> + dispatch_async(dispatch_get_main_queue(), { + NSLog("Disconnected") + + isNetworkConnected.value = false + }.freeze()) + }.freeze() + reachability?.unreachableBlock = unreachableCallback + + reachability?.startNotifier() + + dispatch_async(dispatch_get_main_queue(), { + isNetworkConnected.value = reachability?.isReachable() ?: false + + NSLog("Initial reachability: ${reachability?.isReachable()}") + }.freeze()) + } + } + + actual fun stop() { + reachability?.stopNotifier() + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 0b32ec1..9c56c94 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,4 +8,5 @@ pluginManagement { rootProject.name = "ReaderForSelfossV2" include(":androidApp") -include(":shared") \ No newline at end of file +include(":shared") +include(":connectionstatus") \ No newline at end of file diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 9a5992d..ca82dbd 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -38,7 +38,9 @@ kotlin { implementation("io.github.aakira:napier:2.6.1") // Network information - implementation("com.github.ln-12:multiplatform-connectivity-status:1.1.0") + // TODO: Include this library once this is merged https://github.com/ln-12/multiplatform-connectivity-status/pull/4 + //implementation("com.github.ln-12:multiplatform-connectivity-status:1.1.0") + implementation(project(":connectionstatus")) } } val commonTest by getting { diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index 06639c9..d3876bf 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -5,7 +5,7 @@ import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.utils.DateUtils import bou.amine.apps.readerforselfossv2.utils.ItemType -import com.github.`ln-12`.library.ConnectivityStatus +import com.github.ln12.library.ConnectivityStatus import com.russhwolf.settings.Settings import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineScope @@ -348,8 +348,8 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails // com.github.ln-12:multiplatform-connectivity-status:1.1.0 // https://github.com/ln-12/multiplatform-connectivity-status/issues/2 fun startNetwork() { - connectivityStatus.start() connectionMonitored = true + connectivityStatus.start() } fun stopNetwork() {