diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index a1e59a4..4e65d85 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -12,28 +12,38 @@ plugins { id("app.cash.sqldelight") version "2.0.2" } -fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { - val result: String = ByteArrayOutputStream().use { outputStream -> - project.exec { - commandLine = cmd.split(" ") - standardOutput = outputStream - isIgnoreExitValue = ignore +fun Project.execWithOutput( + cmd: String, + ignore: Boolean = false, +): String { + val result: String = + ByteArrayOutputStream().use { outputStream -> + project.exec { + commandLine = cmd.split(" ") + standardOutput = outputStream + isIgnoreExitValue = ignore + } + outputStream.toString() } - outputStream.toString() - } return result } fun gitVersion(): String { val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true) - val process = if (maybeTagOfCurrentCommit.isEmpty()) { - println("No tag on current commit. Will take the latest one.") - execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1") - } else { - println("Tag found on current commit") - execWithOutput("git -C ../ describe --contains HEAD") - } - return process.replace("^0", "").replace("'", "").substring(1).replace("\\.", "").trim() + val process = + if (maybeTagOfCurrentCommit.isEmpty()) { + println("No tag on current commit. Will take the latest one.") + execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1") + } else { + println("Tag found on current commit") + execWithOutput("git -C ../ describe --contains HEAD") + } + return process + .replace("^0", "") + .replace("'", "") + .substring(1) + .replace("\\.", "") + .trim() } fun versionCodeFromGit(): Int { @@ -116,7 +126,6 @@ android { isIncludeAndroidResources = true } } - } dependencies { @@ -141,7 +150,7 @@ dependencies { implementation("androidx.constraintlayout:constraintlayout:2.2.0") implementation("org.jsoup:jsoup:1.18.3") - //multidex + // multidex implementation("androidx.multidex:multidex:2.0.1") // About @@ -162,31 +171,28 @@ dependencies { implementation("me.relex:circleindicator:2.1.6") implementation("androidx.viewpager2:viewpager2:1.1.0") - //Dependency Injection + // Dependency Injection implementation("org.kodein.di:kodein-di:7.23.1") implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1") implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1") - //Settings + // Settings implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0") - //Logging + // Logging implementation("io.github.aakira:napier:2.7.1") - //PhotoView + // PhotoView implementation("com.github.chrisbanes:PhotoView:2.3.0") implementation("androidx.core:core-ktx:1.15.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") - // Network information - implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") - // SQLDELIGHT implementation("app.cash.sqldelight:android-driver:2.0.2") - //test + // test testImplementation("junit:junit:4.13.2") testImplementation("io.mockk:mockk:1.13.14") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") @@ -210,11 +216,12 @@ tasks.withType { useJUnit() testLogging { exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - events = setOf( - org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, - org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED, - org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR - ) + events = + setOf( + org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, + org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED, + org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR, + ) showStandardStreams = true } } @@ -227,4 +234,4 @@ aboutLibraries { strictMode = com.mikepenz.aboutlibraries.plugin.StrictMode.FAIL duplicationMode = com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE duplicationRule = com.mikepenz.aboutlibraries.plugin.DuplicateRule.GROUP -} \ 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 bbd4651..ee3b85c 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 @@ -10,18 +10,16 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDexApplication import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper -import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel import bou.amine.apps.readerforselfossv2.dao.DriverFactory import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB import bou.amine.apps.readerforselfossv2.di.networkModule import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.service.AppSettingsService -import com.github.ln_12.library.ConnectivityStatus +import bou.amine.apps.readerforselfossv2.service.ConnectivityService import io.github.aakira.napier.DebugAntilog import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import org.acra.ACRA import org.acra.ReportField @@ -44,27 +42,21 @@ class MyApp : import(networkModule) bind() with singleton { DriverFactory(applicationContext) } bind() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) } + bind() with singleton { ConnectivityService() } bind() with singleton { Repository( instance(), instance(), - isConnectionAvailable, + instance(), instance(), ) } - bind() with singleton { ConnectivityStatus(applicationContext) } - bind() with singleton { AppViewModel(repository = instance()) } } private val repository: Repository by instance() - private val viewModel: AppViewModel by instance() - private val connectivityStatus: ConnectivityStatus by instance() private val driverFactory: DriverFactory by instance() - - @Suppress("detekt:ForbiddenComment") - // TODO: handle with the "previous" way - private val isConnectionAvailable: MutableStateFlow = MutableStateFlow(true) + private val connectivityService: ConnectivityService by instance() override fun onCreate() { super.onCreate() @@ -77,13 +69,12 @@ class MyApp : ProcessLifecycleOwner.get().lifecycle.addObserver( AppLifeCycleObserver( - connectivityStatus, - repository, + connectivityService, ), ) CoroutineScope(Dispatchers.Main).launch { - viewModel.networkAvailableProvider.collect { networkAvailable -> + connectivityService.networkAvailableProvider.collect { networkAvailable -> val toastMessage = if (networkAvailable) { repository.handleDBActions() @@ -189,18 +180,15 @@ class MyApp : } class AppLifeCycleObserver( - val connectivityStatus: ConnectivityStatus, - val repository: Repository, + val connectivityService: ConnectivityService, ) : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { super.onResume(owner) - repository.connectionMonitored = true - connectivityStatus.start() + connectivityService.start() } override fun onPause(owner: LifecycleOwner) { - repository.connectionMonitored = false - connectivityStatus.stop() + connectivityService.stop() super.onPause(owner) } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt index 893dd34..cce872a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt @@ -42,6 +42,7 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.rest.MercuryApi import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import bou.amine.apps.readerforselfossv2.service.ConnectivityService import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getThumbnail @@ -88,6 +89,7 @@ class ArticleFragment : override val di: DI by closestDI() private val repository: Repository by instance() private val appSettingsService: AppSettingsService by instance() + private val connectivityService: ConnectivityService by instance() private var typeface: Typeface? = null private var resId: Int = 0 @@ -168,7 +170,7 @@ class ArticleFragment : private fun handleContent() { if (contentText.isEmptyOrNullOrNullString()) { - if (repository.isNetworkAvailable() && url.isUrlValid()) { + if (connectivityService.isNetworkAvailable() == true && url.isUrlValid()) { getContentFromMercury(url!!) } } else { 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 deleted file mode 100644 index 2b73e01..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.viewmodel - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import bou.amine.apps.readerforselfossv2.repository.Repository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -class AppViewModel( - private val repository: Repository, -) : ViewModel() { - private val _networkAvailableProvider = MutableSharedFlow() - val networkAvailableProvider = _networkAvailableProvider.asSharedFlow() - private var wasConnected = true - - init { - viewModelScope.launch { - repository.isConnectionAvailable.collect { isConnected -> - if (repository.connectionMonitored) { - if (isConnected && !wasConnected && repository.connectionMonitored) { - _networkAvailableProvider.emit(true) - wasConnected = true - } else if (!isConnected && wasConnected && repository.connectionMonitored) { - _networkAvailableProvider.emit(false) - wasConnected = false - } - } - } - } - } -} diff --git a/androidApp/src/test/kotlin/bou/amine/apps/readerforselfossv2/tests/repository/RepositoryTest.kt b/androidApp/src/test/kotlin/bou/amine/apps/readerforselfossv2/tests/repository/RepositoryTest.kt index 3eebe02..29d68c7 100644 --- a/androidApp/src/test/kotlin/bou/amine/apps/readerforselfossv2/tests/repository/RepositoryTest.kt +++ b/androidApp/src/test/kotlin/bou/amine/apps/readerforselfossv2/tests/repository/RepositoryTest.kt @@ -11,6 +11,7 @@ import bou.amine.apps.readerforselfossv2.model.SuccessResponse import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.rest.SelfossApi import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import bou.amine.apps.readerforselfossv2.service.ConnectivityService import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.toView import io.mockk.clearAllMocks @@ -24,7 +25,6 @@ import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotSame import junit.framework.TestCase.assertSame import junit.framework.TestCase.assertTrue -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.runBlocking import org.junit.Assert.assertNotEquals import org.junit.Before @@ -52,15 +52,12 @@ class RepositoryTest { private val db = mockk(relaxed = true) private val appSettingsService = mockk() private val api = mockk() + private val connectivityService = mockk() private lateinit var repository: Repository - private fun initializeRepository( - isConnectionAvailable: MutableStateFlow = - MutableStateFlow( - true, - ), - ) { - repository = Repository(api, appSettingsService, isConnectionAvailable, db) + private fun initializeRepository(isNetworkAvailable: Boolean? = true) { + every { connectivityService.isNetworkAvailable() } returns isNetworkAvailable + repository = Repository(api, appSettingsService, connectivityService, db) runBlocking { repository.updateApiInformation() @@ -110,7 +107,7 @@ class RepositoryTest { fun instantiate_repository_without_api_version() { every { appSettingsService.getApiVersion() } returns -1 - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) coVerify(exactly = 0) { api.apiInformation() } coVerify(exactly = 0) { api.stats() } @@ -287,7 +284,7 @@ class RepositoryTest { fun get_newer_items_without_connectivity() { every { appSettingsService.isItemCachingEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) runBlocking { repository.getNewerItems() } @@ -314,7 +311,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) repository.setTagFilter(SelfossModel.Tag("Test", "red", 3)) runBlocking { repository.getNewerItems() @@ -342,7 +339,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) repository.setSourceFilter( SelfossModel.SourceDetail( 1, @@ -457,7 +454,7 @@ class RepositoryTest { var success: Boolean - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) runBlocking { success = repository.reloadBadges() } @@ -477,7 +474,7 @@ class RepositoryTest { var success: Boolean - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) runBlocking { success = repository.reloadBadges() } @@ -572,7 +569,7 @@ class RepositoryTest { every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testTags: List runBlocking { testTags = repository.getTags() @@ -590,7 +587,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testTags: List runBlocking { testTags = repository.getTags() @@ -607,7 +604,7 @@ class RepositoryTest { every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testTags: List runBlocking { testTags = repository.getTags() @@ -625,7 +622,7 @@ class RepositoryTest { every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testTags: List runBlocking { testTags = repository.getTags() @@ -775,7 +772,7 @@ class RepositoryTest { @Test fun get_sources_without_connection() { val (_, sourcesDB) = prepareSources() - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testSources: List runBlocking { testSources = repository.getSourcesDetails() @@ -792,7 +789,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns true - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testSources: List runBlocking { testSources = repository.getSourcesDetails() @@ -809,7 +806,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns false - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testSources: List runBlocking { testSources = repository.getSourcesDetails() @@ -826,7 +823,7 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var testSources: List runBlocking { testSources = repository.getSourcesDetails() @@ -898,7 +895,7 @@ class RepositoryTest { coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns SuccessResponse(true) - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var response: Boolean runBlocking { response = @@ -955,7 +952,7 @@ class RepositoryTest { fun delete_source_without_connection() { coEvery { api.deleteSource(any()) } returns SuccessResponse(false) - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var response: Boolean runBlocking { response = repository.deleteSource(5, "src") @@ -1028,7 +1025,7 @@ class RepositoryTest { data = "undocumented...", ) - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var response: Boolean runBlocking { response = repository.updateRemote() @@ -1070,7 +1067,7 @@ class RepositoryTest { fun login_but_without_connection() { coEvery { api.login() } returns SuccessResponse(success = true) - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) var response: Boolean runBlocking { response = repository.login() @@ -1150,7 +1147,7 @@ class RepositoryTest { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns StatusAndData(success = false, data = generateTestApiItem()) - initializeRepository(MutableStateFlow(false)) + initializeRepository(false) prepareSearch() runBlocking { repository.tryToCacheItemsAndGetNewOnes() diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 3e20988..995496e 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -4,7 +4,6 @@ object SqlDelight { const val runtime = "app.cash.sqldelight:runtime:2.0.2" const val android = "app.cash.sqldelight:android-driver:2.0.2" const val native = "app.cash.sqldelight:native-driver:2.0.2" - } plugins { @@ -41,13 +40,13 @@ kotlin { implementation("org.jsoup:jsoup:1.15.4") - //Dependency Injection + // Dependency Injection implementation("org.kodein.di:kodein-di:7.14.0") - //Settings + // Settings implementation("com.russhwolf:multiplatform-settings-no-arg:1.0.0-RC") - //Logging + // Logging implementation("io.github.aakira:napier:2.6.1") // Sql @@ -55,6 +54,10 @@ kotlin { // Sql implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") + + // Connectivity + implementation("dev.jordond.connectivity:connectivity-core:1.2.0") + implementation("dev.jordond.connectivity:connectivity-device:1.2.0") } } val commonTest by getting { @@ -114,4 +117,4 @@ sqldelight { packageName.set("bou.amine.apps.readerforselfossv2.dao") } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/Repository.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/Repository.kt index b842903..ce717ff 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/Repository.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/Repository.kt @@ -13,6 +13,7 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.StatusAndData import bou.amine.apps.readerforselfossv2.rest.SelfossApi import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import bou.amine.apps.readerforselfossv2.service.ConnectivityService import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.toEntity @@ -30,11 +31,10 @@ private const val MAX_ITEMS_NUMBER = 200 class Repository( private val api: SelfossApi, private val appSettingsService: AppSettingsService, - val isConnectionAvailable: MutableStateFlow, + private val connectivityService: ConnectivityService, private val db: ReaderForSelfossDB, ) { var items = ArrayList() - var connectionMonitored = false var baseUrl = appSettingsService.getBaseUrl() @@ -63,7 +63,7 @@ class Repository( suspend fun getNewerItems(): ArrayList { var fetchedItems: StatusAndData> = StatusAndData.error() - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { fetchedItems = api.getItems( displayedItems.type, @@ -102,7 +102,7 @@ class Repository( suspend fun getOlderItems(): ArrayList { var fetchedItems: StatusAndData> = StatusAndData.error() - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { val offset = items.size fetchedItems = api.getItems( @@ -122,7 +122,7 @@ class Repository( } private suspend fun getMaxItemsForBackground(itemType: ItemType): List { - return if (isNetworkAvailable()) { + return if (connectivityService.isNetworkAvailable() == true) { val items = api.getItems( itemType.type, @@ -146,7 +146,7 @@ class Repository( @Suppress("detekt:ForbiddenComment") suspend fun reloadBadges(): Boolean { var success = false - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { val response = api.stats() if (response.success && response.data != null) { _badgeUnread.value = response.data.unread ?: 0 @@ -168,7 +168,7 @@ class Repository( suspend fun getTags(): List { val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() - return if (isNetworkAvailable() && !fetchedTags) { + return if (connectivityService.isNetworkAvailable() == true && !fetchedTags) { val apiTags = api.tags() if (apiTags.success && apiTags.data != null && isDatabaseEnabled) { resetDBTagsWithData(apiTags.data) @@ -185,7 +185,7 @@ class Repository( } suspend fun getSpouts(): Map = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { val spouts = api.spouts() if (spouts.success && spouts.data != null) { spouts.data @@ -201,7 +201,7 @@ class Repository( val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() val shouldFetch = if (!appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true - if (shouldFetch && isNetworkAvailable()) { + if (shouldFetch && connectivityService.isNetworkAvailable() == true) { if (appSettingsService.getPublicAccess()) { val apiSources = api.sourcesStats() if (apiSources.success && apiSources.data != null) { @@ -223,7 +223,7 @@ class Repository( val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() val shouldFetch = if (!appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true - if (shouldFetch && isNetworkAvailable()) { + if (shouldFetch && connectivityService.isNetworkAvailable() == true) { val apiSources = api.sourcesDetailed() if (apiSources.success && apiSources.data != null) { fetchedSources = true @@ -248,7 +248,7 @@ class Repository( } private suspend fun markAsReadById(id: Int): Boolean = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { api.markAsRead(id.toString()).isSuccess } else { insertDBAction(id.toString(), read = true) @@ -265,7 +265,7 @@ class Repository( } private suspend fun unmarkAsReadById(id: Int): Boolean = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { api.unmarkAsRead(id.toString()).isSuccess } else { insertDBAction(id.toString(), unread = true) @@ -282,7 +282,7 @@ class Repository( } private suspend fun starrById(id: Int): Boolean = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { api.starr(id.toString()).isSuccess } else { insertDBAction(id.toString(), starred = true) @@ -299,7 +299,7 @@ class Repository( } private suspend fun unstarrById(id: Int): Boolean = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { api.unstarr(id.toString()).isSuccess } else { insertDBAction(id.toString(), starred = true) @@ -309,7 +309,10 @@ class Repository( suspend fun markAllAsRead(items: ArrayList): Boolean { var success = false - if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() }).isSuccess) { + if (connectivityService.isNetworkAvailable() != null && + connectivityService.isNetworkAvailable()!! && + api.markAllAsRead(items.map { it.id.toString() }).isSuccess + ) { success = true for (item in items) { markAsReadLocally(item) @@ -369,7 +372,7 @@ class Repository( tags: String, ): Boolean { var response = false - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { response = api .createSourceForVersion( title, @@ -390,7 +393,7 @@ class Repository( tags: String, ): Boolean { var response = false - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { response = api.updateSourceForVersion(id, title, url, spout, tags).isSuccess == true } @@ -402,13 +405,13 @@ class Repository( title: String, ): Boolean { var success = false - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { val response = api.deleteSource(id) success = response.isSuccess } // We filter on success or if the network isn't available - if (success || !isNetworkAvailable()) { + if (success || !(connectivityService.isNetworkAvailable() == true)) { items = ArrayList(items.filter { it.sourcetitle != title }) setReaderItems(items) db.itemsQueries.deleteItemsWhereSource(title) @@ -418,7 +421,7 @@ class Repository( } suspend fun updateRemote(): Boolean = - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { api.update().data.equals("finished") } else { false @@ -426,7 +429,7 @@ class Repository( suspend fun login(): Boolean { var result = false - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { try { val response = api.login() result = response.isSuccess == true @@ -439,7 +442,7 @@ class Repository( suspend fun checkIfFetchFails(): Boolean { var fetchFailed = true - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { try { // Trying to fetch one item, and check someone is trying to use the app with // a random rss feed, that would throw a NoTransformationFoundException @@ -453,7 +456,7 @@ class Repository( } suspend fun logout() { - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { try { val response = api.logout() if (!response.isSuccess) { @@ -481,7 +484,7 @@ class Repository( suspend fun updateApiInformation() { val apiMajorVersion = appSettingsService.getApiVersion() - if (isNetworkAvailable()) { + if (connectivityService.isNetworkAvailable() == true) { val fetchedInformation = api.apiInformation() if (fetchedInformation.success && fetchedInformation.data != null) { if (fetchedInformation.data.getApiMajorVersion() != apiMajorVersion) { @@ -500,8 +503,6 @@ class Repository( } } - fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride - private fun getDBActions(): List = db.actionsQueries.actions().executeAsList() private fun deleteDBAction(action: ACTION) = db.actionsQueries.deleteAction(action.id) diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ConnectivityService.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ConnectivityService.kt new file mode 100644 index 0000000..4f067bc --- /dev/null +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ConnectivityService.kt @@ -0,0 +1,42 @@ +package bou.amine.apps.readerforselfossv2.service + +import dev.jordond.connectivity.Connectivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch + +class ConnectivityService { + private val _networkAvailableProvider = MutableSharedFlow() + val networkAvailableProvider = _networkAvailableProvider.asSharedFlow() + private var currentStatus: Boolean? = null + private lateinit var connectivity: Connectivity + + fun start() { + connectivity = Connectivity() + connectivity.start() + CoroutineScope(Dispatchers.Main).launch { + connectivity.statusUpdates.collect { status -> + when (status) { + is Connectivity.Status.Connected -> { + currentStatus = true + _networkAvailableProvider.emit(true) + } + + is Connectivity.Status.Disconnected -> { + currentStatus = false + _networkAvailableProvider.emit(false) + } + } + } + } + } + + fun isNetworkAvailable(): Boolean? = currentStatus + + fun stop() { + currentStatus = null + connectivity.stop() + } +}