mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 01:55:04 +08:00
Merge pull request #566 from vector-im/feature/redact_notification
Redact notification
This commit is contained in:
commit
c728834273
@ -7,6 +7,7 @@ Features:
|
||||
Improvements:
|
||||
- Add unread indent on room list (#485)
|
||||
- Message Editing: Update notifications (#128)
|
||||
- Remove any notification of a redacted event (#563)
|
||||
|
||||
Other changes:
|
||||
-
|
||||
|
@ -43,6 +43,7 @@ interface PushRuleService {
|
||||
interface PushRuleListener {
|
||||
fun onMatchRule(event: Event, actions: List<Action>)
|
||||
fun onRoomLeft(roomId: String)
|
||||
fun onEventRedacted(redactedEventId: String)
|
||||
fun batchFinish()
|
||||
}
|
||||
}
|
@ -132,6 +132,16 @@ internal class DefaultPushRuleService @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatchRedactedEventId(redactedEventId: String) {
|
||||
try {
|
||||
listeners.forEach {
|
||||
it.onEventRedacted(redactedEventId)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Error while dispatching room left")
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatchFinish() {
|
||||
try {
|
||||
listeners.forEach {
|
||||
|
@ -78,6 +78,25 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||
defaultPushRuleService.dispatchBing(event, it)
|
||||
}
|
||||
}
|
||||
|
||||
val allRedactedEvents = params.syncResponse.join
|
||||
.map { entries ->
|
||||
entries.value.timeline?.events?.filter {
|
||||
it.type == EventType.REDACTION
|
||||
}
|
||||
.orEmpty()
|
||||
.mapNotNull { it.redacts }
|
||||
}
|
||||
.fold(emptyList<String>(), { acc, next ->
|
||||
acc + next
|
||||
})
|
||||
|
||||
Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events")
|
||||
|
||||
allRedactedEvents.forEach { redactedEventId ->
|
||||
defaultPushRuleService.dispatchRedactedEventId(redactedEventId)
|
||||
}
|
||||
|
||||
defaultPushRuleService.dispatchFinish()
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
// This ID can and should be used to detect duplicate notification requests.
|
||||
val eventId = data["event_id"] ?: return //Just ignore
|
||||
|
||||
|
||||
val eventType = data["type"]
|
||||
if (eventType == null) {
|
||||
//Just add a generic unknown event
|
||||
@ -214,10 +213,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
)
|
||||
notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent)
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
|
||||
return
|
||||
} else {
|
||||
|
||||
val event = parseEvent(data) ?: return
|
||||
|
||||
val notifiableEvent = notifiableEventResolver.resolveEvent(event, session)
|
||||
@ -228,8 +224,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
Timber.e("--> ${event}")
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
if (notifiableEvent is NotifiableMessageEvent) {
|
||||
if (TextUtils.isEmpty(notifiableEvent.senderName)) {
|
||||
notifiableEvent.senderName = data["sender_display_name"]
|
||||
@ -246,7 +240,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun findRoomNameBestEffort(data: Map<String, String>, session: Session?): String? {
|
||||
|
@ -48,7 +48,6 @@ import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
||||
import im.vector.riotx.features.rageshake.VectorFileLogger
|
||||
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import im.vector.riotx.features.version.VersionProvider
|
||||
@ -73,6 +72,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
||||
@Inject lateinit var pushRuleTriggerListener: PushRuleTriggerListener
|
||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||
@Inject lateinit var versionProvider: VersionProvider
|
||||
@Inject lateinit var notificationUtils: NotificationUtils
|
||||
lateinit var vectorComponent: VectorComponent
|
||||
private var fontThreadHandler: Handler? = null
|
||||
|
||||
@ -112,7 +112,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
||||
|
||||
emojiCompatWrapper.init(fontRequest)
|
||||
|
||||
NotificationUtils.createNotificationChannels(applicationContext)
|
||||
notificationUtils.createNotificationChannels()
|
||||
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
||||
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
|
||||
activeSessionHolder.setActiveSession(lastAuthenticatedSession)
|
||||
|
@ -36,10 +36,7 @@ import im.vector.riotx.features.home.HomeRoomListObservableStore
|
||||
import im.vector.riotx.features.home.group.SelectedGroupStore
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.notifications.NotifiableEventResolver
|
||||
import im.vector.riotx.features.notifications.NotificationBroadcastReceiver
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
||||
import im.vector.riotx.features.notifications.*
|
||||
import im.vector.riotx.features.rageshake.BugReporter
|
||||
import im.vector.riotx.features.rageshake.VectorFileLogger
|
||||
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||
@ -58,6 +55,8 @@ interface VectorComponent {
|
||||
|
||||
fun currentSession(): Session
|
||||
|
||||
fun notificationUtils(): NotificationUtils
|
||||
|
||||
fun notificationDrawerManager(): NotificationDrawerManager
|
||||
|
||||
fun appContext(): Context
|
||||
@ -72,7 +71,7 @@ interface VectorComponent {
|
||||
|
||||
fun emojiCompatFontProvider(): EmojiCompatFontProvider
|
||||
|
||||
fun emojiCompatWrapper() : EmojiCompatWrapper
|
||||
fun emojiCompatWrapper(): EmojiCompatWrapper
|
||||
|
||||
fun eventHtmlRenderer(): EventHtmlRenderer
|
||||
|
||||
|
@ -19,6 +19,7 @@ package im.vector.riotx.core.services
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.riotx.core.extensions.vectorComponent
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import timber.log.Timber
|
||||
|
||||
@ -32,11 +33,18 @@ class CallService : VectorService() {
|
||||
*/
|
||||
private var mCallIdInProgress: String? = null
|
||||
|
||||
private lateinit var notificationUtils: NotificationUtils
|
||||
|
||||
/**
|
||||
* incoming (foreground notification)
|
||||
*/
|
||||
private var mIncomingCallId: String? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
notificationUtils = vectorComponent().notificationUtils()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (intent == null) {
|
||||
// Service started again by the system.
|
||||
@ -120,7 +128,7 @@ class CallService : VectorService() {
|
||||
private fun displayCallInProgressNotification(intent: Intent) {
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID)
|
||||
|
||||
val notification = NotificationUtils.buildPendingCallNotification(applicationContext,
|
||||
val notification = notificationUtils.buildPendingCallNotification(
|
||||
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
|
||||
intent.getStringExtra(EXTRA_ROOM_NAME),
|
||||
intent.getStringExtra(EXTRA_ROOM_ID),
|
||||
@ -136,7 +144,7 @@ class CallService : VectorService() {
|
||||
* Hide the permanent call notifications
|
||||
*/
|
||||
private fun hideCallNotifications() {
|
||||
val notification = NotificationUtils.buildCallEndedNotification(applicationContext)
|
||||
val notification = notificationUtils.buildCallEndedNotification()
|
||||
|
||||
// It's mandatory to startForeground to avoid crash
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
@ -18,11 +18,7 @@ package im.vector.riotx.core.utils
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
@ -32,7 +28,7 @@ import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.notifications.supportNotificationChannels
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
@ -138,7 +134,7 @@ fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: In
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String) {
|
||||
if (!supportNotificationChannels()) return
|
||||
if (!NotificationUtils.supportNotificationChannels()) return
|
||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
|
||||
putExtra(Settings.EXTRA_APP_PACKAGE, fragment.context?.packageName)
|
||||
putExtra(Settings.EXTRA_CHANNEL_ID, channelID)
|
||||
|
@ -32,6 +32,7 @@ data class InviteNotifiableEvent(
|
||||
override var isPushGatewayEvent: Boolean = false) : NotifiableEvent {
|
||||
|
||||
override var hasBeenDisplayed: Boolean = false
|
||||
override var isRedacted: Boolean = false
|
||||
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
|
||||
|
||||
}
|
@ -31,6 +31,7 @@ interface NotifiableEvent : Serializable {
|
||||
// Compat: Only for android <7, for newer version the sound is defined in the channel
|
||||
var soundName: String?
|
||||
var hasBeenDisplayed: Boolean
|
||||
var isRedacted: Boolean
|
||||
//Used to know if event should be replaced with the one coming from eventstream
|
||||
var isPushGatewayEvent: Boolean
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ data class NotifiableMessageEvent(
|
||||
override var soundName: String? = null
|
||||
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
|
||||
override var hasBeenDisplayed: Boolean = false
|
||||
override var isRedacted: Boolean = false
|
||||
|
||||
var roomAvatarPath: String? = null
|
||||
var senderAvatarPath: String? = null
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package im.vector.riotx.features.notifications
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Handler
|
||||
@ -27,6 +26,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import me.gujun.android.span.span
|
||||
import timber.log.Timber
|
||||
@ -43,7 +43,9 @@ import javax.inject.Singleton
|
||||
*/
|
||||
@Singleton
|
||||
class NotificationDrawerManager @Inject constructor(private val context: Context,
|
||||
private val notificationUtils: NotificationUtils,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val stringProvider: StringProvider,
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val iconLoader: IconLoader,
|
||||
private val bitmapLoader: BitmapLoader,
|
||||
@ -96,7 +98,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
notifiableEvent.noisy = false
|
||||
eventList.remove(existing)
|
||||
eventList.add(notifiableEvent)
|
||||
|
||||
} else {
|
||||
//keep the existing one, do not replace
|
||||
}
|
||||
@ -127,6 +128,15 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
}
|
||||
}
|
||||
|
||||
fun onEventRedacted(eventId: String) {
|
||||
synchronized(eventList) {
|
||||
eventList.filter { it.eventId == eventId }.map { notifiableEvent ->
|
||||
notifiableEvent.isRedacted = true
|
||||
notifiableEvent.hasBeenDisplayed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Clear all known events and refresh the notification drawer
|
||||
*/
|
||||
@ -147,7 +157,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
e is NotifiableMessageEvent && e.roomId == roomId
|
||||
}
|
||||
}
|
||||
NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
||||
notificationUtils.cancelNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
||||
}
|
||||
refreshNotificationDrawer()
|
||||
}
|
||||
@ -215,20 +225,15 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
val summaryInboxStyle = NotificationCompat.InboxStyle()
|
||||
|
||||
//group events by room to create a single MessagingStyle notif
|
||||
val roomIdToEventMap: MutableMap<String, ArrayList<NotifiableMessageEvent>> = HashMap()
|
||||
val simpleEvents: ArrayList<NotifiableEvent> = ArrayList()
|
||||
val notifications: ArrayList<Notification> = ArrayList()
|
||||
val roomIdToEventMap: MutableMap<String, MutableList<NotifiableMessageEvent>> = LinkedHashMap()
|
||||
val simpleEvents: MutableList<NotifiableEvent> = ArrayList()
|
||||
|
||||
val eventIterator = eventList.listIterator()
|
||||
while (eventIterator.hasNext()) {
|
||||
val event = eventIterator.next()
|
||||
if (event is NotifiableMessageEvent) {
|
||||
val roomId = event.roomId
|
||||
var roomEvents = roomIdToEventMap[roomId]
|
||||
if (roomEvents == null) {
|
||||
roomEvents = ArrayList()
|
||||
roomIdToEventMap[roomId] = roomEvents
|
||||
}
|
||||
val roomEvents = roomIdToEventMap.getOrPut(roomId) { ArrayList() }
|
||||
|
||||
if (shouldIgnoreMessageEventInRoom(roomId) || outdatedDetector?.isMessageOutdated(event) == true) {
|
||||
//forget this event
|
||||
@ -246,13 +251,13 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
|
||||
var globalLastMessageTimestamp = 0L
|
||||
|
||||
//events have been grouped
|
||||
//events have been grouped by roomId
|
||||
for ((roomId, events) in roomIdToEventMap) {
|
||||
|
||||
if (events.isEmpty()) {
|
||||
// Build the notification for the room
|
||||
if (events.isEmpty() || events.all { it.isRedacted }) {
|
||||
//Just clear this notification
|
||||
Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId has no more events")
|
||||
NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
||||
notificationUtils.cancelNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -280,7 +285,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
|
||||
for (event in events) {
|
||||
//if all events in this room have already been displayed there is no need to update it
|
||||
if (!event.hasBeenDisplayed) {
|
||||
if (!event.hasBeenDisplayed && !event.isRedacted) {
|
||||
roomEventGroupInfo.shouldBing = roomEventGroupInfo.shouldBing || event.noisy
|
||||
roomEventGroupInfo.customSound = event.soundName
|
||||
}
|
||||
@ -293,16 +298,18 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
.build()
|
||||
|
||||
if (event.outGoingMessage && event.outGoingMessageFailed) {
|
||||
style.addMessage(context.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson)
|
||||
style.addMessage(stringProvider.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson)
|
||||
roomEventGroupInfo.hasSmartReplyError = true
|
||||
} else {
|
||||
style.addMessage(event.body, event.timestamp, senderPerson)
|
||||
if (!event.isRedacted) {
|
||||
style.addMessage(event.body, event.timestamp, senderPerson)
|
||||
}
|
||||
}
|
||||
event.hasBeenDisplayed = true //we can consider it as displayed
|
||||
|
||||
//It is possible that this event was previously shown as an 'anonymous' simple notif.
|
||||
//And now it will be merged in a single MessageStyle notif, so we can clean to be sure
|
||||
NotificationUtils.cancelNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID)
|
||||
notificationUtils.cancelNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
try {
|
||||
@ -328,7 +335,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
summaryInboxStyle.addLine(line)
|
||||
}
|
||||
} else {
|
||||
val summaryLine = context.resources.getQuantityString(
|
||||
val summaryLine = stringProvider.getQuantityString(
|
||||
R.plurals.notification_compat_summary_line_for_room, events.size, roomName, events.size)
|
||||
summaryInboxStyle.addLine(summaryLine)
|
||||
}
|
||||
@ -347,18 +354,16 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
globalLastMessageTimestamp = lastMessageTimestamp
|
||||
}
|
||||
|
||||
NotificationUtils.buildMessagesListNotification(context,
|
||||
vectorPreferences,
|
||||
val notification = notificationUtils.buildMessagesListNotification(
|
||||
style,
|
||||
roomEventGroupInfo,
|
||||
largeBitmap,
|
||||
lastMessageTimestamp,
|
||||
myUserDisplayName)
|
||||
?.let {
|
||||
//is there an id for this room?
|
||||
notifications.add(it)
|
||||
NotificationUtils.showNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID, it)
|
||||
}
|
||||
|
||||
//is there an id for this room?
|
||||
notificationUtils.showNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID, notification)
|
||||
|
||||
hasNewEvent = true
|
||||
summaryIsNoisy = summaryIsNoisy || roomEventGroupInfo.shouldBing
|
||||
} else {
|
||||
@ -371,14 +376,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
for (event in simpleEvents) {
|
||||
//We build a simple event
|
||||
if (firstTime || !event.hasBeenDisplayed) {
|
||||
NotificationUtils.buildSimpleEventNotification(context, vectorPreferences, event, null, session.myUserId)?.let {
|
||||
notifications.add(it)
|
||||
NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it)
|
||||
event.hasBeenDisplayed = true //we can consider it as displayed
|
||||
hasNewEvent = true
|
||||
summaryIsNoisy = summaryIsNoisy || event.noisy
|
||||
summaryInboxStyle.addLine(event.description)
|
||||
}
|
||||
val notification = notificationUtils.buildSimpleEventNotification(event, null, session.myUserId)
|
||||
notificationUtils.showNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID, notification)
|
||||
event.hasBeenDisplayed = true //we can consider it as displayed
|
||||
hasNewEvent = true
|
||||
summaryIsNoisy = summaryIsNoisy || event.noisy
|
||||
summaryInboxStyle.addLine(event.description)
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,28 +399,22 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
// To ensure the best experience on all devices and versions, always include a group summary when you create a group
|
||||
// https://developer.android.com/training/notify-user/group
|
||||
|
||||
if (eventList.isEmpty()) {
|
||||
NotificationUtils.cancelNotificationMessage(context, null, SUMMARY_NOTIFICATION_ID)
|
||||
if (eventList.isEmpty() || eventList.all { it.isRedacted }) {
|
||||
notificationUtils.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID)
|
||||
} else {
|
||||
val nbEvents = roomIdToEventMap.size + simpleEvents.size
|
||||
val sumTitle = context.resources.getQuantityString(
|
||||
R.plurals.notification_compat_summary_title, nbEvents, nbEvents)
|
||||
val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents)
|
||||
summaryInboxStyle.setBigContentTitle(sumTitle)
|
||||
//TODO get latest event?
|
||||
.setSummaryText(
|
||||
context.resources
|
||||
.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
|
||||
.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
|
||||
|
||||
NotificationUtils.buildSummaryListNotification(
|
||||
context,
|
||||
vectorPreferences,
|
||||
val notification = notificationUtils.buildSummaryListNotification(
|
||||
summaryInboxStyle,
|
||||
sumTitle,
|
||||
noisy = hasNewEvent && summaryIsNoisy,
|
||||
lastMessageTimestamp = globalLastMessageTimestamp
|
||||
)?.let {
|
||||
NotificationUtils.showNotificationMessage(context, null, SUMMARY_NOTIFICATION_ID, it)
|
||||
}
|
||||
lastMessageTimestamp = globalLastMessageTimestamp)
|
||||
|
||||
notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification)
|
||||
|
||||
if (hasNewEvent && summaryIsNoisy) {
|
||||
try {
|
||||
@ -443,12 +440,11 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRoomBitmap(events: ArrayList<NotifiableMessageEvent>): Bitmap? {
|
||||
private fun getRoomBitmap(events: List<NotifiableMessageEvent>): Bitmap? {
|
||||
if (events.isEmpty()) return null
|
||||
|
||||
//Use the last event (most recent?)
|
||||
val roomAvatarPath = events.last().roomAvatarPath
|
||||
?: events.last().senderAvatarPath
|
||||
val roomAvatarPath = events.last().roomAvatarPath ?: events.last().senderAvatarPath
|
||||
|
||||
return bitmapLoader.getRoomBitmap(roomAvatarPath)
|
||||
}
|
||||
@ -461,7 +457,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
fun persistInfo() {
|
||||
synchronized(eventList) {
|
||||
if (eventList.isEmpty()) {
|
||||
deleteCachedRoomNotifications(context)
|
||||
deleteCachedRoomNotifications()
|
||||
return
|
||||
}
|
||||
try {
|
||||
@ -476,14 +472,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadEventInfo(): ArrayList<NotifiableEvent> {
|
||||
private fun loadEventInfo(): MutableList<NotifiableEvent> {
|
||||
try {
|
||||
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
||||
if (file.exists()) {
|
||||
FileInputStream(file).use {
|
||||
val events: ArrayList<NotifiableEvent>? = activeSessionHolder.getSafeActiveSession()?.loadSecureSecret(it, KEY_ALIAS_SECRET_STORAGE)
|
||||
if (events != null) {
|
||||
return ArrayList(events.mapNotNull { it as? NotifiableEvent })
|
||||
return events.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -493,7 +489,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
private fun deleteCachedRoomNotifications(context: Context) {
|
||||
private fun deleteCachedRoomNotifications() {
|
||||
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
|
@ -27,7 +27,6 @@ import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
@ -37,6 +36,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.utils.startNotificationChannelSettingsIntent
|
||||
import im.vector.riotx.features.home.HomeActivity
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
||||
@ -44,51 +44,73 @@ import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
|
||||
/**
|
||||
* Util class for creating notifications.
|
||||
* Note: Cannot inject ColorProvider in the constructor, because it requires an Activity
|
||||
*/
|
||||
object NotificationUtils {
|
||||
@Singleton
|
||||
class NotificationUtils @Inject constructor(private val context: Context,
|
||||
private val stringProvider: StringProvider,
|
||||
private val vectorPreferences: VectorPreferences) {
|
||||
|
||||
/* ==========================================================================================
|
||||
* IDs for notifications
|
||||
* ========================================================================================== */
|
||||
companion object {
|
||||
/* ==========================================================================================
|
||||
* IDs for notifications
|
||||
* ========================================================================================== */
|
||||
|
||||
/**
|
||||
* Identifier of the foreground notification used to keep the application alive
|
||||
* when it runs in background.
|
||||
* This notification, which is not removable by the end user, displays what
|
||||
* the application is doing while in background.
|
||||
*/
|
||||
const val NOTIFICATION_ID_FOREGROUND_SERVICE = 61
|
||||
/**
|
||||
* Identifier of the foreground notification used to keep the application alive
|
||||
* when it runs in background.
|
||||
* This notification, which is not removable by the end user, displays what
|
||||
* the application is doing while in background.
|
||||
*/
|
||||
const val NOTIFICATION_ID_FOREGROUND_SERVICE = 61
|
||||
|
||||
/* ==========================================================================================
|
||||
* IDs for actions
|
||||
* ========================================================================================== */
|
||||
/* ==========================================================================================
|
||||
* IDs for actions
|
||||
* ========================================================================================== */
|
||||
|
||||
const val JOIN_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.JOIN_ACTION"
|
||||
const val REJECT_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.REJECT_ACTION"
|
||||
private const val QUICK_LAUNCH_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.QUICK_LAUNCH_ACTION"
|
||||
const val MARK_ROOM_READ_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.MARK_ROOM_READ_ACTION"
|
||||
const val SMART_REPLY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.SMART_REPLY_ACTION"
|
||||
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
|
||||
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
|
||||
private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
||||
const val JOIN_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.JOIN_ACTION"
|
||||
const val REJECT_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.REJECT_ACTION"
|
||||
private const val QUICK_LAUNCH_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.QUICK_LAUNCH_ACTION"
|
||||
const val MARK_ROOM_READ_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.MARK_ROOM_READ_ACTION"
|
||||
const val SMART_REPLY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.SMART_REPLY_ACTION"
|
||||
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
|
||||
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
|
||||
private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
||||
|
||||
/* ==========================================================================================
|
||||
* IDs for channels
|
||||
* ========================================================================================== */
|
||||
/* ==========================================================================================
|
||||
* IDs for channels
|
||||
* ========================================================================================== */
|
||||
|
||||
// on devices >= android O, we need to define a channel for each notifications
|
||||
private const val LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID = "LISTEN_FOR_EVENTS_NOTIFICATION_CHANNEL_ID"
|
||||
// on devices >= android O, we need to define a channel for each notifications
|
||||
private const val LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID = "LISTEN_FOR_EVENTS_NOTIFICATION_CHANNEL_ID"
|
||||
|
||||
private const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID"
|
||||
private const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID"
|
||||
|
||||
private const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2"
|
||||
private const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V2"
|
||||
private const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2"
|
||||
private const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V2"
|
||||
|
||||
fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
|
||||
fun openSystemSettingsForSilentCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
|
||||
fun openSystemSettingsForNoisyCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, NOISY_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
|
||||
fun openSystemSettingsForCallCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
}
|
||||
|
||||
private val notificationManager = NotificationManagerCompat.from(context)
|
||||
|
||||
/* ==========================================================================================
|
||||
* Channel names
|
||||
@ -96,17 +118,13 @@ object NotificationUtils {
|
||||
|
||||
/**
|
||||
* Create notification channels.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
fun createNotificationChannels(context: Context) {
|
||||
fun createNotificationChannels() {
|
||||
if (!supportNotificationChannels()) {
|
||||
return
|
||||
}
|
||||
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
|
||||
//Migration - the noisy channel was deleted and recreated when sound preference was changed (id was DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_BASE
|
||||
@ -132,10 +150,10 @@ object NotificationUtils {
|
||||
* intrude.
|
||||
*/
|
||||
notificationManager.createNotificationChannel(NotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID,
|
||||
context.getString(R.string.notification_noisy_notifications),
|
||||
stringProvider.getString(R.string.notification_noisy_notifications),
|
||||
NotificationManager.IMPORTANCE_DEFAULT)
|
||||
.apply {
|
||||
description = context.getString(R.string.notification_noisy_notifications)
|
||||
description = stringProvider.getString(R.string.notification_noisy_notifications)
|
||||
enableVibration(true)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
@ -145,29 +163,29 @@ object NotificationUtils {
|
||||
* Low notification importance: shows everywhere, but is not intrusive.
|
||||
*/
|
||||
notificationManager.createNotificationChannel(NotificationChannel(SILENT_NOTIFICATION_CHANNEL_ID,
|
||||
context.getString(R.string.notification_silent_notifications),
|
||||
stringProvider.getString(R.string.notification_silent_notifications),
|
||||
NotificationManager.IMPORTANCE_LOW)
|
||||
.apply {
|
||||
description = context.getString(R.string.notification_silent_notifications)
|
||||
description = stringProvider.getString(R.string.notification_silent_notifications)
|
||||
setSound(null, null)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
})
|
||||
|
||||
notificationManager.createNotificationChannel(NotificationChannel(LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID,
|
||||
context.getString(R.string.notification_listening_for_events),
|
||||
stringProvider.getString(R.string.notification_listening_for_events),
|
||||
NotificationManager.IMPORTANCE_MIN)
|
||||
.apply {
|
||||
description = context.getString(R.string.notification_listening_for_events)
|
||||
description = stringProvider.getString(R.string.notification_listening_for_events)
|
||||
setSound(null, null)
|
||||
setShowBadge(false)
|
||||
})
|
||||
|
||||
notificationManager.createNotificationChannel(NotificationChannel(CALL_NOTIFICATION_CHANNEL_ID,
|
||||
context.getString(R.string.call),
|
||||
stringProvider.getString(R.string.call),
|
||||
NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
description = context.getString(R.string.call)
|
||||
description = stringProvider.getString(R.string.call)
|
||||
setSound(null, null)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
@ -177,12 +195,11 @@ object NotificationUtils {
|
||||
/**
|
||||
* Build a polling thread listener notification
|
||||
*
|
||||
* @param context Android context
|
||||
* @param subTitleResId subtitle string resource Id of the notification
|
||||
* @return the polling thread listener notification
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun buildForegroundServiceNotification(context: Context, @StringRes subTitleResId: Int, withProgress: Boolean = true): Notification {
|
||||
fun buildForegroundServiceNotification(@StringRes subTitleResId: Int, withProgress: Boolean = true): Notification {
|
||||
// build the pending intent go to the home screen if this is clicked.
|
||||
val i = Intent(context, HomeActivity::class.java)
|
||||
i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
@ -191,7 +208,7 @@ object NotificationUtils {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
|
||||
val builder = NotificationCompat.Builder(context, LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(subTitleResId))
|
||||
.setContentTitle(stringProvider.getString(subTitleResId))
|
||||
.setSmallIcon(R.drawable.sync)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setColor(accentColor)
|
||||
@ -225,7 +242,7 @@ object NotificationUtils {
|
||||
CharSequence::class.java,
|
||||
CharSequence::class.java,
|
||||
PendingIntent::class.java)
|
||||
deprecatedMethod.invoke(notification, context, context.getString(R.string.app_name), context.getString(subTitleResId), pi)
|
||||
deprecatedMethod.invoke(notification, context, stringProvider.getString(R.string.app_name), stringProvider.getString(subTitleResId), pi)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "## buildNotification(): Exception - setLatestEventInfo() Msg=")
|
||||
}
|
||||
@ -238,7 +255,6 @@ object NotificationUtils {
|
||||
* Build an incoming call notification.
|
||||
* This notification starts the VectorHomeActivity which is in charge of centralizing the incoming call flow.
|
||||
*
|
||||
* @param context the context.
|
||||
* @param isVideo true if this is a video call, false for voice call
|
||||
* @param roomName the room name in which the call is pending.
|
||||
* @param matrixId the matrix id
|
||||
@ -246,20 +262,19 @@ object NotificationUtils {
|
||||
* @return the call notification.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun buildIncomingCallNotification(context: Context,
|
||||
isVideo: Boolean,
|
||||
fun buildIncomingCallNotification(isVideo: Boolean,
|
||||
roomName: String,
|
||||
matrixId: String,
|
||||
callId: String): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
|
||||
val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(ensureTitleNotEmpty(context, roomName))
|
||||
.setContentTitle(ensureTitleNotEmpty(roomName))
|
||||
.apply {
|
||||
if (isVideo) {
|
||||
setContentText(context.getString(R.string.incoming_video_call))
|
||||
setContentText(stringProvider.getString(R.string.incoming_video_call))
|
||||
} else {
|
||||
setContentText(context.getString(R.string.incoming_voice_call))
|
||||
setContentText(stringProvider.getString(R.string.incoming_voice_call))
|
||||
}
|
||||
}
|
||||
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
|
||||
@ -295,7 +310,6 @@ object NotificationUtils {
|
||||
/**
|
||||
* Build a pending call notification
|
||||
*
|
||||
* @param context the context.
|
||||
* @param isVideo true if this is a video call, false for voice call
|
||||
* @param roomName the room name in which the call is pending.
|
||||
* @param roomId the room Id
|
||||
@ -304,20 +318,19 @@ object NotificationUtils {
|
||||
* @return the call notification.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun buildPendingCallNotification(context: Context,
|
||||
isVideo: Boolean,
|
||||
fun buildPendingCallNotification(isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String): Notification {
|
||||
|
||||
val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(ensureTitleNotEmpty(context, roomName))
|
||||
.setContentTitle(ensureTitleNotEmpty(roomName))
|
||||
.apply {
|
||||
if (isVideo) {
|
||||
setContentText(context.getString(R.string.video_call_in_progress))
|
||||
setContentText(stringProvider.getString(R.string.video_call_in_progress))
|
||||
} else {
|
||||
setContentText(context.getString(R.string.call_in_progress))
|
||||
setContentText(stringProvider.getString(R.string.call_in_progress))
|
||||
}
|
||||
}
|
||||
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
|
||||
@ -355,9 +368,9 @@ object NotificationUtils {
|
||||
/**
|
||||
* Build a temporary (because service will be stopped just after) notification for the CallService, when a call is ended
|
||||
*/
|
||||
fun buildCallEndedNotification(context: Context): Notification {
|
||||
fun buildCallEndedNotification(): Notification {
|
||||
return NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(R.string.call_ended))
|
||||
.setContentTitle(stringProvider.getString(R.string.call_ended))
|
||||
.setSmallIcon(R.drawable.ic_material_call_end_grey)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.build()
|
||||
@ -366,17 +379,14 @@ object NotificationUtils {
|
||||
/**
|
||||
* Build a notification for a Room
|
||||
*/
|
||||
fun buildMessagesListNotification(context: Context,
|
||||
vectorPreferences: VectorPreferences,
|
||||
messageStyle: NotificationCompat.MessagingStyle,
|
||||
fun buildMessagesListNotification(messageStyle: NotificationCompat.MessagingStyle,
|
||||
roomInfo: RoomEventGroupInfo,
|
||||
largeIcon: Bitmap?,
|
||||
lastMessageTimestamp: Long,
|
||||
senderDisplayNameForReplyCompat: String?): Notification? {
|
||||
|
||||
senderDisplayNameForReplyCompat: String?): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
// Build the pending intent for when the notification is clicked
|
||||
val openRoomIntent = buildOpenRoomIntent(context, roomInfo.roomId)
|
||||
val openRoomIntent = buildOpenRoomIntent(roomInfo.roomId)
|
||||
val smallIcon = R.drawable.ic_status_bar
|
||||
|
||||
val channelID = if (roomInfo.shouldBing) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
||||
@ -393,18 +403,15 @@ object NotificationUtils {
|
||||
// Title for API < 16 devices.
|
||||
.setContentTitle(roomInfo.roomDisplayName)
|
||||
// Content for API < 16 devices.
|
||||
.setContentText(context.getString(R.string.notification_new_messages))
|
||||
.setContentText(stringProvider.getString(R.string.notification_new_messages))
|
||||
|
||||
// Number of new notifications for API <24 (M and below) devices.
|
||||
.setSubText(context
|
||||
.resources
|
||||
.getQuantityString(R.plurals.room_new_messages_notification, messageStyle.messages.size, messageStyle.messages.size)
|
||||
)
|
||||
.setSubText(stringProvider.getQuantityString(R.plurals.room_new_messages_notification, messageStyle.messages.size, messageStyle.messages.size))
|
||||
|
||||
// Auto-bundling is enabled for 4 or more notifications on API 24+ (N+)
|
||||
// devices and all Wear devices. But we want a custom grouping, so we specify the groupID
|
||||
// TODO Group should be current user display name
|
||||
.setGroup(context.getString(R.string.app_name))
|
||||
.setGroup(stringProvider.getString(R.string.app_name))
|
||||
|
||||
//In order to avoid notification making sound twice (due to the summary notification)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
@ -440,17 +447,17 @@ object NotificationUtils {
|
||||
|
||||
addAction(NotificationCompat.Action(
|
||||
R.drawable.ic_material_done_all_white,
|
||||
context.getString(R.string.action_mark_room_read),
|
||||
stringProvider.getString(R.string.action_mark_room_read),
|
||||
markRoomReadPendingIntent))
|
||||
|
||||
// Quick reply
|
||||
if (!roomInfo.hasSmartReplyError) {
|
||||
buildQuickReplyIntent(context, roomInfo.roomId, senderDisplayNameForReplyCompat)?.let { replyPendingIntent ->
|
||||
buildQuickReplyIntent(roomInfo.roomId, senderDisplayNameForReplyCompat)?.let { replyPendingIntent ->
|
||||
val remoteInput = RemoteInput.Builder(NotificationBroadcastReceiver.KEY_TEXT_REPLY)
|
||||
.setLabel(context.getString(R.string.action_quick_reply))
|
||||
.setLabel(stringProvider.getString(R.string.action_quick_reply))
|
||||
.build()
|
||||
NotificationCompat.Action.Builder(R.drawable.vector_notification_quick_reply,
|
||||
context.getString(R.string.action_quick_reply), replyPendingIntent)
|
||||
stringProvider.getString(R.string.action_quick_reply), replyPendingIntent)
|
||||
.addRemoteInput(remoteInput)
|
||||
.build()?.let {
|
||||
addAction(it)
|
||||
@ -477,11 +484,9 @@ object NotificationUtils {
|
||||
}
|
||||
|
||||
|
||||
fun buildSimpleEventNotification(context: Context,
|
||||
vectorPreferences: VectorPreferences,
|
||||
simpleNotifiableEvent: NotifiableEvent,
|
||||
fun buildSimpleEventNotification(simpleNotifiableEvent: NotifiableEvent,
|
||||
largeIcon: Bitmap?,
|
||||
matrixId: String): Notification? {
|
||||
matrixId: String): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
// Build the pending intent for when the notification is clicked
|
||||
val smallIcon = R.drawable.ic_status_bar
|
||||
@ -489,9 +494,9 @@ object NotificationUtils {
|
||||
val channelID = if (simpleNotifiableEvent.noisy) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
||||
|
||||
return NotificationCompat.Builder(context, channelID)
|
||||
.setContentTitle(context.getString(R.string.app_name))
|
||||
.setContentTitle(stringProvider.getString(R.string.app_name))
|
||||
.setContentText(simpleNotifiableEvent.description)
|
||||
.setGroup(context.getString(R.string.app_name))
|
||||
.setGroup(stringProvider.getString(R.string.app_name))
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setColor(accentColor)
|
||||
@ -508,7 +513,7 @@ object NotificationUtils {
|
||||
|
||||
addAction(
|
||||
R.drawable.vector_notification_reject_invitation,
|
||||
context.getString(R.string.reject),
|
||||
stringProvider.getString(R.string.reject),
|
||||
rejectIntentPendingIntent)
|
||||
|
||||
// offer to type a quick accept button
|
||||
@ -520,7 +525,7 @@ object NotificationUtils {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
addAction(
|
||||
R.drawable.vector_notification_accept_invitation,
|
||||
context.getString(R.string.join),
|
||||
stringProvider.getString(R.string.join),
|
||||
joinIntentPendingIntent)
|
||||
} else {
|
||||
setAutoCancel(true)
|
||||
@ -551,7 +556,7 @@ object NotificationUtils {
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildOpenRoomIntent(context: Context, roomId: String): PendingIntent? {
|
||||
private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
|
||||
val roomIntentTap = RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId))
|
||||
roomIntentTap.action = TAP_TO_VIEW_ACTION
|
||||
//pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
||||
@ -564,7 +569,7 @@ object NotificationUtils {
|
||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun buildOpenHomePendingIntentForSummary(context: Context): PendingIntent {
|
||||
private fun buildOpenHomePendingIntentForSummary(): PendingIntent {
|
||||
val intent = HomeActivity.newIntent(context, clearNotification = true)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
intent.data = Uri.parse("foobar://tapSummary")
|
||||
@ -578,7 +583,7 @@ object NotificationUtils {
|
||||
However, for Android devices running Marshmallow and below (API level 23 and below),
|
||||
it will be more appropriate to use an activity. Since you have to provide your own UI.
|
||||
*/
|
||||
private fun buildQuickReplyIntent(context: Context, roomId: String, senderName: String?): PendingIntent? {
|
||||
private fun buildQuickReplyIntent(roomId: String, senderName: String?): PendingIntent? {
|
||||
val intent: Intent
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||
@ -610,12 +615,10 @@ object NotificationUtils {
|
||||
/**
|
||||
* Build the summary notification
|
||||
*/
|
||||
fun buildSummaryListNotification(context: Context,
|
||||
vectorPreferences: VectorPreferences,
|
||||
style: NotificationCompat.InboxStyle,
|
||||
fun buildSummaryListNotification(style: NotificationCompat.InboxStyle,
|
||||
compatSummary: String,
|
||||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long): Notification? {
|
||||
lastMessageTimestamp: Long): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val smallIcon = R.drawable.ic_status_bar
|
||||
|
||||
@ -623,12 +626,12 @@ object NotificationUtils {
|
||||
// used in compat < N, after summary is built based on child notifications
|
||||
.setWhen(lastMessageTimestamp)
|
||||
.setStyle(style)
|
||||
.setContentTitle(context.getString(R.string.app_name))
|
||||
.setContentTitle(stringProvider.getString(R.string.app_name))
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setSmallIcon(smallIcon)
|
||||
//set content text to support devices running API level < 24
|
||||
.setContentText(compatSummary)
|
||||
.setGroup(context.getString(R.string.app_name))
|
||||
.setGroup(stringProvider.getString(R.string.app_name))
|
||||
//set this notification as the summary for the group
|
||||
.setGroupSummary(true)
|
||||
.setColor(accentColor)
|
||||
@ -645,13 +648,12 @@ object NotificationUtils {
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
}
|
||||
}
|
||||
.setContentIntent(buildOpenHomePendingIntentForSummary(context))
|
||||
.setDeleteIntent(getDismissSummaryPendingIntent(context))
|
||||
.setContentIntent(buildOpenHomePendingIntentForSummary())
|
||||
.setDeleteIntent(getDismissSummaryPendingIntent())
|
||||
.build()
|
||||
|
||||
}
|
||||
|
||||
private fun getDismissSummaryPendingIntent(context: Context): PendingIntent {
|
||||
private fun getDismissSummaryPendingIntent(): PendingIntent {
|
||||
val intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||
intent.action = DISMISS_SUMMARY_ACTION
|
||||
intent.data = Uri.parse("foobar://deleteSummary")
|
||||
@ -659,33 +661,28 @@ object NotificationUtils {
|
||||
0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
fun showNotificationMessage(context: Context, tag: String?, id: Int, notification: Notification) {
|
||||
with(NotificationManagerCompat.from(context)) {
|
||||
notify(tag, id, notification)
|
||||
}
|
||||
fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
|
||||
notificationManager.notify(tag, id, notification)
|
||||
}
|
||||
|
||||
fun cancelNotificationMessage(context: Context, tag: String?, id: Int) {
|
||||
NotificationManagerCompat.from(context)
|
||||
.cancel(tag, id)
|
||||
fun cancelNotificationMessage(tag: String?, id: Int) {
|
||||
notificationManager.cancel(tag, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the foreground notification service
|
||||
*/
|
||||
fun cancelNotificationForegroundService(context: Context) {
|
||||
NotificationManagerCompat.from(context)
|
||||
.cancel(NOTIFICATION_ID_FOREGROUND_SERVICE)
|
||||
fun cancelNotificationForegroundService() {
|
||||
notificationManager.cancel(NOTIFICATION_ID_FOREGROUND_SERVICE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all the notification
|
||||
*/
|
||||
fun cancelAllNotifications(context: Context) {
|
||||
fun cancelAllNotifications() {
|
||||
// Keep this try catch (reported by GA)
|
||||
try {
|
||||
NotificationManagerCompat.from(context)
|
||||
.cancelAll()
|
||||
notificationManager.cancelAll()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## cancelAllNotifications() failed " + e.message)
|
||||
}
|
||||
@ -694,7 +691,7 @@ object NotificationUtils {
|
||||
/**
|
||||
* Return true it the user has enabled the do not disturb mode
|
||||
*/
|
||||
fun isDoNotDisturbModeOn(context: Context): Boolean {
|
||||
fun isDoNotDisturbModeOn(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return false
|
||||
}
|
||||
@ -706,23 +703,11 @@ object NotificationUtils {
|
||||
|| setting == NotificationManager.INTERRUPTION_FILTER_ALARMS
|
||||
}
|
||||
|
||||
private fun ensureTitleNotEmpty(context: Context, title: String?): CharSequence {
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
return context.getString(R.string.app_name)
|
||||
private fun ensureTitleNotEmpty(title: String?): CharSequence {
|
||||
if (title.isNullOrBlank()) {
|
||||
return stringProvider.getString(R.string.app_name)
|
||||
}
|
||||
|
||||
return title!!
|
||||
}
|
||||
|
||||
fun openSystemSettingsForSilentCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
|
||||
fun openSystemSettingsForNoisyCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, NOISY_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
|
||||
fun openSystemSettingsForCallCategory(fragment: Fragment) {
|
||||
startNotificationChannelSettingsIntent(fragment, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
return title
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,10 @@ class PushRuleTriggerListener @Inject constructor(
|
||||
notificationDrawerManager.clearMessageEventOfRoom(roomId)
|
||||
}
|
||||
|
||||
override fun onEventRedacted(redactedEventId: String) {
|
||||
notificationDrawerManager.onEventRedacted(redactedEventId)
|
||||
}
|
||||
|
||||
override fun batchFinish() {
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ data class SimpleNotifiableEvent(
|
||||
override var isPushGatewayEvent: Boolean = false) : NotifiableEvent {
|
||||
|
||||
override var hasBeenDisplayed: Boolean = false
|
||||
override var isRedacted: Boolean = false
|
||||
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import im.vector.riotx.core.extensions.withArgs
|
||||
import im.vector.riotx.core.preference.BingRule
|
||||
import im.vector.riotx.core.preference.BingRulePreference
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import im.vector.riotx.features.notifications.supportNotificationChannels
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseFragment() {
|
||||
@ -56,7 +55,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseF
|
||||
|
||||
override fun bindPref() {
|
||||
val callNotificationsSystemOptions = findPreference(VectorPreferences.SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY)
|
||||
if (supportNotificationChannels()) {
|
||||
if (NotificationUtils.supportNotificationChannels()) {
|
||||
callNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
NotificationUtils.openSystemSettingsForCallCategory(this)
|
||||
false
|
||||
@ -66,7 +65,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseF
|
||||
}
|
||||
|
||||
val noisyNotificationsSystemOptions = findPreference(VectorPreferences.SETTINGS_SYSTEM_NOISY_NOTIFICATION_PREFERENCE_KEY)
|
||||
if (supportNotificationChannels()) {
|
||||
if (NotificationUtils.supportNotificationChannels()) {
|
||||
noisyNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
NotificationUtils.openSystemSettingsForNoisyCategory(this)
|
||||
false
|
||||
@ -76,7 +75,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseF
|
||||
}
|
||||
|
||||
val silentNotificationsSystemOptions = findPreference(VectorPreferences.SETTINGS_SYSTEM_SILENT_NOTIFICATION_PREFERENCE_KEY)
|
||||
if (supportNotificationChannels()) {
|
||||
if (NotificationUtils.supportNotificationChannels()) {
|
||||
silentNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
NotificationUtils.openSystemSettingsForSilentCategory(this)
|
||||
false
|
||||
@ -89,7 +88,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseF
|
||||
// Ringtone
|
||||
val ringtonePreference = findPreference(VectorPreferences.SETTINGS_NOTIFICATION_RINGTONE_SELECTION_PREFERENCE_KEY)
|
||||
|
||||
if (supportNotificationChannels()) {
|
||||
if (NotificationUtils.supportNotificationChannels()) {
|
||||
ringtonePreference.isVisible = false
|
||||
} else {
|
||||
ringtonePreference.summary = vectorPreferences.getNotificationRingToneName()
|
||||
|
Loading…
Reference in New Issue
Block a user