chore: new connectivity dep. Closes #84.
This commit is contained in:
parent
ef13e300f0
commit
3bf60f1146
@ -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<Test> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
||||
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
||||
bind<ConnectivityService>() with singleton { ConnectivityService() }
|
||||
bind<Repository>() with
|
||||
singleton {
|
||||
Repository(
|
||||
instance(),
|
||||
instance(),
|
||||
isConnectionAvailable,
|
||||
instance(),
|
||||
instance(),
|
||||
)
|
||||
}
|
||||
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
||||
bind<AppViewModel>() 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<Boolean> = 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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<Boolean>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ReaderForSelfossDB>(relaxed = true)
|
||||
private val appSettingsService = mockk<AppSettingsService>()
|
||||
private val api = mockk<SelfossApi>()
|
||||
private val connectivityService = mockk<ConnectivityService>()
|
||||
private lateinit var repository: Repository
|
||||
|
||||
private fun initializeRepository(
|
||||
isConnectionAvailable: MutableStateFlow<Boolean> =
|
||||
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<SelfossModel.Tag>
|
||||
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<SelfossModel.Tag>
|
||||
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<SelfossModel.Tag>
|
||||
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<SelfossModel.Tag>
|
||||
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<SelfossModel.Source>
|
||||
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<SelfossModel.Source>
|
||||
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<SelfossModel.Source>
|
||||
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<SelfossModel.Source>
|
||||
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()
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Boolean>,
|
||||
private val connectivityService: ConnectivityService,
|
||||
private val db: ReaderForSelfossDB,
|
||||
) {
|
||||
var items = ArrayList<SelfossModel.Item>()
|
||||
var connectionMonitored = false
|
||||
|
||||
var baseUrl = appSettingsService.getBaseUrl()
|
||||
|
||||
@ -63,7 +63,7 @@ class Repository(
|
||||
|
||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = StatusAndData.error()
|
||||
if (isNetworkAvailable()) {
|
||||
if (connectivityService.isNetworkAvailable() == true) {
|
||||
fetchedItems =
|
||||
api.getItems(
|
||||
displayedItems.type,
|
||||
@ -102,7 +102,7 @@ class Repository(
|
||||
|
||||
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
|
||||
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = 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<SelfossModel.Item> {
|
||||
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<SelfossModel.Tag> {
|
||||
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<String, SelfossModel.Spout> =
|
||||
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<SelfossModel.Item>): 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<ACTION> = db.actionsQueries.actions().executeAsList()
|
||||
|
||||
private fun deleteDBAction(action: ACTION) = db.actionsQueries.deleteAction(action.id)
|
||||
|
@ -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<Boolean>()
|
||||
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()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user