Improve local sync
This commit is contained in:
parent
b2fe0674c8
commit
ada579377b
|
@ -7,11 +7,13 @@ import androidx.paging.PagingSource
|
|||
import androidx.work.*
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.DataStoreKeys
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
|
@ -109,21 +111,23 @@ abstract class AbstractRssRepository constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun pullImportant(
|
||||
suspend fun pullImportant(
|
||||
isStarred: Boolean = false,
|
||||
isUnread: Boolean = false,
|
||||
): Flow<List<ImportantCount>> {
|
||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!!
|
||||
Log.i(
|
||||
"RLog",
|
||||
"pullImportant: accountId: ${accountId}, isStarred: ${isStarred}, isUnread: ${isUnread}"
|
||||
)
|
||||
return when {
|
||||
isStarred -> articleDao
|
||||
.queryImportantCountWhenIsStarred(accountId, isStarred)
|
||||
isUnread -> articleDao
|
||||
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
||||
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
||||
return withContext(Dispatchers.IO) {
|
||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!!
|
||||
Log.i(
|
||||
"RLog",
|
||||
"pullImportant: accountId: ${accountId}, isStarred: ${isStarred}, isUnread: ${isUnread}"
|
||||
)
|
||||
when {
|
||||
isStarred -> articleDao
|
||||
.queryImportantCountWhenIsStarred(accountId, isStarred)
|
||||
isUnread -> articleDao
|
||||
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
||||
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,11 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import androidx.work.WorkManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.*
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
|
@ -42,6 +45,22 @@ class LocalRssRepository @Inject constructor(
|
|||
context, accountDao, articleDao, groupDao,
|
||||
feedDao, rssNetworkDataSource, workManager,
|
||||
) {
|
||||
private val notificationManager: NotificationManager =
|
||||
(getSystemService(
|
||||
context,
|
||||
NotificationManager::class.java
|
||||
) as NotificationManager).also {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
it.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
NotificationGroupName.ARTICLE_UPDATE,
|
||||
NotificationGroupName.ARTICLE_UPDATE,
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateArticleInfo(article: Article) {
|
||||
articleDao.update(article)
|
||||
}
|
||||
|
@ -68,121 +87,112 @@ class LocalRssRepository @Inject constructor(
|
|||
|
||||
override suspend fun sync() {
|
||||
mutex.withLock {
|
||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
|
||||
?: return
|
||||
val feeds = feedDao.queryAll(accountId)
|
||||
val feedNotificationMap = mutableMapOf<String, Boolean>()
|
||||
feeds.forEach { feed ->
|
||||
feedNotificationMap[feed.id] = feed.isNotification
|
||||
}
|
||||
val preTime = System.currentTimeMillis()
|
||||
val chunked = feeds.chunked(6)
|
||||
chunked.forEachIndexed { index, item ->
|
||||
item.forEach {
|
||||
Log.i("RlOG", "chunked $index: ${it.name}")
|
||||
withContext(Dispatchers.IO) {
|
||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
|
||||
?: return@withContext
|
||||
val feeds = async { feedDao.queryAll(accountId) }
|
||||
val articles = feeds.await().also { feed ->
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = feed.size,
|
||||
)
|
||||
}
|
||||
}.map { feed ->
|
||||
async {
|
||||
val articles = syncFeed(accountId, feed)
|
||||
articles
|
||||
}
|
||||
}
|
||||
}
|
||||
val flows = mutableListOf<Flow<List<Article>>>()
|
||||
repeat(chunked.size) {
|
||||
flows.add(flow {
|
||||
val articles = mutableListOf<Article>()
|
||||
chunked[it].forEach { feed ->
|
||||
val latest = articleDao.queryLatestByFeedId(accountId, feed.id)
|
||||
articles.addAll(
|
||||
rssHelper.queryRssXml(
|
||||
rssNetworkDataSource,
|
||||
accountId,
|
||||
feed,
|
||||
latest?.title,
|
||||
).also {
|
||||
if (feed.icon == null && it.isNotEmpty()) {
|
||||
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
|
||||
}
|
||||
|
||||
articles.awaitAll().sumOf { it.size }.let { count ->
|
||||
Log.i(
|
||||
"RlOG",
|
||||
"[${count}] onCompletion: ${System.currentTimeMillis() - preTime}"
|
||||
)
|
||||
accountDao.queryById(accountId)?.let { account ->
|
||||
accountDao.update(
|
||||
account.apply {
|
||||
updateAt = Date()
|
||||
}
|
||||
)
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = feeds.size,
|
||||
syncedCount = it.syncedCount + 1,
|
||||
currentFeedName = feed.name
|
||||
)
|
||||
}
|
||||
}
|
||||
emit(articles)
|
||||
})
|
||||
}
|
||||
combine(
|
||||
flows
|
||||
) {
|
||||
val notificationManager: NotificationManager =
|
||||
getSystemService(
|
||||
context,
|
||||
NotificationManager::class.java
|
||||
) as NotificationManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
NotificationGroupName.ARTICLE_UPDATE,
|
||||
"文章更新",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
)
|
||||
)
|
||||
}
|
||||
it.forEach { articleList ->
|
||||
val ids = articleDao.insertList(articleList)
|
||||
articleList.forEachIndexed { index, article ->
|
||||
Log.i("RlOG", "combine ${article.feedId}: ${article.title}")
|
||||
if (feedNotificationMap[article.feedId] == true) {
|
||||
val builder = NotificationCompat.Builder(
|
||||
context,
|
||||
NotificationGroupName.ARTICLE_UPDATE
|
||||
).setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setGroup(NotificationGroupName.ARTICLE_UPDATE)
|
||||
.setContentTitle(article.title)
|
||||
.setContentText(article.shortDescription)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
ids[index].toInt(),
|
||||
Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
putExtra(
|
||||
ExtraName.ARTICLE_ID,
|
||||
ids[index]
|
||||
)
|
||||
},
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
notificationManager.notify(
|
||||
ids[index].toInt(),
|
||||
builder.build().apply {
|
||||
flags = Notification.FLAG_AUTO_CANCEL
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.buffer().onCompletion {
|
||||
val afterTime = System.currentTimeMillis()
|
||||
Log.i("RlOG", "onCompletion: ${afterTime - preTime}")
|
||||
accountDao.queryById(accountId)?.let { account ->
|
||||
accountDao.update(
|
||||
account.apply {
|
||||
updateAt = Date()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun syncFeed(
|
||||
accountId: Int,
|
||||
feed: Feed
|
||||
): MutableList<Article> {
|
||||
val articles = mutableListOf<Article>()
|
||||
val latest = articleDao.queryLatestByFeedId(accountId, feed.id)
|
||||
articles.addAll(
|
||||
rssHelper.queryRssXml(
|
||||
rssNetworkDataSource,
|
||||
accountId,
|
||||
feed,
|
||||
latest?.title,
|
||||
).also {
|
||||
if (feed.icon == null && it.isNotEmpty()) {
|
||||
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
|
||||
}
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
}
|
||||
)
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
syncedCount = it.syncedCount + 1,
|
||||
currentFeedName = feed.name
|
||||
)
|
||||
}
|
||||
articleDao.insertList(articles)
|
||||
if (feed.isNotification) {
|
||||
notify(articles)
|
||||
}
|
||||
return articles
|
||||
}
|
||||
|
||||
private fun notify(
|
||||
articles: MutableList<Article>,
|
||||
) {
|
||||
articles.forEach { article ->
|
||||
val builder = NotificationCompat.Builder(
|
||||
context,
|
||||
NotificationGroupName.ARTICLE_UPDATE
|
||||
).setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setGroup(NotificationGroupName.ARTICLE_UPDATE)
|
||||
.setContentTitle(article.title)
|
||||
.setContentText(article.shortDescription)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
Random().nextInt() + article.id.hashCode(),
|
||||
Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
putExtra(
|
||||
ExtraName.ARTICLE_ID,
|
||||
article.id
|
||||
)
|
||||
},
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
notificationManager.notify(
|
||||
Random().nextInt() + article.id.hashCode(),
|
||||
builder.build().apply {
|
||||
flags = Notification.FLAG_AUTO_CANCEL
|
||||
}
|
||||
}.collect()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user