Refactored RxJava to coroutines in ConnectivityBroadcastReceiver.

* Simplified the `combineToLanguageList` method and removed warnings.
* Refactored `ZimManageViewModelTest` to align with the changes.
* Removed unused `RxJava` code from the project.
* Removed all `RxJava` dependencies from the project.
* Updated the `README` file to reflect that `RxJava` is no longer used.
* Updated `credit.html` accordingly, similar to the `README`.
This commit is contained in:
MohitMaliFtechiz 2025-05-27 17:48:28 +05:30
parent ad62661e67
commit 6cae44821e
12 changed files with 50 additions and 139 deletions

View File

@ -103,7 +103,6 @@ This variable should only be set when building an APK. If you set this variable
- 🔄 [Retrofit](https://square.github.io/retrofit/) - Turns REST API into a Java interface.
- 🌐 [OkHttp](https://github.com/square/okhttp) - HTTP client for Android and Java.
- 🎭 [Mockito](https://github.com/mockito/mockito) - Mocking framework for unit tests.
- ⚡ [RxJava](https://github.com/ReactiveX/RxJava) - Reactive Extensions for the JVM.
- 🗃️ [ObjectBox](https://github.com/objectbox/objectbox-java) - Reactive NoSQL Database.
- 🐒 [MockK](https://github.com/mockk/mockk) - Kotlin mocking library.
- 🧪 [JUnit5](https://github.com/junit-team/junit5/) - Next-generation JUnit.

View File

@ -91,5 +91,3 @@
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-keep class io.reactivex.** { *; }

View File

@ -87,11 +87,6 @@ Translatewiki community<br>
<br/>
Copyright 2013 Square, Inc.
</li>
<li>
<b>RxAndroid</b>
<br/>
Copyright 2015 RxAndroid Authors
</li>
<li>
<b>Fetch</b>
<br/>

View File

@ -54,7 +54,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.retry
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.asFlow
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
@ -302,7 +301,6 @@ class ZimManageViewModel @Inject constructor(
}
coroutineJobs.clear()
context.unregisterReceiver(connectivityBroadcastReceiver)
connectivityBroadcastReceiver.stopNetworkState()
appProgressListener = null
super.onCleared()
}
@ -393,8 +391,7 @@ class ZimManageViewModel @Inject constructor(
library: MutableSharedFlow<LibraryNetworkEntity>,
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = requestDownloadLibrary.flatMapConcat {
connectivityBroadcastReceiver.networkStates.asFlow()
.distinctUntilChanged()
connectivityBroadcastReceiver.networkStates
.filter { networkState -> networkState == CONNECTED }
.take(1)
.flatMapConcat {
@ -455,8 +452,6 @@ class ZimManageViewModel @Inject constructor(
}
private fun updateNetworkStates() = connectivityBroadcastReceiver.networkStates
.asFlow()
.catch { it.printStackTrace() }
.onEach { state -> networkStates.postValue(state) }
.launchIn(viewModelScope)
@ -531,21 +526,25 @@ class ZimManageViewModel @Inject constructor(
booksFromNetwork: List<Book>,
allLanguages: List<Language>
) = when {
booksFromNetwork.isEmpty() && allLanguages.isEmpty() -> defaultLanguage()
booksFromNetwork.isEmpty() && allLanguages.isNotEmpty() -> emptyList()
booksFromNetwork.isNotEmpty() && allLanguages.isEmpty() ->
booksFromNetwork.isEmpty() -> {
if (allLanguages.isEmpty()) {
defaultLanguage()
} else {
emptyList()
}
}
allLanguages.isEmpty() ->
fromLocalesWithNetworkMatchesSetActiveBy(
networkLanguageCounts(booksFromNetwork),
defaultLanguage()
)
booksFromNetwork.isNotEmpty() && allLanguages.isNotEmpty() ->
else ->
fromLocalesWithNetworkMatchesSetActiveBy(
networkLanguageCounts(booksFromNetwork),
allLanguages
)
else -> throw RuntimeException("Impossible state")
}
private fun networkLanguageCounts(booksFromNetwork: List<Book>) =

View File

@ -33,7 +33,6 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.processors.PublishProcessor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@ -44,6 +43,7 @@ import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.assertj.core.api.Assertions.assertThat
@ -125,7 +125,7 @@ class ZimManageViewModelTest {
private val languages = MutableStateFlow<List<Language>>(emptyList())
private val fileSystemStates =
MutableStateFlow<FileSystemState>(FileSystemState.DetectingFileSystem)
private val networkStates: PublishProcessor<NetworkState> = PublishProcessor.create()
private val networkStates = MutableStateFlow(NetworkState.NOT_CONNECTED)
private val booksOnDiskListItems = MutableStateFlow<List<BooksOnDiskListItem>>(emptyList())
private val testDispatcher = StandardTestDispatcher()
@ -170,6 +170,7 @@ class ZimManageViewModelTest {
languages.value = emptyList()
fileSystemStates.value = FileSystemState.DetectingFileSystem
booksOnDiskListItems.value = emptyList()
networkStates.value = NOT_CONNECTED
viewModel =
ZimManageViewModel(
downloadRoomDao,
@ -386,18 +387,17 @@ class ZimManageViewModelTest {
coEvery { kiwixService.getLibrary() } returns libraryNetworkEntity(networkBooks)
every { defaultLanguageProvider.provide() } returns defaultLanguage
viewModel.networkLibrary.emit(libraryNetworkEntity(networkBooks))
advanceUntilIdle()
runCurrent()
languages.value = dbBooks
advanceUntilIdle()
networkStates.onNext(CONNECTED)
runCurrent()
networkStates.value = CONNECTED
advanceUntilIdle()
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `network states observed`() = runTest {
networkStates.offer(NOT_CONNECTED)
networkStates.tryEmit(NOT_CONNECTED)
advanceUntilIdle()
viewModel.networkStates.test()
.assertValue(NOT_CONNECTED)
@ -414,15 +414,20 @@ class ZimManageViewModelTest {
triggerAction = {
every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns ""
networkStates.onNext(CONNECTED)
downloads.value = listOf(downloadModel(book = bookDownloading))
books.value = listOf(bookOnDisk(book = bookAlreadyOnDisk))
languages.value =
networkStates.tryEmit(CONNECTED)
advanceUntilIdle()
downloads.tryEmit(listOf(downloadModel(book = bookDownloading)))
advanceUntilIdle()
books.tryEmit(listOf(bookOnDisk(book = bookAlreadyOnDisk)))
advanceUntilIdle()
languages.tryEmit(
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage"),
language(isActive = false, occurencesOfLanguage = 1, languageCode = "inactiveLanguage")
)
fileSystemStates.value = CanWrite4GbFile
)
fileSystemStates.tryEmit(CanWrite4GbFile)
advanceUntilIdle()
viewModel.networkLibrary.emit(
libraryNetworkEntity(
listOf(
@ -450,7 +455,6 @@ class ZimManageViewModelTest {
)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `library marks files over 4GB as can't download if file system state says to`() = runTest {
val bookOver4Gb =
@ -464,14 +468,15 @@ class ZimManageViewModelTest {
testFlow(
viewModel.libraryItems,
triggerAction = {
networkStates.onNext(CONNECTED)
downloads.value = listOf()
books.value = listOf()
languages.value =
networkStates.tryEmit(CONNECTED)
downloads.tryEmit(listOf())
books.tryEmit(listOf())
languages.tryEmit(
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage")
)
fileSystemStates.value = CannotWrite4GbFile
)
fileSystemStates.tryEmit(CannotWrite4GbFile)
viewModel.networkLibrary.emit(libraryNetworkEntity(listOf(bookOver4Gb)))
},
assert = {

View File

@ -20,9 +20,6 @@ object Libs {
"org.jetbrains.kotlinx:kotlinx-coroutines-android:" +
Versions.org_jetbrains_kotlinx_kotlinx_coroutines
const val kotlinx_coroutines_rx3: String =
"org.jetbrains.kotlinx:kotlinx-coroutines-rx3:" + Versions.kotlinx_coroutines_rx3
/**
* https://github.com/Kotlin/kotlinx.coroutines
*/
@ -66,12 +63,6 @@ object Libs {
const val tracing: String = "androidx.tracing:tracing:" + Versions.tracing
/**
* https://github.com/square/retrofit
*/
const val adapter_rxjava2: String = "com.squareup.retrofit2:adapter-rxjava2:" +
Versions.com_squareup_retrofit2
/**
* https://github.com/square/retrofit
*/
@ -189,11 +180,6 @@ object Libs {
*/
const val objectbox_kotlin: String = "io.objectbox:objectbox-kotlin:" + Versions.io_objectbox
/**
* https://objectbox.io
*/
const val objectbox_rxjava: String = "io.objectbox:objectbox-rxjava:" + Versions.io_objectbox
/**
* http://mockk.io
*/
@ -301,11 +287,6 @@ object Libs {
*/
const val appcompat: String = "androidx.appcompat:appcompat:" + Versions.appcompat
/**
* https://github.com/ReactiveX/RxAndroid
*/
const val rxandroid: String = "io.reactivex.rxjava2:rxandroid:" + Versions.rxandroid
/**
* https://developer.android.com/jetpack/androidx
*/
@ -327,11 +308,6 @@ object Libs {
const val barista: String = "com.adevinta.android:barista:" + Versions.barista
/**
* https://github.com/ReactiveX/RxJava
*/
const val rxjava: String = "io.reactivex.rxjava2:rxjava:" + Versions.rxjava
/**
* https://developer.android.com/jetpack/androidx
*/
@ -348,8 +324,6 @@ object Libs {
const val roomRuntime = "androidx.room:room-runtime:" + Versions.roomVersion
const val roomRxjava2 = "androidx.room:room-rxjava2:" + Versions.roomVersion
/**
* https://github.com/zxing/zxing
*/
@ -387,9 +361,6 @@ object Libs {
const val COMPOSE_UI_MANIFEST = "androidx.compose.ui:ui-test-manifest:${Versions.COMPOSE_VERSION}"
const val COMPOSE_RX_JAVA2 =
"androidx.compose.runtime:runtime-rxjava2:${Versions.COMPOSE_VERSION}"
const val COMPOSE_LIVE_DATA =
"androidx.compose.runtime:runtime-livedata:${Versions.COMPOSE_VERSION}"
}

View File

@ -16,8 +16,6 @@ object Versions {
const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.10.1"
const val kotlinx_coroutines_rx3: String = "1.10.1"
const val androidx_test_espresso: String = "3.6.1"
const val tracing: String = "1.2.0"
@ -86,8 +84,6 @@ object Versions {
const val appcompat: String = "1.7.0"
const val rxandroid: String = "2.1.1"
const val core_ktx: String = "1.15.0"
const val androidx_activity: String = "1.9.3"
@ -100,8 +96,6 @@ object Versions {
const val barista: String = "4.3.0"
const val rxjava: String = "2.2.21"
const val webkit: String = "1.12.1"
const val junit: String = "1.1.5"

View File

@ -214,7 +214,6 @@ class AllProjectConfigurer {
androidTestImplementation(Libs.navigation_testing)
implementation(Libs.logging_interceptor)
implementation(Libs.retrofit)
implementation(Libs.adapter_rxjava2)
testImplementation(Libs.junit_jupiter)
testImplementation(Libs.mockk)
testImplementation(Libs.assertj_core)
@ -228,13 +227,10 @@ class AllProjectConfigurer {
implementation(Libs.core_ktx)
implementation(Libs.fragment_ktx)
implementation(Libs.collection_ktx)
implementation(Libs.rxandroid)
implementation(Libs.rxjava)
implementation(Libs.preference_ktx)
implementation(Libs.roomKtx)
annotationProcessor(Libs.roomCompiler)
implementation(Libs.roomRuntime)
implementation(Libs.roomRxjava2)
kapt(Libs.roomCompiler)
implementation(Libs.tracing)
implementation(Libs.fetch)
@ -246,15 +242,12 @@ class AllProjectConfigurer {
implementation(Libs.COMPOSE_MATERIAL3)
implementation(Libs.ANDROIDX_ACTIVITY_COMPOSE)
implementation(Libs.COMPOSE_TOOLING_PREVIEW)
implementation(Libs.COMPOSE_RX_JAVA2)
implementation(Libs.COMPOSE_LIVE_DATA)
// Compose UI test implementation
androidTestImplementation(Libs.COMPOSE_UI_TEST_JUNIT)
debugImplementation(Libs.COMPOSE_UI_MANIFEST)
debugImplementation(Libs.COMPOSE_TOOLING)
implementation(Libs.kotlinx_coroutines_rx3)
}
}
}

View File

@ -58,7 +58,6 @@ dependencies {
implementation(Libs.android_arch_lifecycle_extensions)
implementation(Libs.objectbox_kotlin)
implementation(Libs.objectbox_rxjava)
implementation(Libs.webkit)
testImplementation(Libs.kotlinx_coroutines_test)
implementation(Libs.kotlinx_coroutines_android)

View File

@ -21,30 +21,27 @@ package org.kiwix.kiwixmobile.core.zim_manager
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import io.reactivex.Flowable
import io.reactivex.processors.BehaviorProcessor
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.kiwix.kiwixmobile.core.base.BaseBroadcastReceiver
import org.kiwix.kiwixmobile.core.networkState
import javax.inject.Inject
class ConnectivityBroadcastReceiver @Inject constructor(
private val connectivityManager: ConnectivityManager
) :
BaseBroadcastReceiver() {
@Suppress("DEPRECATION")
override val action: String = ConnectivityManager.CONNECTIVITY_ACTION
) : BaseBroadcastReceiver() {
@Suppress("DEPRECATION")
override val action: String = ConnectivityManager.CONNECTIVITY_ACTION
private val _networkStates = BehaviorProcessor.createDefault(connectivityManager.networkState)
val networkStates: Flowable<NetworkState> = _networkStates
override fun onIntentWithActionReceived(
context: Context,
intent: Intent
) {
_networkStates.onNext(connectivityManager.networkState)
}
fun stopNetworkState() {
_networkStates.onComplete()
}
private val _networkStates = MutableStateFlow(NetworkState.NOT_CONNECTED).apply {
tryEmit(connectivityManager.networkState)
}
val networkStates: StateFlow<NetworkState> = _networkStates
override fun onIntentWithActionReceived(
context: Context,
intent: Intent
) {
_networkStates.tryEmit(connectivityManager.networkState)
}
}

View File

@ -1,34 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.sharedFunctions
import io.reactivex.Scheduler
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.plugins.RxJavaPlugins
fun setScheduler(replacementScheduler: Scheduler) {
RxJavaPlugins.setIoSchedulerHandler { scheduler -> replacementScheduler }
RxJavaPlugins.setComputationSchedulerHandler { scheduler -> replacementScheduler }
RxJavaPlugins.setNewThreadSchedulerHandler { scheduler -> replacementScheduler }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> replacementScheduler }
}
fun resetSchedulers() {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}

View File

@ -88,11 +88,6 @@
<br/>
Copyright 2013 Square, Inc.
</li>
<li>
<b>RxAndroid</b>
<br/>
Copyright 2015 RxAndroid Authors
</li>
<li>
<b>Fetch</b>
<br/>