network #28
@ -181,8 +181,9 @@ dependencies {
|
||||
implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
|
||||
|
||||
//Dependency Injection
|
||||
implementation("org.kodein.di:kodein-di:7.12.0")
|
||||
implementation("org.kodein.di:kodein-di-framework-android-x:7.12.0")
|
||||
implementation("org.kodein.di:kodein-di:7.14.0")
|
||||
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0")
|
||||
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0")
|
||||
|
||||
//Settings
|
||||
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9")
|
||||
@ -193,13 +194,18 @@ dependencies {
|
||||
//PhotoView
|
||||
implementation("com.github.chrisbanes:PhotoView:2.3.0")
|
||||
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
implementation("androidx.core:core-ktx:1.8.0")
|
||||
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
|
||||
implementation("androidx.lifecycle:lifecycle-common-java8:2.4.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
|
||||
implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1")
|
||||
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")
|
||||
kapt("androidx.room:room-compiler:2.4.0-beta01")
|
||||
|
||||
implementation("android.arch.work:work-runtime-ktx:1.0.1")
|
||||
|
||||
// Network information
|
||||
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
|
||||
}
|
@ -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.removeBadge
|
||||
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.toView
|
||||
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||
@ -126,7 +126,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
||||
|
||||
private var fromTabShortcut: Boolean = false
|
||||
private var offlineShortcut: Boolean = false
|
||||
|
||||
private lateinit var tagsBadge: Map<Long, Int>
|
||||
|
||||
@ -153,7 +152,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
val view = binding.root
|
||||
|
||||
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
||||
offlineShortcut = intent.getBooleanExtra("startOffline", false)
|
||||
repository.offlineOverride = intent.getBooleanExtra("startOffline", false)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
|
||||
@ -175,7 +174,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
||||
|
||||
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
|
||||
dataBase = AndroidDeviceDatabase(applicationContext)
|
||||
@ -197,7 +195,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
R.color.refresh_progress_3
|
||||
)
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
offlineShortcut = false
|
||||
repository.offlineOverride = false
|
||||
lastFetchDone = false
|
||||
handleDrawerItems()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
@ -689,30 +687,26 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
var sources: List<SelfossModel.Source>?
|
||||
|
||||
fun sourcesApiCall() {
|
||||
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut) && updateSources) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = repository.getSources()
|
||||
if (response != null) {
|
||||
sources = response
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
} else {
|
||||
val apiDrawerData = DrawerData(tags, null)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = repository.getSources()
|
||||
if (response != null) {
|
||||
sources = response
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
} else {
|
||||
val apiDrawerData = DrawerData(tags, null)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut) && updateSources) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
tags = repository.getTags()
|
||||
sourcesApiCall()
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
tags = repository.getTags()
|
||||
sourcesApiCall()
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,10 +938,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
private fun reloadBadges() {
|
||||
if (displayUnreadCount || displayAllCount) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
if (applicationContext.isNetworkAvailable()) {
|
||||
repository.reloadBadges()
|
||||
reloadBadgeContent()
|
||||
}
|
||||
repository.reloadBadges()
|
||||
reloadBadgeContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1021,61 +1013,56 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.refresh -> {
|
||||
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
|
||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||
// TODO: Use Dispatchers.IO
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val updatedRemote = repository.updateRemote()
|
||||
if (updatedRemote) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response, Toast.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||
// TODO: Use Dispatchers.IO
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val updatedRemote = repository.updateRemote()
|
||||
AmineB marked this conversation as resolved
Outdated
|
||||
if (updatedRemote) {
|
||||
// TODO: Send toast messages from the repository
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response, Toast.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.readAll -> {
|
||||
if (elementsShown == ItemType.UNREAD) {
|
||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
|
||||
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val success = repository.markAllAsRead(items)
|
||||
if (success) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
tabNewBadge.removeBadge()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val success = repository.markAllAsRead(items)
|
||||
if (success) {
|
||||
Toast.makeText(
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
It shouldn't be overridden here. `repository.offlineOverride` should only refreshed on app reload or swipe to refresh.
It shouldn't be overridden here.
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
tabNewBadge.removeBadge()
|
||||
|
||||
handleDrawerItems()
|
||||
handleDrawerItems()
|
||||
|
||||
getElementsAccordingToTab()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
handleListResult()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
getElementsAccordingToTab()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
handleListResult()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1127,17 +1114,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
}
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAvailable(null, offlineShortcut)) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val actions = db.actionsDao().actions()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(repository.markAsReadById(action.articleId.toInt()), action)
|
||||
action.unread -> doAndReportOnFail(repository.unmarkAsReadById(action.articleId.toInt()), action)
|
||||
action.starred -> doAndReportOnFail(repository.starrById(action.articleId.toInt()), action)
|
||||
action.unstarred -> doAndReportOnFail(repository.unstarrById(action.articleId.toInt()), action)
|
||||
}
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(repository.markAsReadById(action.articleId.toInt()), action)
|
||||
action.unread -> doAndReportOnFail(repository.unmarkAsReadById(action.articleId.toInt()), action)
|
||||
action.starred -> doAndReportOnFail(repository.starrById(action.articleId.toInt()), action)
|
||||
action.unstarred -> doAndReportOnFail(repository.unstarrById(action.articleId.toInt()), action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
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 com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import com.russhwolf.settings.Settings
|
||||
@ -202,15 +201,13 @@ class LoginActivity() : AppCompatActivity(), DIAware {
|
||||
|
||||
repository.refreshLoginInformation(url, login, password, httpLogin, httpPassword, isWithSelfSignedCert)
|
||||
|
||||
if (this@LoginActivity.isNetworkAvailable(this@LoginActivity.findViewById(R.id.loginForm))) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val result = repository.login()
|
||||
if (result) {
|
||||
goToMain()
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
preferenceError(Exception("Not success"))
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val result = repository.login()
|
||||
if (result) {
|
||||
goToMain()
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
preferenceError(Exception("Not success"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,18 +7,28 @@ import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
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.preference.PreferenceManager
|
||||
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
||||
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 com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.github.ln_12.library.ConnectivityStatus
|
||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
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.*
|
||||
@ -27,9 +37,14 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
|
||||
override val di by DI.lazy {
|
||||
import(networkModule)
|
||||
bind<Repository>() with singleton { Repository(instance(), instance()) }
|
||||
bind<Repository>() with singleton { Repository(instance(), instance(), connectivityStatus) }
|
||||
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 settings : Settings
|
||||
|
||||
@ -46,6 +61,18 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
tryToHandleBug()
|
||||
|
||||
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() {
|
||||
@ -105,4 +132,19 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
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.rest.SelfossModel
|
||||
import com.ftinc.scoop.Scoop
|
||||
@ -64,30 +63,28 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = mLayoutManager
|
||||
|
||||
if (this@SourcesActivity.isNetworkAvailable(binding.recyclerView)) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = repository.getSources()
|
||||
if (response != null) {
|
||||
items = response
|
||||
val mAdapter = SourcesListAdapter(
|
||||
this@SourcesActivity, items
|
||||
)
|
||||
binding.recyclerView.adapter = mAdapter
|
||||
mAdapter.notifyDataSetChanged()
|
||||
if (items.isEmpty()) {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.nothing_here,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = repository.getSources()
|
||||
if (response != null) {
|
||||
items = response
|
||||
val mAdapter = SourcesListAdapter(
|
||||
this@SourcesActivity, items
|
||||
)
|
||||
binding.recyclerView.adapter = mAdapter
|
||||
mAdapter.notifyDataSetChanged()
|
||||
if (items.isEmpty()) {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
R.string.nothing_here,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ import bou.amine.apps.readerforselfossv2.android.utils.*
|
||||
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.circularBitmapDrawable
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
@ -110,22 +109,20 @@ class ItemCardAdapter(
|
||||
|
||||
binding.favButton.setOnClickListener {
|
||||
val item = items[bindingAdapterPosition]
|
||||
if (c.isNetworkAvailable()) {
|
||||
if (item.starred) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
repository.unstarr(item)
|
||||
// TODO: Handle failure
|
||||
}
|
||||
item.starred = false
|
||||
binding.favButton.isSelected = false
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
repository.starr(item)
|
||||
// TODO: Handle failure
|
||||
}
|
||||
item.starred = true
|
||||
binding.favButton.isSelected = true
|
||||
if (item.starred) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
repository.unstarr(item)
|
||||
// TODO: Handle failure
|
||||
}
|
||||
item.starred = false
|
||||
binding.favButton.isSelected = false
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
repository.starr(item)
|
||||
// TODO: Handle failure
|
||||
}
|
||||
item.starred = true
|
||||
binding.favButton.isSelected = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import bou.amine.apps.readerforselfossv2.android.model.getIcon
|
||||
import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
||||
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.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
@ -78,21 +77,19 @@ class SourcesListAdapter(
|
||||
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
|
||||
|
||||
deleteBtn.setOnClickListener {
|
||||
if (c.isNetworkAvailable(null)) {
|
||||
val (id) = items[adapterPosition]
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val successfullyDeletedSource = repository.deleteSource(id)
|
||||
if (successfullyDeletedSource) {
|
||||
items.removeAt(adapterPosition)
|
||||
notifyItemRemoved(adapterPosition)
|
||||
notifyItemRangeChanged(adapterPosition, itemCount)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
app,
|
||||
R.string.can_delete_source,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
val (id) = items[adapterPosition]
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val successfullyDeletedSource = repository.deleteSource(id)
|
||||
if (successfullyDeletedSource) {
|
||||
items.removeAt(adapterPosition)
|
||||
notifyItemRemoved(adapterPosition)
|
||||
notifyItemRangeChanged(adapterPosition, itemCount)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
app,
|
||||
R.string.can_delete_source,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_3_4
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||
@ -44,67 +44,62 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
|
||||
override fun doWork(): Result {
|
||||
val settings = Settings()
|
||||
val periodicRefresh = settings.getBoolean("periodic_refresh", false)
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This should be reverted to the old network status check, because the network availability watcher should be stopped when the app is in the background. This should be reverted to the old network status check, because the network availability watcher should be stopped when the app is in the background.
|
||||
if (periodicRefresh) {
|
||||
if (periodicRefresh && isNetworkAccessible(context)) {
|
||||
|
||||
if (context.isNetworkAvailable()) {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
I think that this should be the only place where the connectivity check should stay like this. If there is no network connectivity, you don't do anything in the background. I think that this should be the only place where the connectivity check should stay like this.
If there is no network connectivity, you don't do anything in the background.
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val notificationManager =
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val notificationManager =
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val notification =
|
||||
NotificationCompat.Builder(applicationContext, Config.syncChannelId)
|
||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||
.setContentText(context.getString(R.string.loading_notification_text))
|
||||
.setOngoing(true)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setChannelId(Config.syncChannelId)
|
||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||
|
||||
val notification =
|
||||
NotificationCompat.Builder(applicationContext, Config.syncChannelId)
|
||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||
.setContentText(context.getString(R.string.loading_notification_text))
|
||||
.setOngoing(true)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setChannelId(Config.syncChannelId)
|
||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||
notificationManager.notify(1, notification.build())
|
||||
|
||||
notificationManager.notify(1, notification.build())
|
||||
val notifyNewItems = settings.getBoolean("notify_new_items", false)
|
||||
|
||||
val notifyNewItems = settings.getBoolean("notify_new_items", false)
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3)
|
||||
.addMigrations(MIGRATION_3_4).build()
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3)
|
||||
.addMigrations(MIGRATION_3_4).build()
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(
|
||||
repository.markAsReadById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.unread -> doAndReportOnFail(
|
||||
repository.unmarkAsReadById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.starred -> doAndReportOnFail(
|
||||
repository.starrById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.unstarred -> doAndReportOnFail(
|
||||
repository.unstarrById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
}
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(
|
||||
repository.markAsReadById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.unread -> doAndReportOnFail(
|
||||
repository.unmarkAsReadById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.starred -> doAndReportOnFail(
|
||||
repository.starrById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
action.unstarred -> doAndReportOnFail(
|
||||
repository.unstarrById(action.articleId.toInt()),
|
||||
action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (context.isNetworkAvailable()) {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
Same here. Same here.
|
||||
launch {
|
||||
try {
|
||||
val newItems = repository.allItems(ItemType.UNREAD)
|
||||
handleNewItemsNotification(newItems, notifyNewItems, notificationManager)
|
||||
val readItems = repository.allItems(ItemType.ALL)
|
||||
val starredItems = repository.allItems(ItemType.STARRED)
|
||||
// TODO: save all to DB
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
try {
|
||||
val newItems = repository.allItems(ItemType.UNREAD)
|
||||
handleNewItemsNotification(newItems, notifyNewItems, notificationManager)
|
||||
val readItems = repository.allItems(ItemType.ALL)
|
||||
val starredItems = repository.allItems(ItemType.STARRED)
|
||||
// TODO: save all to DB
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ import bou.amine.apps.readerforselfossv2.android.utils.*
|
||||
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.loadMaybeBasicAuth
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAvailable
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
|
||||
@ -276,9 +275,9 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
}
|
||||
|
||||
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
|
||||
if ((context != null && requireContext().isNetworkAvailable(null)) || context == null) {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
Same here. We are fetching data from an api, and we there is no need to do try anything if there is no network available. Same here. We are fetching data from an api, and we there is no need to do try anything if there is no network available.
davidoskky
commented
Right! Right!
By the way, what should this Mercury be?
I never saw this work and each time I got the article open in the browser instead.
AmineB
commented
This is mercury. It parses the page and fetch it´s content. I'll look into why it does not work. [This is mercury](https://mercury.postlight.com/web-parser/). It parses the page and fetch it´s content.
I'll look into why it does not work.
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
if (repository.isNetworkAvailable()) {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This should only be done if the network is available as it was before. This should only be done if the network is available as it was before.
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
|
||||
parser.parseUrl(url).enqueue(
|
||||
object : Callback<ParsedContent> {
|
||||
@ -317,7 +316,10 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
Glide
|
||||
.with(requireContext())
|
||||
.asBitmap()
|
||||
.loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
|
||||
.loadMaybeBasicAuth(
|
||||
config,
|
||||
response.body()!!.lead_image_url.orEmpty()
|
||||
)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(binding.imageView)
|
||||
} catch (e: Exception) {
|
||||
|
@ -1,52 +1,14 @@
|
||||
package bou.amine.apps.readerforselfossv2.android.utils.network
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
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
|
||||
|
||||
var snackBarShown = false
|
||||
var view: View? = null
|
||||
lateinit var s: Snackbar
|
||||
|
||||
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 {
|
||||
fun isNetworkAccessible(context: Context): Boolean {
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
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) {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
It would be better to emit an enum. We'll handle the translation on the android/ios side. It would be better to emit an enum. We'll handle the translation on the android/ios side.
davidoskky
commented
What do you mean? This view model is already in the android side; these strings will have to be localized. I can do it now What do you mean? This view model is already in the android side; these strings will have to be localized. I can do it now
AmineB
commented
I didn´t see that it was in the android side. Yes, please use localized stringd. I didn´t see that it was in the android side.
Yes, please use localized stringd.
|
||||
_toastMessageProvider.emit(R.string.network_connectivity_retrieved)
|
||||
wasConnected = true
|
||||
} else if (!isConnected && wasConnected && repository.connectionMonitored){
|
||||
_toastMessageProvider.emit(R.string.network_connectivity_lost)
|
||||
wasConnected = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -143,6 +143,8 @@
|
||||
<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="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_off">Articles will not be synced in the background</string>
|
||||
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
|
||||
|
@ -36,6 +36,9 @@ kotlin {
|
||||
|
||||
//Logging
|
||||
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 {
|
||||
|
@ -5,16 +5,19 @@ import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||
import bou.amine.apps.readerforselfossv2.utils.DateUtils
|
||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||
import com.github.ln_12.library.ConnectivityStatus
|
||||
import com.russhwolf.settings.Settings
|
||||
import io.github.aakira.napier.Napier
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService) {
|
||||
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, val connectivityStatus: ConnectivityStatus) {
|
||||
val settings = Settings()
|
||||
|
||||
var items = ArrayList<SelfossModel.Item>()
|
||||
val isConnectionAvailable = connectivityStatus.isNetworkConnected
|
||||
var connectionMonitored = false
|
||||
|
||||
var baseUrl = apiDetails.getBaseUrl()
|
||||
lateinit var dateUtils: DateUtils
|
||||
@ -26,6 +29,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
var searchFilter: String? = null
|
||||
|
||||
var itemsCaching = settings.getBoolean("items_caching", false)
|
||||
var offlineOverride = false
|
||||
|
||||
var apiMajorVersion = 0
|
||||
var badgeUnread = 0
|
||||
@ -45,14 +49,21 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
}
|
||||
|
||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||
// TODO: Check connectivity, use the updatedSince parameter
|
||||
val fetchedItems = api.getItems(displayedItems.type,
|
||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
||||
offset = 0,
|
||||
tagFilter?.tag,
|
||||
sourceFilter?.id?.toLong(),
|
||||
searchFilter,
|
||||
null)
|
||||
// TODO: Use the updatedSince parameter
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
`isConnectionAvailable.value && !offlineOverride` should be refactored inside a method named `isNetworkAvailable()`
|
||||
if (isNetworkAvailable()) {
|
||||
fetchedItems = api.getItems(
|
||||
displayedItems.type,
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
There should be a message emited on network available too. There should be a message emited on network available too.
|
||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
||||
offset = 0,
|
||||
tagFilter?.tag,
|
||||
sourceFilter?.id?.toLong(),
|
||||
searchFilter,
|
||||
null
|
||||
)
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This block should be after the else, since the db will work as a fallback and we'll get the items from there. Same for the other block later. This block should be after the else, since the db will work as a fallback and we'll get the items from there.
Same for the other block later.
davidoskky
commented
I'm not sure how the database will handle this. I'm not sure how the database will handle this.
This is correct as of now, moving this block after the else will generate errors if the else block is encountered since the fetchedItems variables is only defined within the if block.
AmineB
commented
The The `else` block would have the BD fetching that'll assign the DB items to `fetchedItems`.
|
||||
} else {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This should be moved after the This should be moved after the `else` block
|
||||
// TODO: Get items from the database
|
||||
}
|
||||
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
The error message should be displayed when the network status change, not after every api call. Same for every comment like this one. The error message should be displayed when the network status change, not after every api call.
Same for every comment like this one.
|
||||
if (fetchedItems != null) {
|
||||
items = ArrayList(fetchedItems)
|
||||
@ -61,15 +72,21 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
}
|
||||
|
||||
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
|
||||
// TODO: Check connectivity
|
||||
val offset = items.size
|
||||
val fetchedItems = api.getItems(displayedItems.type,
|
||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
||||
offset,
|
||||
tagFilter?.tag,
|
||||
sourceFilter?.id?.toLong(),
|
||||
searchFilter,
|
||||
null)
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
if (isNetworkAvailable()) {
|
||||
val offset = items.size
|
||||
fetchedItems = api.getItems(
|
||||
displayedItems.type,
|
||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
||||
offset,
|
||||
tagFilter?.tag,
|
||||
sourceFilter?.id?.toLong(),
|
||||
searchFilter,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This should be moved after the This should be moved after the `else` block
|
||||
// TODO: Get items from the database
|
||||
}
|
||||
|
||||
if (fetchedItems != null) {
|
||||
appendItems(fetchedItems)
|
||||
@ -77,8 +94,22 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
return items
|
||||
}
|
||||
|
||||
suspend fun allItems(itemType: ItemType): List<SelfossModel.Item>? =
|
||||
api.getItems(itemType.type, 200, 0, tagFilter?.tag, sourceFilter?.id?.toLong(), searchFilter, null)
|
||||
suspend fun allItems(itemType: ItemType): List<SelfossModel.Item>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.getItems(
|
||||
itemType.type,
|
||||
200,
|
||||
0,
|
||||
tagFilter?.tag,
|
||||
sourceFilter?.id?.toLong(),
|
||||
searchFilter,
|
||||
null
|
||||
)
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
This should be a todo This should be a todo
|
||||
} else {
|
||||
// TODO: Provide an error message
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendItems(fetchedItems: List<SelfossModel.Item>) {
|
||||
// TODO: Store in DB if enabled by user
|
||||
@ -94,35 +125,52 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
}
|
||||
|
||||
suspend fun reloadBadges(): Boolean {
|
||||
// TODO: Check connectivity, calculate from DB
|
||||
var success = false
|
||||
val response = api.stats()
|
||||
if (response != null) {
|
||||
badgeUnread = response.unread
|
||||
badgeAll = response.total
|
||||
badgeStarred = response.starred
|
||||
success = true
|
||||
if (isNetworkAvailable()) {
|
||||
val response = api.stats()
|
||||
if (response != null) {
|
||||
badgeUnread = response.unread
|
||||
badgeAll = response.total
|
||||
badgeStarred = response.starred
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
// TODO: Compute badges from database
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun getTags(): List<SelfossModel.Tag>? {
|
||||
// TODO: Check success, store in DB
|
||||
return api.tags()
|
||||
// TODO: Store in DB
|
||||
return if (isNetworkAvailable()) {
|
||||
api.tags()
|
||||
} else {
|
||||
// TODO: Compute from database
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
||||
// TODO: Check success, store in DB
|
||||
return api.spouts()
|
||||
// TODO: Store in DB
|
||||
return if (isNetworkAvailable()) {
|
||||
api.spouts()
|
||||
} else {
|
||||
// TODO: Compute from database
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
||||
// TODO: Check success
|
||||
return api.sources()
|
||||
// TODO: Store in DB
|
||||
return if (isNetworkAvailable()) {
|
||||
api.sources()
|
||||
} else {
|
||||
// TODO: Compute from database
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
||||
// TODO: Check internet connection
|
||||
val success = markAsReadById(item.id)
|
||||
|
||||
if (success) {
|
||||
@ -131,10 +179,9 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun markAsReadById(id: Int): Boolean {
|
||||
// TODO: Check internet connection
|
||||
return api.markAsRead(id.toString())?.isSuccess == true
|
||||
}
|
||||
suspend fun markAsReadById(id: Int): Boolean =
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
Every block that does something like
can be refactored to something like
Every block that does something like
```
var success = false
if (isNetworkAvailable()) {
success = DOSOMETHING()
}
return success
```
can be refactored to something like
```
return isNetworkAvailable() && DOSOMETHING()
```
|
||||
isNetworkAvailable() && api.markAsRead(id.toString())?.isSuccess == true
|
||||
|
||||
|
||||
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
||||
val success = unmarkAsReadById(item.id)
|
||||
@ -145,10 +192,8 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun unmarkAsReadById(id: Int): Boolean {
|
||||
// TODO: Check internet connection
|
||||
return api.unmarkAsRead(id.toString())?.isSuccess == true
|
||||
}
|
||||
suspend fun unmarkAsReadById(id: Int): Boolean =
|
||||
isNetworkAvailable() && api.unmarkAsRead(id.toString())?.isSuccess == true
|
||||
|
||||
suspend fun starr(item: SelfossModel.Item): Boolean {
|
||||
val success = starrById(item.id)
|
||||
@ -159,10 +204,8 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun starrById(id: Int): Boolean {
|
||||
// TODO: Check success, store in DB
|
||||
return api.starr(id.toString())?.isSuccess == true
|
||||
}
|
||||
suspend fun starrById(id: Int): Boolean =
|
||||
isNetworkAvailable() && api.starr(id.toString())?.isSuccess == true
|
||||
|
||||
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
||||
val success = unstarrById(item.id)
|
||||
@ -173,17 +216,15 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
return success
|
||||
}
|
||||
AmineB marked this conversation as resolved
Outdated
AmineB
commented
Please refactor this Please refactor this
|
||||
|
||||
suspend fun unstarrById(id: Int): Boolean {
|
||||
// TODO: Check internet connection
|
||||
return api.unstarr(id.toString())?.isSuccess == true
|
||||
}
|
||||
suspend fun unstarrById(id: Int): Boolean =
|
||||
isNetworkAvailable() && api.unstarr(id.toString())?.isSuccess == true
|
||||
|
||||
|
||||
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
||||
// TODO: Check Internet connectivity, store in DB
|
||||
var success = false
|
||||
|
||||
val success = api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true
|
||||
|
||||
if (success) {
|
||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) {
|
||||
success = true
|
||||
for (item in items) {
|
||||
markAsReadLocally(item)
|
||||
}
|
||||
@ -230,45 +271,46 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
tags: String,
|
||||
filter: String
|
||||
): Boolean {
|
||||
// TODO: Check connectivity
|
||||
val response = api.createSourceForVersion(
|
||||
title,
|
||||
url,
|
||||
spout,
|
||||
tags,
|
||||
filter,
|
||||
apiMajorVersion
|
||||
)
|
||||
var response = false
|
||||
if (isNetworkAvailable()) {
|
||||
response = api.createSourceForVersion(
|
||||
title,
|
||||
url,
|
||||
spout,
|
||||
tags,
|
||||
filter,
|
||||
apiMajorVersion
|
||||
)?.isSuccess == true
|
||||
}
|
||||
|
||||
return response != null
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun deleteSource(id: Int): Boolean {
|
||||
// TODO: Check connectivity, store in DB
|
||||
// TODO: Store in DB
|
||||
var success = false
|
||||
val response = api.deleteSource(id)
|
||||
if (response != null) {
|
||||
success = response.isSuccess
|
||||
if (isNetworkAvailable()) {
|
||||
val response = api.deleteSource(id)
|
||||
if (response != null) {
|
||||
success = response.isSuccess
|
||||
}
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun updateRemote(): Boolean {
|
||||
// TODO: Handle connectivity issues
|
||||
val response = api.update()
|
||||
return response?.isSuccess ?: false
|
||||
}
|
||||
suspend fun updateRemote(): Boolean =
|
||||
isNetworkAvailable() && api.update()?.isSuccess == true
|
||||
|
||||
suspend fun login(): Boolean {
|
||||
var result = false
|
||||
try {
|
||||
val response = api.login()
|
||||
if (response != null && response.isSuccess) {
|
||||
result = true
|
||||
if (isNetworkAvailable()) {
|
||||
try {
|
||||
val response = api.login()
|
||||
result = response?.isSuccess == true
|
||||
} catch (cause: Throwable) {
|
||||
Napier.e(cause.stackTraceToString(), tag = "RepositoryImpl.updateRemote")
|
||||
}
|
||||
} catch (cause: Throwable) {
|
||||
Napier.e(cause.stackTraceToString(),tag = "RepositoryImpl.updateRemote")
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -287,15 +329,18 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
||||
}
|
||||
|
||||
private suspend fun updateApiVersion() {
|
||||
// TODO: Handle connectivity issues
|
||||
val fetchedVersion = api.version()
|
||||
if (fetchedVersion != null) {
|
||||
apiMajorVersion = fetchedVersion.getApiMajorVersion()
|
||||
settings.putInt("apiVersionMajor", apiMajorVersion)
|
||||
} else {
|
||||
apiMajorVersion = settings.getInt("apiVersionMajor", 0)
|
||||
apiMajorVersion = settings.getInt("apiVersionMajor", 0)
|
||||
|
||||
if (isNetworkAvailable()) {
|
||||
val fetchedVersion = api.version()
|
||||
if (fetchedVersion != null) {
|
||||
apiMajorVersion = fetchedVersion.getApiMajorVersion()
|
||||
settings.putInt("apiVersionMajor", apiMajorVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride
|
||||
|
||||
// TODO: Handle offline actions
|
||||
}
|
repository.offlineOverride should only refreshed on app reload or swipe to refresh.
It shouldn't be overridden here.