Refine notification fetching

This commit is contained in:
Matthieu 2021-09-22 22:18:59 +02:00
parent 8f5e4e2317
commit df016a524f
4 changed files with 111 additions and 33 deletions

View File

@ -49,6 +49,7 @@ import org.pixeldroid.app.utils.hasInternet
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.INSTANCE_NOTIFICATION_TAG import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.INSTANCE_NOTIFICATION_TAG
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.SHOW_NOTIFICATION_TAG import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.SHOW_NOTIFICATION_TAG
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.USER_NOTIFICATION_TAG import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.USER_NOTIFICATION_TAG
import org.pixeldroid.app.utils.notificationsWorker.removeNotificationChannelsFromAccount
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
@ -114,7 +115,7 @@ class MainActivity : BaseActivity() {
} }
} }
//Checks if the activity was launched from a notification from another account than the // Checks if the activity was launched from a notification from another account than the
// current active one, and if so switches to that account // current active one, and if so switches to that account
private fun notificationFromOtherUser(): Boolean { private fun notificationFromOtherUser(): Boolean {
val userOfNotification: String? = intent.extras?.getString(USER_NOTIFICATION_TAG) val userOfNotification: String? = intent.extras?.getString(USER_NOTIFICATION_TAG)
@ -214,6 +215,9 @@ class MainActivity : BaseActivity() {
private fun logOut(){ private fun logOut(){
finish() finish()
removeNotificationChannelsFromAccount(applicationContext, user)
db.runInTransaction { db.runInTransaction {
db.userDao().deleteActiveUsers() db.userDao().deleteActiveUsers()

View File

@ -6,6 +6,7 @@ import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.os.Build import android.os.Build
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -25,7 +26,6 @@ import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import java.time.Instant import java.time.Instant
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -48,13 +48,16 @@ class NotificationsWorker(
val users: List<UserDatabaseEntity> = db.userDao().getAll() val users: List<UserDatabaseEntity> = db.userDao().getAll()
for (user in users){ for (user in users){
val channelId = user.instance_uri + user.user_id val uniqueUserId = makeChannelGroupId(user)
createNotificationChannels( val notificationsEnabledForUser = makeNotificationChannels(
"@${user.username}@${user.instance_uri.removePrefix("https://")}", "@${user.username}@${user.instance_uri.removePrefix("https://")}",
channelId uniqueUserId
) )
//if notifications are disabled for this user, move on to next user
if(!notificationsEnabledForUser) continue
// Get newest notification from database // Get newest notification from database
var previouslyLatestNotification: Notification? = db.notificationDao().latestNotification(user.user_id, user.instance_uri) var previouslyLatestNotification: Notification? = db.notificationDao().latestNotification(user.user_id, user.instance_uri)
@ -81,7 +84,7 @@ class NotificationsWorker(
// Launch new notifications // Launch new notifications
filteredNewNotifications.forEach { filteredNewNotifications.forEach {
showNotification(it, user, channelId) showNotification(it, user, uniqueUserId)
} }
previouslyLatestNotification = previouslyLatestNotification =
@ -105,10 +108,8 @@ class NotificationsWorker(
private fun showNotification( private fun showNotification(
notification: Notification, notification: Notification,
user: UserDatabaseEntity, user: UserDatabaseEntity,
channelIdPrefix: String uniqueUserId: String
) { ) {
val channelId = channelIdPrefix + (notification.type ?: "other").toString()
val intent: Intent = when (notification.type) { val intent: Intent = when (notification.type) {
mention -> notification.status?.let { mention -> notification.status?.let {
Intent(applicationContext, PostActivity::class.java).apply { Intent(applicationContext, PostActivity::class.java).apply {
@ -128,7 +129,7 @@ class NotificationsWorker(
.putExtra(INSTANCE_NOTIFICATION_TAG, user.instance_uri) .putExtra(INSTANCE_NOTIFICATION_TAG, user.instance_uri)
val builder = NotificationCompat.Builder(applicationContext, channelId) val builder = NotificationCompat.Builder(applicationContext, makeChannelId(uniqueUserId, notification.type))
.setSmallIcon( .setSmallIcon(
when (notification.type) { when (notification.type) {
follow -> R.drawable.ic_follow follow -> R.drawable.ic_follow
@ -140,6 +141,7 @@ class NotificationsWorker(
null -> R.drawable.ic_comment_empty null -> R.drawable.ic_comment_empty
} }
) )
.setColor(Color.parseColor("#6200EE"))
.setContentTitle( .setContentTitle(
notification.account?.username?.let { username -> notification.account?.username?.let { username ->
applicationContext.getString( applicationContext.getString(
@ -166,39 +168,59 @@ class NotificationsWorker(
builder.setContentText(notification.status?.content) builder.setContentText(notification.status?.content)
} }
builder.setGroup(uniqueUserId)
with(NotificationManagerCompat.from(applicationContext)) { with(NotificationManagerCompat.from(applicationContext)) {
// notificationId is a unique int for each notification // notificationId is a unique int for each notification
notify((user.instance_uri + user.user_id + notification.id).hashCode(), builder.build()) notify((uniqueUserId + notification.id).hashCode(), builder.build())
} }
} }
private fun createNotificationChannels(handle: String, channelId: String) { private fun makeNotificationChannels(handle: String, channelGroupId: String): Boolean {
val notificationManager: NotificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Create the NotificationChannel, but only on API 26+ because // Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library // the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// The id of the group. // The id of the group, hashed (since when creating the group, it may be truncated if too long)
val notificationManager: NotificationManager = val hashedGroupId = channelGroupId.hashCode().toString()
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannelGroup(NotificationChannelGroup(hashedGroupId, handle))
notificationManager.createNotificationChannelGroup(NotificationChannelGroup(channelId, handle))
val importance = NotificationManager.IMPORTANCE_DEFAULT val importance = NotificationManager.IMPORTANCE_DEFAULT
val followsChannel = NotificationChannel(channelId + follow.toString(), "New followers", importance).apply { group = channelId } val channels: List<NotificationChannel> = listOf(
val mentionChannel = NotificationChannel(channelId + mention.toString(), "Mentions", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, follow), applicationContext.getString(R.string.followed_notification_channel), importance),
val sharesChannel = NotificationChannel(channelId + reblog.toString(), "Shares", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, mention), applicationContext.getString(R.string.mention_notification_channel), importance),
val likesChannel = NotificationChannel(channelId + favourite.toString(), "Likes", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, reblog), applicationContext.getString(R.string.shared_notification_channel), importance),
val commentsChannel = NotificationChannel(channelId + comment.toString(), "Comments", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, favourite), applicationContext.getString(R.string.liked_notification_channel), importance),
val pollsChannel = NotificationChannel(channelId + poll.toString(), "Polls", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, comment), applicationContext.getString(R.string.comment_notification_channel), importance),
val othersChannel = NotificationChannel(channelId + "other", "Other", importance).apply { group = channelId } NotificationChannel(makeChannelId(channelGroupId, poll), applicationContext.getString(R.string.poll_notification_channel), importance),
NotificationChannel(makeChannelId(channelGroupId, null), applicationContext.getString(R.string.other_notification_channel), importance),
).map {
it.apply { group = hashedGroupId }
}
// Register the channels with the system // Register the channels with the system
notificationManager.createNotificationChannel(followsChannel) notificationManager.createNotificationChannels(channels)
notificationManager.createNotificationChannel(mentionChannel)
notificationManager.createNotificationChannel(sharesChannel) //Return true if notifications are enabled, false if disabled
notificationManager.createNotificationChannel(likesChannel) return notificationManager.areNotificationsEnabled() and
notificationManager.createNotificationChannel(commentsChannel) (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
notificationManager.createNotificationChannel(pollsChannel) val channelGroup =
notificationManager.createNotificationChannel(othersChannel) notificationManager.getNotificationChannelGroup(hashedGroupId)
!channelGroup.isBlocked
} else true) and
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
!notificationManager.areNotificationsPaused()
} else true) and
!channels.all {
notificationManager.getNotificationChannel(it.id).importance <= NotificationManager.IMPORTANCE_NONE
}
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
notificationManager.areNotificationsEnabled()
} else {
true
} }
} }
@ -206,6 +228,39 @@ class NotificationsWorker(
const val SHOW_NOTIFICATION_TAG = "org.pixeldroid.app.SHOW_NOTIFICATION" const val SHOW_NOTIFICATION_TAG = "org.pixeldroid.app.SHOW_NOTIFICATION"
const val INSTANCE_NOTIFICATION_TAG = "org.pixeldroid.app.USER_NOTIFICATION" const val INSTANCE_NOTIFICATION_TAG = "org.pixeldroid.app.USER_NOTIFICATION"
const val USER_NOTIFICATION_TAG = "org.pixeldroid.app.INSTANCE_NOTIFICATION" const val USER_NOTIFICATION_TAG = "org.pixeldroid.app.INSTANCE_NOTIFICATION"
const val otherNotificationType = "other"
} }
}
/**
* [channelGroupId] is the id used to uniquely identify the group: for us it is a unique id
* identifying a user consisting of the concatenation of the instance uri and user id.
*/
private fun makeChannelId(channelGroupId: String, type: Notification.NotificationType?): String =
(channelGroupId + (type ?: NotificationsWorker.otherNotificationType)).hashCode().toString()
private fun makeChannelGroupId(user: UserDatabaseEntity) = user.instance_uri + user.user_id
fun removeNotificationChannelsFromAccount(context: Context, user: UserDatabaseEntity?) = user?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelGroupId = makeChannelGroupId(user)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
notificationManager.deleteNotificationChannelGroup(channelGroupId.hashCode().toString())
} else {
val types: MutableList<Notification.NotificationType?> =
Notification.NotificationType.values().toMutableList()
types += null
types.forEach {
notificationManager.deleteNotificationChannel(makeChannelId(channelGroupId, it))
}
}
}
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/colorDrawing"
android:pathData="M7.58,4.08L6.15,2.65C3.75,4.48 2.17,7.3 2.03,10.5h2c0.15,-2.65 1.51,-4.97 3.55,-6.42zM19.97,10.5h2c-0.15,-3.2 -1.73,-6.02 -4.12,-7.85l-1.42,1.43c2.02,1.45 3.39,3.77 3.54,6.42zM18,11c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2v-5zM12,22c0.14,0 0.27,-0.01 0.4,-0.04 0.65,-0.14 1.18,-0.58 1.44,-1.18 0.1,-0.24 0.15,-0.5 0.15,-0.78h-4c0.01,1.1 0.9,2 2.01,2z"/>
</vector>

View File

@ -47,15 +47,25 @@
<!-- Notifications: like (favourite) notification --> <!-- Notifications: like (favourite) notification -->
<string name="liked_notification">%1$s liked your post</string> <string name="liked_notification">%1$s liked your post</string>
<!-- Notifications: end of poll notification -->
<string name="poll_notification">"%1$s's poll has ended"</string>
<!-- Notifications: comment notification --> <!-- Notifications: comment notification -->
<string name="comment_notification">%1$s commented on your post</string> <string name="comment_notification">%1$s commented on your post</string>
<!-- Notifications: end of poll notification -->
<string name="poll_notification">"%1$s's poll has ended"</string>
<!-- Notifications: other notification --> <!-- Notifications: other notification -->
<string name="other_notification">"Notification from %1$s"</string> <string name="other_notification">"Notification from %1$s"</string>
<string name="followed_notification_channel">"New followers"</string>
<string name="mention_notification_channel">"Mentions"</string>
<string name="shared_notification_channel">"Shares"</string>
<string name="liked_notification_channel">"Likes"</string>
<string name="comment_notification_channel">"Comments"</string>
<string name="poll_notification_channel">"Polls"</string>
<string name="other_notification_channel">"Other"</string>
<!-- Login page --> <!-- Login page -->
<string name="whats_an_instance">"What's an instance?"</string> <string name="whats_an_instance">"What's an instance?"</string>
<string name="whats_an_instance_explanation">"You might be confused by the text field asking for the domain of your 'instance'. <string name="whats_an_instance_explanation">"You might be confused by the text field asking for the domain of your 'instance'.