Compare commits

..

No commits in common. "afcc55e907786fb424b561c08935e087ce7c306c" and "1392e2a571550e136419bad00a2f8dce225cb075" have entirely different histories.

36 changed files with 346 additions and 445 deletions

View File

@ -10,7 +10,7 @@ Please read the guidelines before contributing, and follow them (or try to) when
There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users. There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
You can fork the repository, and [help me solve some issues](https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues) You can fork the repository, and [help me solve some issues](https://github.com/aminecmi/ReaderforSelfoss/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://github.com/aminecmi/ReaderforSelfoss/issues)
### What I can't help you with. ### What I can't help you with.
@ -28,7 +28,7 @@ Always check if the web version of your instance is working.
### Pull requests ### Pull requests
* Don't create a PR for translations. * Don't create a PR for translations. See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654) for an explanation why.
* Please ask before starting to work on an issue. I may be working on it, or someone else could be doing so. * Please ask before starting to work on an issue. I may be working on it, or someone else could be doing so.
* Each pull request should implement **ONE** feature or bugfix. Keep in mind that you can submit as many PR as you want. * Each pull request should implement **ONE** feature or bugfix. Keep in mind that you can submit as many PR as you want.
* Your code must be simple and clear enough to avoid using comments to explain what it does. * Your code must be simple and clear enough to avoid using comments to explain what it does.

View File

