Compare commits
2 Commits
fbcb428e96
...
8c817b5938
Author | SHA1 | Date | |
---|---|---|---|
8c817b5938 | |||
4857a3d0ac |
@ -17,6 +17,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.*
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
@ -34,10 +35,13 @@ 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.dao.ACTION
|
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.utils.*
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.longHash
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||||
@ -66,7 +70,6 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
||||||
|
|
||||||
@ -118,6 +121,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
private val repository : Repository by instance()
|
private val repository : Repository by instance()
|
||||||
|
private val viewModel: AppViewModel by instance()
|
||||||
|
|
||||||
data class DrawerData(val tags: List<SelfossModel.Tag>?, val sources: List<SelfossModel.Source>?)
|
data class DrawerData(val tags: List<SelfossModel.Tag>?, val sources: List<SelfossModel.Source>?)
|
||||||
|
|
||||||
@ -142,6 +146,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.refreshingIndicatorProvider.collect { showRefresh ->
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = showRefresh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleThemeBinding()
|
handleThemeBinding()
|
||||||
|
|
||||||
@ -153,18 +162,25 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
mDrawerToggle.syncState()
|
mDrawerToggle.syncState()
|
||||||
|
|
||||||
customTabActivityHelper = CustomTabActivityHelper()
|
customTabActivityHelper = CustomTabActivityHelper()
|
||||||
|
handleSettings()
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.items.collect { fetchedItems ->
|
||||||
|
items = fetchedItems
|
||||||
|
handleListResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleBottomBar()
|
handleBottomBar()
|
||||||
handleDrawer()
|
handleDrawer()
|
||||||
|
|
||||||
handleSwipeRefreshLayout()
|
handleSwipeRefreshLayout()
|
||||||
|
|
||||||
handleSettings()
|
|
||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
|
|
||||||
|
handleBadgesContent()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.tryToCacheItemsAndGetNewOnes()
|
repository.tryToCacheItemsAndGetNewOnes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,10 +196,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
repository.offlineOverride = false
|
repository.offlineOverride = false
|
||||||
lastFetchDone = false
|
lastFetchDone = false
|
||||||
handleDrawerItems()
|
handleDrawerItems()
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
viewModel.getItems(false, elementsShown)
|
||||||
getElementsAccordingToTab()
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val simpleItemTouchCallback =
|
val simpleItemTouchCallback =
|
||||||
@ -219,8 +232,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
adapter.handleItemAtIndex(position)
|
adapter.handleItemAtIndex(position)
|
||||||
|
|
||||||
reloadBadgeContent()
|
|
||||||
|
|
||||||
val tagHashes = i.tags.map { it.longHash() }
|
val tagHashes = i.tags.map { it.longHash() }
|
||||||
tagsBadge = tagsBadge.map {
|
tagsBadge = tagsBadge.map {
|
||||||
if (tagHashes.contains(it.key)) {
|
if (tagHashes.contains(it.key)) {
|
||||||
@ -252,16 +263,23 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
tabNewBadge = TextBadgeItem()
|
tabNewBadge = TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
.setBackgroundColor(appColors.colorPrimary)
|
.setBackgroundColor(appColors.colorPrimary)
|
||||||
|
if (!displayUnreadCount) {
|
||||||
|
tabNewBadge.hide(false)
|
||||||
|
}
|
||||||
tabArchiveBadge = TextBadgeItem()
|
tabArchiveBadge = TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
.setBackgroundColor(appColors.colorPrimary)
|
.setBackgroundColor(appColors.colorPrimary)
|
||||||
tabStarredBadge = TextBadgeItem()
|
tabStarredBadge = TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
.setBackgroundColor(appColors.colorPrimary)
|
.setBackgroundColor(appColors.colorPrimary)
|
||||||
|
if (!displayAllCount) {
|
||||||
|
tabArchiveBadge.hide(false)
|
||||||
|
tabStarredBadge.hide(false)
|
||||||
|
}
|
||||||
|
|
||||||
val tabNew =
|
val tabNew =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
@ -453,7 +471,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
// TODO: refactor this.
|
// TODO: refactor this.
|
||||||
private fun handleDrawerItems() {
|
private fun handleDrawerItems() {
|
||||||
tagsBadge = emptyMap()
|
tagsBadge = emptyMap()
|
||||||
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
|
fun handleDrawerData(maybeDrawerData: DrawerData?) {
|
||||||
fun createDrawerItem(
|
fun createDrawerItem(
|
||||||
it: SelfossModel.Tag
|
it: SelfossModel.Tag
|
||||||
) {
|
) {
|
||||||
@ -495,12 +513,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
fun handleTags(maybeTags: List<SelfossModel.Tag>?) {
|
fun handleTags(maybeTags: List<SelfossModel.Tag>?) {
|
||||||
if (maybeTags == null) {
|
if (maybeTags == null) {
|
||||||
if (loadedFromCache) {
|
|
||||||
binding.mainDrawer.itemAdapter.add(
|
binding.mainDrawer.itemAdapter.add(
|
||||||
SecondaryDrawerItem()
|
SecondaryDrawerItem()
|
||||||
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
|
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val filteredTags = maybeTags
|
val filteredTags = maybeTags
|
||||||
.filterNot { hiddenTags.contains(it.tag) }
|
.filterNot { hiddenTags.contains(it.tag) }
|
||||||
@ -515,14 +531,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
fun handleHiddenTags(maybeTags: List<SelfossModel.Tag>?) {
|
fun handleHiddenTags(maybeTags: List<SelfossModel.Tag>?) {
|
||||||
if (maybeTags == null) {
|
if (maybeTags == null) {
|
||||||
if (loadedFromCache) {
|
|
||||||
binding.mainDrawer.itemAdapter.add(
|
binding.mainDrawer.itemAdapter.add(
|
||||||
SecondaryDrawerItem().apply {
|
SecondaryDrawerItem().apply {
|
||||||
nameRes = R.string.drawer_error_loading_tags
|
nameRes = R.string.drawer_error_loading_tags
|
||||||
isSelectable = false
|
isSelectable = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val filteredHiddenTags: List<SelfossModel.Tag> =
|
val filteredHiddenTags: List<SelfossModel.Tag> =
|
||||||
maybeTags.filter { hiddenTags.contains(it.tag) }
|
maybeTags.filter { hiddenTags.contains(it.tag) }
|
||||||
@ -536,14 +550,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
fun handleSources(maybeSources: List<SelfossModel.Source>?) {
|
fun handleSources(maybeSources: List<SelfossModel.Source>?) {
|
||||||
if (maybeSources == null) {
|
if (maybeSources == null) {
|
||||||
if (loadedFromCache) {
|
|
||||||
binding.mainDrawer.itemAdapter.add(
|
binding.mainDrawer.itemAdapter.add(
|
||||||
SecondaryDrawerItem().apply {
|
SecondaryDrawerItem().apply {
|
||||||
nameRes = R.string.drawer_error_loading_sources
|
nameRes = R.string.drawer_error_loading_sources
|
||||||
isSelectable = false
|
isSelectable = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (source in maybeSources) {
|
for (source in maybeSources) {
|
||||||
val item = PrimaryDrawerItem().apply {
|
val item = PrimaryDrawerItem().apply {
|
||||||
@ -632,20 +644,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!loadedFromCache) {
|
|
||||||
if (maybeDrawerData.tags != null) {
|
|
||||||
thread {
|
|
||||||
repository.resetDBTagsWithData(maybeDrawerData.tags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maybeDrawerData.sources != null) {
|
|
||||||
thread {
|
|
||||||
repository.resetDBSourcesWithData(maybeDrawerData.sources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!loadedFromCache) {
|
|
||||||
binding.mainDrawer.itemAdapter.add(
|
binding.mainDrawer.itemAdapter.add(
|
||||||
PrimaryDrawerItem().apply {
|
PrimaryDrawerItem().apply {
|
||||||
nameRes = R.string.no_tags_loaded
|
nameRes = R.string.no_tags_loaded
|
||||||
@ -660,35 +659,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
|
|
||||||
var tags: List<SelfossModel.Tag>? = null
|
|
||||||
var sources: List<SelfossModel.Source>?
|
|
||||||
|
|
||||||
fun sourcesApiCall() {
|
|
||||||
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.IO).launch {
|
|
||||||
tags = repository.getTags()
|
|
||||||
sourcesApiCall()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.mainDrawer.itemAdapter.add(
|
binding.mainDrawer.itemAdapter.add(
|
||||||
PrimaryDrawerItem().apply {
|
PrimaryDrawerItem().apply {
|
||||||
@ -697,12 +667,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
thread {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
|
val drawerData = DrawerData(repository.getTags(),
|
||||||
repository.getDBSources().map { it.toView() })
|
repository.getSources())
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
handleDrawerData(drawerData, loadedFromCache = true)
|
handleDrawerData(drawerData)
|
||||||
drawerApiCalls(drawerData)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -839,21 +808,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
firstVisible = if (appendResults) firstVisible else 0
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
|
|
||||||
getItems(appendResults, elementsShown)
|
viewModel.getItems(appendResults, elementsShown)
|
||||||
}
|
|
||||||
|
|
||||||
private fun getItems(appendResults: Boolean, itemType: ItemType) {
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
|
||||||
repository.displayedItems = itemType
|
|
||||||
items = if (appendResults) {
|
|
||||||
repository.getOlderItems()
|
|
||||||
} else {
|
|
||||||
repository.getNewerItems()
|
|
||||||
}
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
|
||||||
handleListResult()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleListResult(appendResults: Boolean = false) {
|
private fun handleListResult(appendResults: Boolean = false) {
|
||||||
@ -915,26 +870,50 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
private fun reloadBadges() {
|
private fun reloadBadges() {
|
||||||
if (displayUnreadCount || displayAllCount) {
|
if (displayUnreadCount || displayAllCount) {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.reloadBadges()
|
repository.reloadBadges()
|
||||||
reloadBadgeContent()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadBadgeContent() {
|
private fun handleBadgesContent() {
|
||||||
if (displayUnreadCount) {
|
if (displayUnreadCount) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repository.badgeUnread.collect { unreadCount ->
|
||||||
|
if (unreadCount > 0) {
|
||||||
tabNewBadge
|
tabNewBadge
|
||||||
.setText(repository.badgeUnread.toString())
|
.setText(unreadCount.toString())
|
||||||
.maybeShow()
|
.maybeShow()
|
||||||
|
} else {
|
||||||
|
tabNewBadge.removeBadge()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (displayAllCount) {
|
if (displayAllCount) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repository.badgeAll.collect { itemsCount ->
|
||||||
|
if (itemsCount > 0) {
|
||||||
tabArchiveBadge
|
tabArchiveBadge
|
||||||
.setText(repository.badgeAll.toString())
|
.setText(itemsCount.toString())
|
||||||
.maybeShow()
|
.maybeShow()
|
||||||
|
} else {
|
||||||
|
tabArchiveBadge.removeBadge()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repository.badgeStarred.collect { starredCount ->
|
||||||
|
if (starredCount > 0) {
|
||||||
tabStarredBadge
|
tabStarredBadge
|
||||||
.setText(repository.badgeStarred.toString())
|
.setText(starredCount.toString())
|
||||||
.maybeShow()
|
.maybeShow()
|
||||||
|
} else {
|
||||||
|
tabStarredBadge.removeBadge()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -992,56 +971,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.refresh -> {
|
R.id.refresh -> {
|
||||||
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()
|
viewModel.updateRemote()
|
||||||
// TODO: Use Dispatchers.IO
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
val updatedRemote = repository.updateRemote()
|
|
||||||
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
|
return true
|
||||||
}
|
}
|
||||||
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
|
viewModel.markAllAsRead()
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
handleDrawerItems()
|
|
||||||
|
|
||||||
getElementsAccordingToTab()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this@HomeActivity,
|
|
||||||
R.string.all_posts_not_read,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
handleListResult()
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -1055,10 +992,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
private fun maxItemNumber(): Int =
|
private fun maxItemNumber(): Int =
|
||||||
when (elementsShown) {
|
when (elementsShown) {
|
||||||
ItemType.UNREAD -> repository.badgeUnread
|
ItemType.UNREAD -> repository.badgeUnread.value
|
||||||
ItemType.ALL -> repository.badgeAll
|
ItemType.ALL -> repository.badgeAll.value
|
||||||
ItemType.STARRED -> repository.badgeStarred
|
ItemType.STARRED -> repository.badgeStarred.value
|
||||||
else -> repository.badgeUnread // if !elementsShown then unread are fetched.
|
else -> repository.badgeUnread.value // if !elementsShown then unread are fetched.
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateItems(adapterItems: ArrayList<SelfossModel.Item>) {
|
private fun updateItems(adapterItems: ArrayList<SelfossModel.Item>) {
|
||||||
@ -1082,9 +1019,4 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOfflineActions() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -84,6 +84,15 @@ class MyApp : MultiDexApplication(), DIAware {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
viewModel.toastMessageProvider.collect { toastMessage ->
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
toastMessage,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,16 +3,29 @@ package bou.amine.apps.readerforselfossv2.android.viewmodel
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class AppViewModel(private val repository: Repository) : ViewModel() {
|
class AppViewModel(private val repository: Repository) : ViewModel() {
|
||||||
private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
|
private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
|
||||||
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
|
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
|
||||||
|
private val _refreshingIndicatorProvider = MutableSharedFlow<Boolean>()
|
||||||
|
val refreshingIndicatorProvider = _refreshingIndicatorProvider.asSharedFlow()
|
||||||
|
private val _toastMessageProvider = MutableSharedFlow<Int>()
|
||||||
|
val toastMessageProvider = _toastMessageProvider.asSharedFlow()
|
||||||
private var wasConnected = true
|
private var wasConnected = true
|
||||||
|
|
||||||
|
private val _items = MutableStateFlow(ArrayList<SelfossModel.Item>())
|
||||||
|
val items = _items.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.isConnectionAvailable.collect { isConnected ->
|
repository.isConnectionAvailable.collect { isConnected ->
|
||||||
@ -28,4 +41,43 @@ class AppViewModel(private val repository: Repository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateRemote() {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
_toastMessageProvider.emit(R.string.refresh_in_progress)
|
||||||
|
val updatedRemote = repository.updateRemote()
|
||||||
|
if (updatedRemote) {
|
||||||
|
_toastMessageProvider.emit(R.string.refresh_success_response)
|
||||||
|
} else {
|
||||||
|
_toastMessageProvider.emit(R.string.refresh_failer_message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItems(appendResults: Boolean, itemType: ItemType) {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
_refreshingIndicatorProvider.emit(true)
|
||||||
|
repository.displayedItems = itemType
|
||||||
|
val items = if (appendResults) {
|
||||||
|
repository.getOlderItems()
|
||||||
|
} else {
|
||||||
|
repository.getNewerItems()
|
||||||
|
}
|
||||||
|
_items.emit(items)
|
||||||
|
_refreshingIndicatorProvider.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markAllAsRead() {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
_refreshingIndicatorProvider.emit(true)
|
||||||
|
val success = repository.markAllAsRead(items.value)
|
||||||
|
if (success) {
|
||||||
|
_toastMessageProvider.emit(R.string.all_posts_read)
|
||||||
|
} else {
|
||||||
|
_toastMessageProvider.emit(R.string.all_posts_not_read)
|
||||||
|
}
|
||||||
|
_refreshingIndicatorProvider.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,6 +11,8 @@ 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.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, private val connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
|
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, private val connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
|
||||||
@ -33,20 +35,25 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
var offlineOverride = false
|
var offlineOverride = false
|
||||||
|
|
||||||
var apiMajorVersion = 0
|
var apiMajorVersion = 0
|
||||||
var badgeUnread = 0
|
private val _badgeUnread = MutableStateFlow(0)
|
||||||
set(value) {field = if (value < 0) { 0 } else { value } }
|
val badgeUnread = _badgeUnread.asStateFlow()
|
||||||
var badgeAll = 0
|
private val _badgeAll = MutableStateFlow(0)
|
||||||
set(value) {field = if (value < 0) { 0 } else { value } }
|
val badgeAll = _badgeAll.asStateFlow()
|
||||||
var badgeStarred = 0
|
private val _badgeStarred = MutableStateFlow(0)
|
||||||
set(value) {field = if (value < 0) { 0 } else { value } }
|
val badgeStarred = _badgeStarred.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
updateApiVersion()
|
||||||
|
isConnectionAvailable.collect { connectionAvailable ->
|
||||||
|
if (connectionAvailable) {
|
||||||
updateApiVersion()
|
updateApiVersion()
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||||
// TODO: Use the updatedSince parameter
|
// TODO: Use the updatedSince parameter
|
||||||
@ -125,27 +132,31 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
val response = api.stats()
|
val response = api.stats()
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
badgeUnread = response.unread
|
_badgeUnread.value = response.unread
|
||||||
badgeAll = response.total
|
_badgeAll.value = response.total
|
||||||
badgeStarred = response.starred
|
_badgeStarred.value = response.starred
|
||||||
success = true
|
success = true
|
||||||
}
|
}
|
||||||
} else {
|
} else if (itemsCaching) {
|
||||||
// TODO: do this differently, because it's not efficient
|
// TODO: do this differently, because it's not efficient
|
||||||
val dbItems = getDBItems()
|
val dbItems = getDBItems()
|
||||||
badgeUnread = dbItems.filter { item -> item.unread }.size
|
_badgeUnread.value = dbItems.filter { item -> item.unread }.size
|
||||||
badgeStarred = dbItems.filter { item -> item.starred }.size
|
_badgeStarred.value = dbItems.filter { item -> item.starred }.size
|
||||||
badgeAll = items.size
|
_badgeAll.value = dbItems.size
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getTags(): List<SelfossModel.Tag>? {
|
suspend fun getTags(): List<SelfossModel.Tag>? {
|
||||||
return if (isNetworkAvailable()) {
|
val tags = if (isNetworkAvailable()) {
|
||||||
api.tags()
|
api.tags()
|
||||||
} else {
|
} else {
|
||||||
getDBTags().map { it.toView() }
|
getDBTags().map { it.toView() }
|
||||||
}
|
}
|
||||||
|
if (tags != null) {
|
||||||
|
resetDBTagsWithData(tags)
|
||||||
|
}
|
||||||
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
||||||
@ -157,12 +168,15 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
||||||
|
val sources = if (isNetworkAvailable()) {
|
||||||
return if (isNetworkAvailable()) {
|
|
||||||
api.sources()
|
api.sources()
|
||||||
} else {
|
} else {
|
||||||
ArrayList(getDBSources().map { it.toView() })
|
ArrayList(getDBSources().map { it.toView() })
|
||||||
}
|
}
|
||||||
|
if (sources != null) {
|
||||||
|
resetDBSourcesWithData(sources)
|
||||||
|
}
|
||||||
|
return sources
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
||||||
@ -253,7 +267,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
private fun markAsReadLocally(item: SelfossModel.Item) {
|
private fun markAsReadLocally(item: SelfossModel.Item) {
|
||||||
if (item.unread) {
|
if (item.unread) {
|
||||||
item.unread = false
|
item.unread = false
|
||||||
badgeUnread -= 1
|
_badgeUnread.value -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -264,7 +278,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
private fun unmarkAsReadLocally(item: SelfossModel.Item) {
|
private fun unmarkAsReadLocally(item: SelfossModel.Item) {
|
||||||
if (!item.unread) {
|
if (!item.unread) {
|
||||||
item.unread = true
|
item.unread = true
|
||||||
badgeUnread += 1
|
_badgeUnread.value += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -275,7 +289,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
private fun starrLocally(item: SelfossModel.Item) {
|
private fun starrLocally(item: SelfossModel.Item) {
|
||||||
if (!item.starred) {
|
if (!item.starred) {
|
||||||
item.starred = true
|
item.starred = true
|
||||||
badgeStarred += 1
|
_badgeStarred.value += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -286,7 +300,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
private fun unstarrLocally(item: SelfossModel.Item) {
|
private fun unstarrLocally(item: SelfossModel.Item) {
|
||||||
if (item.starred) {
|
if (item.starred) {
|
||||||
item.starred = false
|
item.starred = false
|
||||||
badgeStarred -= 1
|
_badgeStarred.value -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -428,13 +442,16 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
|
|
||||||
|
|
||||||
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item>? {
|
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item>? {
|
||||||
|
if (itemsCaching) {
|
||||||
try {
|
try {
|
||||||
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
||||||
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
||||||
val starredItems = getMaxItemsForBackground(ItemType.STARRED)
|
val starredItems = getMaxItemsForBackground(ItemType.STARRED)
|
||||||
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty())
|
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty())
|
||||||
return newItems
|
return newItems
|
||||||
} catch (e: Throwable) {}
|
} catch (e: Throwable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user