Improve local sync

This commit is contained in:
Ash 2022-03-23 18:50:13 +08:00
parent b2fe0674c8
commit ada579377b
2 changed files with 131 additions and 117 deletions

View File

@ -7,11 +7,13 @@ import androidx.paging.PagingSource
import androidx.work.* import androidx.work.*
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import me.ash.reader.DataStoreKeys import me.ash.reader.DataStoreKeys
import me.ash.reader.data.account.AccountDao import me.ash.reader.data.account.AccountDao
import me.ash.reader.data.article.Article import me.ash.reader.data.article.Article
@ -109,21 +111,23 @@ abstract class AbstractRssRepository constructor(
} }
} }
fun pullImportant( suspend fun pullImportant(
isStarred: Boolean = false, isStarred: Boolean = false,
isUnread: Boolean = false, isUnread: Boolean = false,
): Flow<List<ImportantCount>> { ): Flow<List<ImportantCount>> {
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!! return withContext(Dispatchers.IO) {
Log.i( val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!!
"RLog", Log.i(
"pullImportant: accountId: ${accountId}, isStarred: ${isStarred}, isUnread: ${isUnread}" "RLog",
) "pullImportant: accountId: ${accountId}, isStarred: ${isStarred}, isUnread: ${isUnread}"
return when { )
isStarred -> articleDao when {
.queryImportantCountWhenIsStarred(accountId, isStarred) isStarred -> articleDao
isUnread -> articleDao .queryImportantCountWhenIsStarred(accountId, isStarred)
.queryImportantCountWhenIsUnread(accountId, isUnread) isUnread -> articleDao
else -> articleDao.queryImportantCountWhenIsAll(accountId) .queryImportantCountWhenIsUnread(accountId, isUnread)
else -> articleDao.queryImportantCountWhenIsAll(accountId)
}
} }
} }

View File

@ -12,8 +12,11 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.ContextCompat.getSystemService
import androidx.work.WorkManager import androidx.work.WorkManager
import dagger.hilt.android.qualifiers.ApplicationContext 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.sync.withLock
import kotlinx.coroutines.withContext
import me.ash.reader.* import me.ash.reader.*
import me.ash.reader.data.account.AccountDao import me.ash.reader.data.account.AccountDao
import me.ash.reader.data.article.Article import me.ash.reader.data.article.Article
@ -42,6 +45,22 @@ class LocalRssRepository @Inject constructor(
context, accountDao, articleDao, groupDao, context, accountDao, articleDao, groupDao,
feedDao, rssNetworkDataSource, workManager, 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) { override suspend fun updateArticleInfo(article: Article) {
articleDao.update(article) articleDao.update(article)
} }
@ -68,121 +87,112 @@ class LocalRssRepository @Inject constructor(
override suspend fun sync() { override suspend fun sync() {
mutex.withLock { 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 preTime = System.currentTimeMillis()
val chunked = feeds.chunked(6) withContext(Dispatchers.IO) {
chunked.forEachIndexed { index, item -> val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
item.forEach { ?: return@withContext
Log.i("RlOG", "chunked $index: ${it.name}") 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>>>() articles.awaitAll().sumOf { it.size }.let { count ->
repeat(chunked.size) { Log.i(
flows.add(flow { "RlOG",
val articles = mutableListOf<Article>() "[${count}] onCompletion: ${System.currentTimeMillis() - preTime}"
chunked[it].forEach { feed -> )
val latest = articleDao.queryLatestByFeedId(accountId, feed.id) accountDao.queryById(accountId)?.let { account ->
articles.addAll( accountDao.update(
rssHelper.queryRssXml( account.apply {
rssNetworkDataSource, updateAt = Date()
accountId,
feed,
latest?.title,
).also {
if (feed.icon == null && it.isNotEmpty()) {
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
}
} }
) )
updateSyncState {
it.copy(
feedCount = feeds.size,
syncedCount = it.syncedCount + 1,
currentFeedName = feed.name
)
}
} }
emit(articles) updateSyncState {
}) it.copy(
} feedCount = 0,
combine( syncedCount = 0,
flows currentFeedName = ""
) {
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
) )
)
}
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( private suspend fun syncFeed(
account.apply { accountId: Int,
updateAt = Date() 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, updateSyncState {
syncedCount = 0, it.copy(
currentFeedName = "" 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() )
} }
} }
} }