@ -5,7 +5,7 @@
- [ ] I have updated the documentation accordingly. - [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes. - [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed. - [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related. - [ ] This is **NOT** translation related. (See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654))
This closes issue #XXX This closes issue #XXX

2
.gitignore vendored
View File

@ -319,5 +319,3 @@ fabric.properties
# End of https://www.toptal.com/developers/gitignore/api/gradle,kotlin,androidstudio,android,xcode,swift # End of https://www.toptal.com/developers/gitignore/api/gradle,kotlin,androidstudio,android,xcode,swift
crowdin.properties

View File

@ -22,15 +22,15 @@ If you are a user, you can still create new issues. I'll fix them when I can.
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/). 1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
2. Check the [Contribution guide](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md). 2. Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss-multiplatform/blob/master/.github/CONTRIBUTING.md).
3. Build the project by following [these steps](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide) 3. Build the project by following [these steps](https://github.com/aminecmi/ReaderforSelfoss-multiplatform/blob/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
## Useful links ## Useful links
- [Check what changed](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/CHANGELOG.md) - [Check what changed](https://github.com/aminecmi/ReaderforSelfoss-multiplatform/blob/master/CHANGELOG.md)
- [See what I'm doing](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/projects/1) - [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss-multiplatform/projects/1)
- [Create an issue, or request a new feature](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/issues) - [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss-multiplatform/issues)
- [Help translation the app](https://crowdin.com/project/readerforselfoss) - [Help translation the app](https://crowdin.com/project/readerforselfoss)
## Contributors (V1) (Alphabetical order) ❤️ ## Contributors (V1) (Alphabetical order) ❤️

View File

@ -181,31 +181,22 @@ dependencies {
implementation("androidx.viewpager2:viewpager2:1.1.0-beta01") implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
//Dependency Injection //Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0") implementation("org.kodein.di:kodein-di:7.12.0")
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0") implementation("org.kodein.di:kodein-di-framework-android-x:7.12.0")
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0")
//Settings //Settings
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9") implementation("com.russhwolf:multiplatform-settings-no-arg:0.9")
//Logging
implementation("io.github.aakira:napier:2.6.1")
//PhotoView //PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0") implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1") implementation("androidx.lifecycle:lifecycle-common-java8:2.4.0")
implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
implementation("androidx.room:room-ktx:2.4.0-beta01") implementation("androidx.room:room-ktx:2.4.0-beta01")
kapt("androidx.room:room-compiler:2.4.0-beta01") kapt("androidx.room:room-compiler:2.4.0-beta01")
implementation("android.arch.work:work-runtime-ktx:1.0.1") implementation("android.arch.work:work-runtime-ktx:1.0.1")
// Network information
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
} }

View File

@ -44,9 +44,9 @@ import bou.amine.apps.readerforselfossv2.android.utils.Config
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.android.utils.persistence.toEntity import bou.amine.apps.readerforselfossv2.android.utils.persistence.toEntity
import bou.amine.apps.readerforselfossv2.android.utils.persistence.toView import bou.amine.apps.readerforselfossv2.android.utils.persistence.toView
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.ItemType
@ -126,6 +126,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private var recyclerAdapter: RecyclerView.Adapter<*>? = null private var recyclerAdapter: RecyclerView.Adapter<*>? = null
private var fromTabShortcut: Boolean = false private var fromTabShortcut: Boolean = false
private var offlineShortcut: Boolean = false
private lateinit var tagsBadge: Map<Long, Int> private lateinit var tagsBadge: Map<Long, Int>
@ -152,7 +153,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
val view = binding.root val view = binding.root
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1 fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
repository.offlineOverride = intent.getBooleanExtra("startOffline", false) offlineShortcut = intent.getBooleanExtra("startOffline", false)
if (fromTabShortcut) { if (fromTabShortcut) {
elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position)) elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
@ -174,6 +175,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
AppDatabase::class.java, "selfoss-database" AppDatabase::class.java, "selfoss-database"
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build() ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
customTabActivityHelper = CustomTabActivityHelper() customTabActivityHelper = CustomTabActivityHelper()
dataBase = AndroidDeviceDatabase(applicationContext) dataBase = AndroidDeviceDatabase(applicationContext)
@ -195,7 +197,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
R.color.refresh_progress_3 R.color.refresh_progress_3
) )
binding.swipeRefreshLayout.setOnRefreshListener { binding.swipeRefreshLayout.setOnRefreshListener {
repository.offlineOverride = false offlineShortcut = false
lastFetchDone = false lastFetchDone = false
handleDrawerItems() handleDrawerItems()
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
@ -687,6 +689,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
var sources: List<SelfossModel.Source>? var sources: List<SelfossModel.Source>?
fun sourcesApiCall() { fun sourcesApiCall() {
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut) && updateSources) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val response = repository.getSources() val response = repository.getSources()
if (response != null) { if (response != null) {
@ -703,12 +706,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
} }
}
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut) && updateSources) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
tags = repository.getTags() tags = repository.getTags()
sourcesApiCall() sourcesApiCall()
} }
} }
}
binding.mainDrawer.itemAdapter.add( binding.mainDrawer.itemAdapter.add(
PrimaryDrawerItem().apply { PrimaryDrawerItem().apply {
@ -938,11 +944,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private fun reloadBadges() { private fun reloadBadges() {
if (displayUnreadCount || displayAllCount) { if (displayUnreadCount || displayAllCount) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
if (applicationContext.isNetworkAvailable()) {
repository.reloadBadges() repository.reloadBadges()
reloadBadgeContent() reloadBadgeContent()
} }
} }
} }
}
private fun reloadBadgeContent() { private fun reloadBadgeContent() {
if (displayUnreadCount) { if (displayUnreadCount) {
@ -1013,13 +1021,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.refresh -> { R.id.refresh -> {
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) { needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
// TODO: Use Dispatchers.IO // TODO: Use Dispatchers.IO
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val updatedRemote = repository.updateRemote() val updatedRemote = repository.updateRemote()
if (updatedRemote) { if (updatedRemote) {
// TODO: Send toast messages from the repository
Toast.makeText( Toast.makeText(
this@HomeActivity, this@HomeActivity,
R.string.refresh_success_response, Toast.LENGTH_LONG R.string.refresh_success_response, Toast.LENGTH_LONG
@ -1035,12 +1043,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
return true return true
} else {
return false
}
} }
R.id.readAll -> { R.id.readAll -> {
if (elementsShown == ItemType.UNREAD) { if (elementsShown == ItemType.UNREAD) {
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) { needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
binding.swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val success = repository.markAllAsRead(items) val success = repository.markAllAsRead(items)
if (success) { if (success) {
@ -1066,6 +1078,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
} }
}
return true return true
} }
R.id.action_disconnect -> { R.id.action_disconnect -> {
@ -1114,6 +1127,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val actions = db.actionsDao().actions() val actions = db.actionsDao().actions()
@ -1128,4 +1142,5 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
} }
}

View File

@ -15,6 +15,7 @@ import androidx.appcompat.app.AppCompatActivity
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
@ -201,6 +202,7 @@ class LoginActivity() : AppCompatActivity(), DIAware {
repository.refreshLoginInformation(url, login, password, httpLogin, httpPassword, isWithSelfSignedCert) repository.refreshLoginInformation(url, login, password, httpLogin, httpPassword, isWithSelfSignedCert)
if (this@LoginActivity.isNetworkAvailable(this@LoginActivity.findViewById(R.id.loginForm))) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val result = repository.login() val result = repository.login()
if (result) { if (result) {
@ -211,6 +213,7 @@ class LoginActivity() : AppCompatActivity(), DIAware {
} }
} }
} }
}
showProgress(false) showProgress(false)
} }
} }

View File

@ -7,50 +7,32 @@ import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import bou.amine.apps.readerforselfossv2.DI.networkModule import bou.amine.apps.readerforselfossv2.DI.networkModule
import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.Config
import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import com.github.ln_12.library.ConnectivityStatus
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import io.github.aakira.napier.DebugAntilog
import io.github.aakira.napier.Napier
import org.kodein.di.* import org.kodein.di.*
class MyApp : MultiDexApplication(), DIAware { class MyApp : MultiDexApplication(), DIAware {
override val di by DI.lazy { override val di by DI.lazy {
import(networkModule) import(networkModule)
bind<Repository>() with singleton { Repository(instance(), instance(), connectivityStatus) } bind<Repository>() with singleton { Repository(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 lateinit var config: Config private lateinit var config: Config
private lateinit var settings : Settings private lateinit var settings : Settings
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Napier.base(DebugAntilog())
config = Config() config = Config()
settings = Settings() settings = Settings()
@ -61,18 +43,6 @@ class MyApp : MultiDexApplication(), DIAware {
tryToHandleBug() tryToHandleBug()
handleNotificationChannels() handleNotificationChannels()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifeCycleObserver(connectivityStatus, repository))
CoroutineScope(Dispatchers.Main).launch {
viewModel.toastMessageProvider.collect { toastMessage ->
Toast.makeText(
applicationContext,
toastMessage,
Toast.LENGTH_SHORT
).show()
}
}
} }
private fun handleNotificationChannels() { private fun handleNotificationChannels() {
@ -132,19 +102,4 @@ class MyApp : MultiDexApplication(), DIAware {
} }
} }
} }
class AppLifeCycleObserver(val connectivityStatus: ConnectivityStatus, val repository: Repository) : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
repository.connectionMonitored = true
connectivityStatus.start()
}
override fun onPause(owner: LifecycleOwner) {
repository.connectionMonitored = false
connectivityStatus.stop()
super.onPause(owner)
}
}
} }

