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