Merge notifications by feeds
This commit is contained in:
parent
ee5e6e3687
commit
72b07a7e0a
|
@ -562,7 +562,7 @@ interface ArticleDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
suspend fun insertIfNotExist(articles: List<Article>): List<Article?> {
|
suspend fun insertIfNotExist(articles: List<Article>): List<Article> {
|
||||||
return articles.map { if (insertIfNotExist(it) > 0) it else null }
|
return articles.mapNotNull { if (insertIfNotExist(it) > 0) it else null }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
package me.ash.reader.data.repository
|
package me.ash.reader.data.repository
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.*
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ListenableWorker
|
import androidx.work.ListenableWorker
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
@ -25,6 +23,7 @@ import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.dao.GroupDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.entity.Article
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.entity.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
|
import me.ash.reader.data.entity.FeedWithArticle
|
||||||
import me.ash.reader.data.entity.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.module.DispatcherDefault
|
import me.ash.reader.data.module.DispatcherDefault
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
|
@ -56,12 +55,9 @@ class LocalRssRepository @Inject constructor(
|
||||||
feedDao, rssNetworkDataSource, workManager,
|
feedDao, rssNetworkDataSource, workManager,
|
||||||
dispatcherIO
|
dispatcherIO
|
||||||
) {
|
) {
|
||||||
private val notificationManager: NotificationManager =
|
private val notificationManager: NotificationManagerCompat =
|
||||||
(getSystemService(
|
NotificationManagerCompat.from(context).apply {
|
||||||
context,
|
createNotificationChannel(
|
||||||
NotificationManager::class.java
|
|
||||||
) as NotificationManager).also {
|
|
||||||
it.createNotificationChannel(
|
|
||||||
NotificationChannel(
|
NotificationChannel(
|
||||||
NotificationGroupName.ARTICLE_UPDATE,
|
NotificationGroupName.ARTICLE_UPDATE,
|
||||||
NotificationGroupName.ARTICLE_UPDATE,
|
NotificationGroupName.ARTICLE_UPDATE,
|
||||||
|
@ -105,9 +101,14 @@ class LocalRssRepository @Inject constructor(
|
||||||
.awaitAll()
|
.awaitAll()
|
||||||
.forEach {
|
.forEach {
|
||||||
if (it.isNotify) {
|
if (it.isNotify) {
|
||||||
notify(articleDao.insertIfNotExist(it.articles))
|
notify(
|
||||||
|
FeedWithArticle(
|
||||||
|
it.feedWithArticle.feed,
|
||||||
|
articleDao.insertIfNotExist(it.feedWithArticle.articles)
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
articleDao.insertIfNotExist(it.articles)
|
articleDao.insertIfNotExist(it.feedWithArticle.articles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i("RlOG", "onCompletion: ${System.currentTimeMillis() - preTime}")
|
Log.i("RlOG", "onCompletion: ${System.currentTimeMillis() - preTime}")
|
||||||
|
@ -158,7 +159,7 @@ class LocalRssRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ArticleNotify(
|
data class ArticleNotify(
|
||||||
val articles: List<Article>,
|
val feedWithArticle: FeedWithArticle,
|
||||||
val isNotify: Boolean,
|
val isNotify: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ class LocalRssRepository @Inject constructor(
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
Log.e("RLog", "queryRssXml[${feed.name}]: ${e.message}")
|
Log.e("RLog", "queryRssXml[${feed.name}]: ${e.message}")
|
||||||
return ArticleNotify(listOf(), false)
|
return ArticleNotify(FeedWithArticle(feed, listOf()), false)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// if (feed.icon == null && !articles.isNullOrEmpty()) {
|
// if (feed.icon == null && !articles.isNullOrEmpty()) {
|
||||||
|
@ -178,32 +179,33 @@ class LocalRssRepository @Inject constructor(
|
||||||
// }
|
// }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("RLog", "queryRssIcon[${feed.name}]: ${e.message}")
|
Log.e("RLog", "queryRssIcon[${feed.name}]: ${e.message}")
|
||||||
return ArticleNotify(listOf(), false)
|
return ArticleNotify(FeedWithArticle(feed, listOf()), false)
|
||||||
}
|
}
|
||||||
return ArticleNotify(
|
return ArticleNotify(
|
||||||
articles = articles,
|
feedWithArticle = FeedWithArticle(feed, articles),
|
||||||
isNotify = articles.isNotEmpty() && feed.isNotification
|
isNotify = articles.isNotEmpty() && feed.isNotification
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(
|
private fun notify(
|
||||||
articles: List<Article?>,
|
feedWithArticle: FeedWithArticle,
|
||||||
) {
|
) {
|
||||||
articles.filterNotNull().forEach { article ->
|
notificationManager.createNotificationChannelGroup(
|
||||||
val builder = NotificationCompat.Builder(
|
NotificationChannelGroup(
|
||||||
context,
|
feedWithArticle.feed.id,
|
||||||
NotificationGroupName.ARTICLE_UPDATE
|
feedWithArticle.feed.name
|
||||||
).setSmallIcon(R.drawable.ic_notification)
|
)
|
||||||
// .setLargeIcon(
|
)
|
||||||
// BitmapFactory.decodeResource(
|
feedWithArticle.articles.forEach { article ->
|
||||||
// context.resources,
|
val builder = NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
|
||||||
// R.mipmap.ic_launcher_round,
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
// )
|
.setLargeIcon(
|
||||||
// )
|
(BitmapFactory.decodeResource(
|
||||||
.setGroup(NotificationGroupName.ARTICLE_UPDATE)
|
context.resources,
|
||||||
|
R.drawable.ic_notification
|
||||||
|
))
|
||||||
|
)
|
||||||
.setContentTitle(article.title)
|
.setContentTitle(article.title)
|
||||||
.setContentText(article.shortDescription)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
||||||
.setContentIntent(
|
.setContentIntent(
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
|
@ -219,6 +221,13 @@ class LocalRssRepository @Inject constructor(
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.setGroup(feedWithArticle.feed.id)
|
||||||
|
.setStyle(
|
||||||
|
NotificationCompat.BigTextStyle()
|
||||||
|
.bigText(article.shortDescription)
|
||||||
|
.setSummaryText(feedWithArticle.feed.name)
|
||||||
|
)
|
||||||
|
|
||||||
notificationManager.notify(
|
notificationManager.notify(
|
||||||
Random().nextInt() + article.id.hashCode(),
|
Random().nextInt() + article.id.hashCode(),
|
||||||
builder.build().apply {
|
builder.build().apply {
|
||||||
|
@ -226,5 +235,26 @@ class LocalRssRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (feedWithArticle.articles.size > 1) {
|
||||||
|
notificationManager.notify(
|
||||||
|
Random().nextInt() + feedWithArticle.feed.id.hashCode(),
|
||||||
|
NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setLargeIcon(
|
||||||
|
(BitmapFactory.decodeResource(
|
||||||
|
context.resources,
|
||||||
|
R.drawable.ic_notification
|
||||||
|
))
|
||||||
|
)
|
||||||
|
.setStyle(
|
||||||
|
NotificationCompat.InboxStyle()
|
||||||
|
.setSummaryText(feedWithArticle.feed.name)
|
||||||
|
)
|
||||||
|
.setGroup(feedWithArticle.feed.id)
|
||||||
|
.setGroupSummary(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -163,7 +163,7 @@ class RssHelper @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseDate(
|
private fun parseDate(
|
||||||
inputDate: String, patterns: Array<String?> = arrayOf(
|
inputDate: String, patterns: Array<String> = arrayOf(
|
||||||
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||||
"yyyy-MM-dd",
|
"yyyy-MM-dd",
|
||||||
"yyyy-MM-dd HH:mm:ss",
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
@ -174,7 +174,6 @@ class RssHelper @Inject constructor(
|
||||||
)
|
)
|
||||||
): Date? {
|
): Date? {
|
||||||
val df = SimpleDateFormat()
|
val df = SimpleDateFormat()
|
||||||
df.timeZone = TimeZone.getDefault()
|
|
||||||
for (pattern in patterns) {
|
for (pattern in patterns) {
|
||||||
df.applyPattern(pattern)
|
df.applyPattern(pattern)
|
||||||
df.isLenient = false
|
df.isLenient = false
|
||||||
|
|
|
@ -10,8 +10,6 @@ package me.ash.reader.ui.component
|
||||||
|
|
||||||
import android.view.SoundEffectConstants
|
import android.view.SoundEffectConstants
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
@ -86,7 +84,7 @@ fun Banner(
|
||||||
)
|
)
|
||||||
desc?.let {
|
desc?.let {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.animateContentSize(tween()),
|
// modifier = Modifier.animateContentSize(tween()),
|
||||||
text = it,
|
text = it,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = (MaterialTheme.colorScheme.onSurface alwaysLight true).copy(alpha = 0.7f),
|
color = (MaterialTheme.colorScheme.onSurface alwaysLight true).copy(alpha = 0.7f),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package me.ash.reader.ui.component
|
package me.ash.reader.ui.component
|
||||||
|
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
@ -32,8 +31,8 @@ fun DisplayText(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(44.dp)
|
.height(44.dp),
|
||||||
.animateContentSize(tween()),
|
// .animateContentSize(tween()),
|
||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.displaySmall.copy(
|
style = MaterialTheme.typography.displaySmall.copy(
|
||||||
baselineShift = BaselineShift.Superscript
|
baselineShift = BaselineShift.Superscript
|
||||||
|
|
|
@ -78,7 +78,7 @@ fun HomeEntry(
|
||||||
FlowPage(
|
FlowPage(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
homeViewModel = homeViewModel,
|
homeViewModel = homeViewModel,
|
||||||
pagingItems = pagingItems
|
pagingItems = pagingItems,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
animatedComposable(route = "${RouteName.READING}/{articleId}") {
|
animatedComposable(route = "${RouteName.READING}/{articleId}") {
|
||||||
|
|
|
@ -166,7 +166,7 @@ fun FeedsPage(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
text = feedsViewState.account?.name ?: "",
|
text = feedsViewState.account?.name ?: stringResource(R.string.read_you),
|
||||||
desc = if (isSyncing) stringResource(R.string.syncing) else "",
|
desc = if (isSyncing) stringResource(R.string.syncing) else "",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package me.ash.reader.ui.page.home.flow
|
package me.ash.reader.ui.page.home.flow
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
@ -89,7 +88,6 @@ fun FlowPage(
|
||||||
|
|
||||||
LaunchedEffect(viewState.listState) {
|
LaunchedEffect(viewState.listState) {
|
||||||
snapshotFlow { viewState.listState.firstVisibleItemIndex }.collect {
|
snapshotFlow { viewState.listState.firstVisibleItemIndex }.collect {
|
||||||
Log.i("RLog", "FlowPage: ${it}")
|
|
||||||
if (it > 0) {
|
if (it > 0) {
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user