View File

@ -10,6 +10,7 @@ import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
@ -63,6 +64,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = mLayoutManager binding.recyclerView.layoutManager = mLayoutManager
if (this@SourcesActivity.isNetworkAvailable(binding.recyclerView)) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val response = repository.getSources() val response = repository.getSources()
if (response != null) { if (response != null) {
@ -87,6 +89,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
).show() ).show()
} }
} }
}
binding.fab.setOnClickListener { binding.fab.setOnClickListener {
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java)) startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))

View File

@ -16,6 +16,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.*
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
@ -109,6 +110,7 @@ class ItemCardAdapter(
binding.favButton.setOnClickListener { binding.favButton.setOnClickListener {
val item = items[bindingAdapterPosition] val item = items[bindingAdapterPosition]
if (c.isNetworkAvailable()) {
if (item.starred) { if (item.starred) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(item) repository.unstarr(item)
@ -125,6 +127,7 @@ class ItemCardAdapter(
binding.favButton.isSelected = true binding.favButton.isSelected = true
} }
} }
}
binding.shareBtn.setOnClickListener { binding.shareBtn.setOnClickListener {
val item = items[bindingAdapterPosition] val item = items[bindingAdapterPosition]

View File

@ -14,6 +14,7 @@ import bou.amine.apps.readerforselfossv2.android.model.getIcon
import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded
import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.Config
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.android.utils.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.utils.toTextDrawableString
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
@ -77,6 +78,7 @@ class SourcesListAdapter(
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn) val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
deleteBtn.setOnClickListener { deleteBtn.setOnClickListener {
if (c.isNetworkAvailable(null)) {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val successfullyDeletedSource = repository.deleteSource(id) val successfullyDeletedSource = repository.deleteSource(id)
@ -96,3 +98,4 @@ class SourcesListAdapter(
} }
} }
} }
}

