Improve local sync
This commit is contained in:
parent
b2fe0674c8
commit
ada579377b
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user