View File

@ -21,7 +21,7 @@ import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATIO
import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3
import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4
import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.Config
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.ItemType
@ -44,7 +44,9 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
override fun doWork(): Result { override fun doWork(): Result {
val settings = Settings() val settings = Settings()
val periodicRefresh = settings.getBoolean("periodic_refresh", false) val periodicRefresh = settings.getBoolean("periodic_refresh", false)
if (periodicRefresh && isNetworkAccessible(context)) { if (periodicRefresh) {
if (context.isNetworkAvailable()) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val notificationManager = val notificationManager =
@ -92,6 +94,7 @@ override fun doWork(): Result {
} }
} }
if (context.isNetworkAvailable()) {
launch { launch {
try { try {
val newItems = repository.allItems(ItemType.UNREAD) val newItems = repository.allItems(ItemType.UNREAD)
@ -103,6 +106,8 @@ override fun doWork(): Result {
} }
} }
} }
}
}
return Result.success() return Result.success()
} }

View File

@ -35,6 +35,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.*
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.SelfossModel import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
@ -275,7 +276,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) { private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
if (repository.isNetworkAvailable()) { if ((context != null && requireContext().isNetworkAvailable(null)) || context == null) {
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
val parser = MercuryApi() val parser = MercuryApi()
@ -316,10 +317,7 @@ class ArticleFragment : Fragment(), DIAware {
Glide Glide
.with(requireContext()) .with(requireContext())
.asBitmap() .asBitmap()
.loadMaybeBasicAuth( .loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
config,
response.body()!!.lead_image_url.orEmpty()
)
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(binding.imageView) .into(binding.imageView)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -28,13 +28,13 @@ class Config {
companion object { companion object {
const val settingsName = "paramsselfoss" const val settingsName = "paramsselfoss"
const val feedbackEmail = "aminecmi@pm.me.com" const val feedbackEmail = "aminecmi@gmail.com"
const val translationUrl = "https://crwd.in/readerforselfoss" const val translationUrl = "https://crwd.in/readerforselfoss"
const val sourceUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform" const val sourceUrl = "https://github.com/aminecmi/ReaderforSelfoss"
const val trackerUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues" const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
const val syncChannelId = "sync-channel-id" const val syncChannelId = "sync-channel-id"

View File

@ -1,14 +1,52 @@
package bou.amine.apps.readerforselfossv2.android.utils.network package bou.amine.apps.readerforselfossv2.android.utils.network
import android.content.Context import android.content.Context
import android.graphics.Color
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Build import android.os.Build
import android.view.View
import android.widget.TextView
import bou.amine.apps.readerforselfossv2.android.R
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
var snackBarShown = false
var view: View? = null
lateinit var s: Snackbar lateinit var s: Snackbar
fun isNetworkAccessible(context: Context): Boolean { fun Context.isNetworkAvailable(
v: View? = null,
overrideOffline: Boolean = false
): Boolean {
val networkIsAccessible = isNetworkAccessible(this)
if (v != null && (!networkIsAccessible || overrideOffline) && (!snackBarShown || v != view)) {
view = v
s = Snackbar
.make(
v,
R.string.no_network_connectivity,
Snackbar.LENGTH_INDEFINITE
)
s.setAction(android.R.string.ok) {
snackBarShown = false
s.dismiss()
}
val view = s.view
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
tv.setTextColor(Color.WHITE)
s.show()
snackBarShown = true
}
if (snackBarShown && networkIsAccessible && !overrideOffline) {
s.dismiss()
}
return if(overrideOffline) overrideOffline else networkIsAccessible
}
private fun isNetworkAccessible(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View File

@ -1,29 +0,0 @@
package bou.amine.apps.readerforselfossv2.android.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import bou.amine.apps.readerforselfossv2.android.R
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 _toastMessageProvider = MutableSharedFlow<Int>()
val toastMessageProvider = _toastMessageProvider.asSharedFlow()
private var wasConnected = true
init {
viewModelScope.launch {
repository.isConnectionAvailable.collect { isConnected ->
if (isConnected && !wasConnected && repository.connectionMonitored) {
_toastMessageProvider.emit(R.string.network_connectivity_retrieved)
wasConnected = true
} else if (!isConnected && wasConnected && repository.connectionMonitored){
_toastMessageProvider.emit(R.string.network_connectivity_lost)
wasConnected = false
}
}
}
}
}

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Sense connexió!</string> <string name="no_network_connectivity">Sense connexió!</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string> <string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string> <string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
<string name="pref_switch_periodic_refresh_on">Els articles se sincronitzaran periòdicament</string> <string name="pref_switch_periodic_refresh_on">Els articles se sincronitzaran periòdicament</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Nicht verbunden !</string> <string name="no_network_connectivity">Nicht verbunden !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string> <string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string> <string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
<string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string> <string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Sin conexión!</string> <string name="no_network_connectivity">Sin conexión!</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string> <string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string> <string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
<string name="pref_switch_periodic_refresh_on">Los artículos se sincronizarán periódicamente</string> <string name="pref_switch_periodic_refresh_on">Los artículos se sincronizarán periódicamente</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Vérifier les nouvelles sources et tags</string> <string name="pref_switch_update_sources">Vérifier les nouvelles sources et tags</string>
<string name="pref_switch_update_sources_summary">Désactivez cette option si votre serveur reçoit trop de requêtes.</string> <string name="pref_switch_update_sources_summary">Désactivez cette option si votre serveur reçoit trop de requêtes.</string>
<string name="no_network_connectivity">Hors connexion !</string> <string name="no_network_connectivity">Hors connexion !</string>
<string name="network_connectivity_lost">"Connexion au réseau perdue"</string>
<string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string>
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string> <string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string> <string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string>
<string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisées</string> <string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisées</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Comproba novas fontes e etiquetas</string> <string name="pref_switch_update_sources">Comproba novas fontes e etiquetas</string>
<string name="pref_switch_update_sources_summary">Deshabilita isto se o teu servidor está recibindo demasiadas peticións de base de datos.</string> <string name="pref_switch_update_sources_summary">Deshabilita isto se o teu servidor está recibindo demasiadas peticións de base de datos.</string>
<string name="no_network_connectivity">Non conectado!</string> <string name="no_network_connectivity">Non conectado!</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronizar artigos</string> <string name="pref_switch_periodic_refresh">Sincronizar artigos</string>
<string name="pref_switch_periodic_refresh_off">Os artigos non se sincronizarán coa aplicación de fondo</string> <string name="pref_switch_periodic_refresh_off">Os artigos non se sincronizarán coa aplicación de fondo</string>
<string name="pref_switch_periodic_refresh_on">Os artigos sincronizaranse periódicamente</string> <string name="pref_switch_periodic_refresh_on">Os artigos sincronizaranse periódicamente</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">检查新来源和标签</string> <string name="pref_switch_update_sources">检查新来源和标签</string>
<string name="pref_switch_update_sources_summary">如果你的服务器接收过多的数据库查询,请禁用此功能。</string> <string name="pref_switch_update_sources_summary">如果你的服务器接收过多的数据库查询,请禁用此功能。</string>
<string name="no_network_connectivity">未连接!</string> <string name="no_network_connectivity">未连接!</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">同步文章</string> <string name="pref_switch_periodic_refresh">同步文章</string>
<string name="pref_switch_periodic_refresh_off">文章将不会在后台同步</string> <string name="pref_switch_periodic_refresh_off">文章将不会在后台同步</string>
<string name="pref_switch_periodic_refresh_on">将定期同步文章</string> <string name="pref_switch_periodic_refresh_on">将定期同步文章</string>

View File

@ -142,8 +142,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -143,8 +143,6 @@
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="no_network_connectivity">Not connected !</string> <string name="no_network_connectivity">Not connected !</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>

View File

@ -68,9 +68,9 @@ redirect_from: "/ReaderforSelfoss-multiplatform/"
<div id="links"> <div id="links">
<a class="github-button" href="https://gitea.amine-louveau.fr/Louvorg/readerforselfoss-multiplatform" data-size="large" aria-label="Star aminecmi/readerforselfoss-multiplatform on GitHub">Star</a> <a class="github-button" href="https://github.com/aminecmi/readerforselfoss-multiplatform" data-size="large" aria-label="Star aminecmi/readerforselfoss-multiplatform on GitHub">Star</a>
</div> </div>
<meta itemprop="url" content="https://gitea.amine-louveau.fr/Louvorg/readerforselfoss-multiplatform"> <meta itemprop="url" content="https://github.com/aminecmi/readerforselfoss-multiplatform">
<meta itemprop="applicationCategory" content="News & Magazines"> <meta itemprop="applicationCategory" content="News & Magazines">
</div> </div>
</body> </body>

View File

@ -36,9 +36,6 @@ kotlin {
//Logging //Logging
implementation("io.github.aakira:napier:2.6.1") implementation("io.github.aakira:napier:2.6.1")
// Network information
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
} }
} }
val commonTest by getting { val commonTest by getting {

View File

@ -5,19 +5,16 @@ import bou.amine.apps.readerforselfossv2.rest.SelfossModel
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
import bou.amine.apps.readerforselfossv2.utils.DateUtils import bou.amine.apps.readerforselfossv2.utils.DateUtils
import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.ItemType
import com.github.ln_12.library.ConnectivityStatus
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import io.github.aakira.napier.Napier import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, val connectivityStatus: ConnectivityStatus) { class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService) {
val settings = Settings() val settings = Settings()
var items = ArrayList<SelfossModel.Item>() var items = ArrayList<SelfossModel.Item>()
val isConnectionAvailable = connectivityStatus.isNetworkConnected
var connectionMonitored = false
var baseUrl = apiDetails.getBaseUrl() var baseUrl = apiDetails.getBaseUrl()
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -29,7 +26,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
var searchFilter: String? = null var searchFilter: String? = null
var itemsCaching = settings.getBoolean("items_caching", false) var itemsCaching = settings.getBoolean("items_caching", false)
var offlineOverride = false
var apiMajorVersion = 0 var apiMajorVersion = 0
var badgeUnread = 0 var badgeUnread = 0
@ -49,21 +45,14 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
} }
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> { suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
// TODO: Use the updatedSince parameter // TODO: Check connectivity, use the updatedSince parameter
var fetchedItems: List<SelfossModel.Item>? = null val fetchedItems = api.getItems(displayedItems.type,
if (isNetworkAvailable()) {
fetchedItems = api.getItems(
displayedItems.type,
settings.getString("prefer_api_items_number", "200").toInt(), settings.getString("prefer_api_items_number", "200").toInt(),
offset = 0, offset = 0,
tagFilter?.tag, tagFilter?.tag,
sourceFilter?.id?.toLong(), sourceFilter?.id?.toLong(),
searchFilter, searchFilter,
null null)
)
} else {
// TODO: Get items from the database
}
if (fetchedItems != null) { if (fetchedItems != null) {
items = ArrayList(fetchedItems) items = ArrayList(fetchedItems)
@ -72,21 +61,15 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
} }
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> { suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
var fetchedItems: List<SelfossModel.Item>? = null // TODO: Check connectivity
if (isNetworkAvailable()) {
val offset = items.size val offset = items.size
fetchedItems = api.getItems( val fetchedItems = api.getItems(displayedItems.type,
displayedItems.type,
settings.getString("prefer_api_items_number", "200").toInt(), settings.getString("prefer_api_items_number", "200").toInt(),
offset, offset,
tagFilter?.tag, tagFilter?.tag,
sourceFilter?.id?.toLong(), sourceFilter?.id?.toLong(),
searchFilter, searchFilter,
null null)
)
} else {
// TODO: Get items from the database
}
if (fetchedItems != null) { if (fetchedItems != null) {
appendItems(fetchedItems) appendItems(fetchedItems)
@ -94,22 +77,8 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return items return items
} }
suspend fun allItems(itemType: ItemType): List<SelfossModel.Item>? { suspend fun allItems(itemType: ItemType): List<SelfossModel.Item>? =
return if (isNetworkAvailable()) { api.getItems(itemType.type, 200, 0, tagFilter?.tag, sourceFilter?.id?.toLong(), searchFilter, null)
api.getItems(
itemType.type,
200,
0,
tagFilter?.tag,
sourceFilter?.id?.toLong(),
searchFilter,
null
)
} else {
// TODO: Provide an error message
null
}
}
private fun appendItems(fetchedItems: List<SelfossModel.Item>) { private fun appendItems(fetchedItems: List<SelfossModel.Item>) {
// TODO: Store in DB if enabled by user // TODO: Store in DB if enabled by user
@ -125,8 +94,8 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
} }
suspend fun reloadBadges(): Boolean { suspend fun reloadBadges(): Boolean {
// TODO: Check connectivity, calculate from DB
var success = false var success = false
if (isNetworkAvailable()) {
val response = api.stats() val response = api.stats()
if (response != null) { if (response != null) {
badgeUnread = response.unread badgeUnread = response.unread
@ -134,43 +103,26 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
badgeStarred = response.starred badgeStarred = response.starred
success = true success = true
} }
} else {
// TODO: Compute badges from database
}
return success return success
} }
suspend fun getTags(): List<SelfossModel.Tag>? { suspend fun getTags(): List<SelfossModel.Tag>? {
// TODO: Store in DB // TODO: Check success, store in DB
return if (isNetworkAvailable()) { return api.tags()
api.tags()
} else {
// TODO: Compute from database
null
}
} }
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? { suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
// TODO: Store in DB // TODO: Check success, store in DB
return if (isNetworkAvailable()) { return api.spouts()
api.spouts()
} else {
// TODO: Compute from database
null
}
} }
suspend fun getSources(): ArrayList<SelfossModel.Source>? { suspend fun getSources(): ArrayList<SelfossModel.Source>? {
// TODO: Store in DB // TODO: Check success
return if (isNetworkAvailable()) { return api.sources()
api.sources()
} else {
// TODO: Compute from database
null
}
} }
suspend fun markAsRead(item: SelfossModel.Item): Boolean { suspend fun markAsRead(item: SelfossModel.Item): Boolean {
// TODO: Check internet connection
val success = markAsReadById(item.id) val success = markAsReadById(item.id)
if (success) { if (success) {
@ -179,9 +131,10 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun markAsReadById(id: Int): Boolean = suspend fun markAsReadById(id: Int): Boolean {
isNetworkAvailable() && api.markAsRead(id.toString())?.isSuccess == true // TODO: Check internet connection
return api.markAsRead(id.toString())?.isSuccess == true
}
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean { suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
val success = unmarkAsReadById(item.id) val success = unmarkAsReadById(item.id)
@ -192,8 +145,10 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun unmarkAsReadById(id: Int): Boolean = suspend fun unmarkAsReadById(id: Int): Boolean {
isNetworkAvailable() && api.unmarkAsRead(id.toString())?.isSuccess == true // TODO: Check internet connection
return api.unmarkAsRead(id.toString())?.isSuccess == true
}
suspend fun starr(item: SelfossModel.Item): Boolean { suspend fun starr(item: SelfossModel.Item): Boolean {
val success = starrById(item.id) val success = starrById(item.id)
@ -204,8 +159,10 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun starrById(id: Int): Boolean = suspend fun starrById(id: Int): Boolean {
isNetworkAvailable() && api.starr(id.toString())?.isSuccess == true // TODO: Check success, store in DB
return api.starr(id.toString())?.isSuccess == true
}
suspend fun unstarr(item: SelfossModel.Item): Boolean { suspend fun unstarr(item: SelfossModel.Item): Boolean {
val success = unstarrById(item.id) val success = unstarrById(item.id)
@ -216,15 +173,17 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun unstarrById(id: Int): Boolean = suspend fun unstarrById(id: Int): Boolean {
isNetworkAvailable() && api.unstarr(id.toString())?.isSuccess == true // TODO: Check internet connection
return api.unstarr(id.toString())?.isSuccess == true
}
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean { suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
var success = false // TODO: Check Internet connectivity, store in DB
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) { val success = api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true
success = true
if (success) {
for (item in items) { for (item in items) {
markAsReadLocally(item) markAsReadLocally(item)
} }
@ -271,47 +230,46 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
tags: String, tags: String,
filter: String filter: String
): Boolean { ): Boolean {
var response = false // TODO: Check connectivity
if (isNetworkAvailable()) { val response = api.createSourceForVersion(
response = api.createSourceForVersion(
title, title,
url, url,
spout, spout,
tags, tags,
filter, filter,
apiMajorVersion apiMajorVersion
)?.isSuccess == true )
}
return response return response != null
} }
suspend fun deleteSource(id: Int): Boolean { suspend fun deleteSource(id: Int): Boolean {
// TODO: Store in DB // TODO: Check connectivity, store in DB
var success = false var success = false
if (isNetworkAvailable()) {
val response = api.deleteSource(id) val response = api.deleteSource(id)
if (response != null) { if (response != null) {
success = response.isSuccess success = response.isSuccess
} }
}
return success return success
} }
suspend fun updateRemote(): Boolean = suspend fun updateRemote(): Boolean {
isNetworkAvailable() && api.update()?.isSuccess == true // TODO: Handle connectivity issues
val response = api.update()
return response?.isSuccess ?: false
}
suspend fun login(): Boolean { suspend fun login(): Boolean {
var result = false var result = false
if (isNetworkAvailable()) {
try { try {
val response = api.login() val response = api.login()
result = response?.isSuccess == true if (response != null && response.isSuccess) {
result = true
}
} catch (cause: Throwable) { } catch (cause: Throwable) {
Napier.e(cause.stackTraceToString(),tag = "RepositoryImpl.updateRemote") Napier.e(cause.stackTraceToString(),tag = "RepositoryImpl.updateRemote")
} }
}
return result return result
} }
@ -329,18 +287,15 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
} }
private suspend fun updateApiVersion() { private suspend fun updateApiVersion() {
apiMajorVersion = settings.getInt("apiVersionMajor", 0) // TODO: Handle connectivity issues
if (isNetworkAvailable()) {
val fetchedVersion = api.version() val fetchedVersion = api.version()
if (fetchedVersion != null) { if (fetchedVersion != null) {
apiMajorVersion = fetchedVersion.getApiMajorVersion() apiMajorVersion = fetchedVersion.getApiMajorVersion()
settings.putInt("apiVersionMajor", apiMajorVersion) settings.putInt("apiVersionMajor", apiMajorVersion)
} else {
apiMajorVersion = settings.getInt("apiVersionMajor", 0)
} }
} }
}
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride
// TODO: Handle offline actions // TODO: Handle offline actions
} }