mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-27 03:49:04 +03:00
chaining the event process, notification creation and display logic into a NotificationRender
- extract the displaying into its own class to avoid leaking the entire notificationutils - cancel/display notification actions are completely driven by the event or abscense of event from the eventList - attempts to avoid redundant render passes by checking if the eventList has changed since the last render
This commit is contained in:
parent
0f4ec65b7a
commit
3023cb4d39
10 changed files with 507 additions and 18 deletions
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.notifications
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class NotificationDisplayer @Inject constructor(context: Context) {
|
||||||
|
|
||||||
|
private val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
|
||||||
|
fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
|
||||||
|
notificationManager.notify(tag, id, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelNotificationMessage(tag: String?, id: Int) {
|
||||||
|
notificationManager.cancel(tag, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelAllNotifications() {
|
||||||
|
// Keep this try catch (reported by GA)
|
||||||
|
try {
|
||||||
|
notificationManager.cancelAll()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "## cancelAllNotifications() failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ class NotificationFactory @Inject constructor(
|
||||||
fun Map<String, List<NotifiableMessageEvent>>.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List<RoomNotification> {
|
fun Map<String, List<NotifiableMessageEvent>>.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List<RoomNotification> {
|
||||||
return this.map { (roomId, events) ->
|
return this.map { (roomId, events) ->
|
||||||
when {
|
when {
|
||||||
events.hasNoEventsToDisplay() -> RoomNotification.EmptyRoom(roomId)
|
events.hasNoEventsToDisplay() -> RoomNotification.Removed(roomId)
|
||||||
else -> roomGroupMessageCreator.createRoomMessage(events, roomId, myUserDisplayName, myUserAvatarUrl)
|
else -> roomGroupMessageCreator.createRoomMessage(events, roomId, myUserDisplayName, myUserAvatarUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ private fun List<RoomNotification>.mapToMeta() = filterIsInstance<RoomNotificati
|
||||||
private fun List<OneShotNotification>.mapToMeta() = filterIsInstance<OneShotNotification.Append>().map { it.meta }
|
private fun List<OneShotNotification>.mapToMeta() = filterIsInstance<OneShotNotification.Append>().map { it.meta }
|
||||||
|
|
||||||
sealed interface RoomNotification {
|
sealed interface RoomNotification {
|
||||||
data class EmptyRoom(val roomId: String) : RoomNotification
|
data class Removed(val roomId: String) : RoomNotification
|
||||||
data class Message(val notification: Notification, val meta: Meta) : RoomNotification {
|
data class Message(val notification: Notification, val meta: Meta) : RoomNotification {
|
||||||
data class Meta(
|
data class Meta(
|
||||||
val summaryLine: CharSequence,
|
val summaryLine: CharSequence,
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package im.vector.app.features.notifications
|
||||||
|
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class NotificationRenderer @Inject constructor(private val notifiableEventProcessor: NotifiableEventProcessor,
|
||||||
|
private val notificationDisplayer: NotificationDisplayer,
|
||||||
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
private val notificationFactory: NotificationFactory) {
|
||||||
|
|
||||||
|
private var lastKnownEventList = -1
|
||||||
|
private var useCompleteNotificationFormat = vectorPreferences.useCompleteNotificationFormat()
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
fun render(currentRoomId: String?, myUserId: String, myUserDisplayName: String, myUserAvatarUrl: String?, eventList: MutableList<NotifiableEvent>) {
|
||||||
|
Timber.v("refreshNotificationDrawerBg()")
|
||||||
|
val newSettings = vectorPreferences.useCompleteNotificationFormat()
|
||||||
|
if (newSettings != useCompleteNotificationFormat) {
|
||||||
|
// Settings has changed, remove all current notifications
|
||||||
|
notificationDisplayer.cancelAllNotifications()
|
||||||
|
useCompleteNotificationFormat = newSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
val notificationEvents = notifiableEventProcessor.modifyAndProcess(eventList, currentRoomId)
|
||||||
|
if (lastKnownEventList == notificationEvents.hashCode()) {
|
||||||
|
Timber.d("Skipping notification update due to event list not changing")
|
||||||
|
} else {
|
||||||
|
processEvents(notificationEvents, myUserId, myUserDisplayName, myUserAvatarUrl)
|
||||||
|
lastKnownEventList = notificationEvents.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processEvents(notificationEvents: ProcessedNotificationEvents, myUserId: String, myUserDisplayName: String, myUserAvatarUrl: String?) {
|
||||||
|
val (roomEvents, simpleEvents, invitationEvents) = notificationEvents
|
||||||
|
with(notificationFactory) {
|
||||||
|
val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl)
|
||||||
|
val invitationNotifications = invitationEvents.toNotifications(myUserId)
|
||||||
|
val simpleNotifications = simpleEvents.toNotifications(myUserId)
|
||||||
|
|
||||||
|
if (roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty()) {
|
||||||
|
notificationDisplayer.cancelNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID)
|
||||||
|
} else {
|
||||||
|
val summaryNotification = createSummaryNotification(
|
||||||
|
roomNotifications = roomNotifications,
|
||||||
|
invitationNotifications = invitationNotifications,
|
||||||
|
simpleNotifications = simpleNotifications,
|
||||||
|
useCompleteNotificationFormat = useCompleteNotificationFormat
|
||||||
|
)
|
||||||
|
roomNotifications.forEach { wrapper ->
|
||||||
|
when (wrapper) {
|
||||||
|
is RoomNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID)
|
||||||
|
is RoomNotification.Message -> if (useCompleteNotificationFormat) {
|
||||||
|
Timber.d("Updating room messages notification ${wrapper.meta.roomId}")
|
||||||
|
notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invitationNotifications.forEach { wrapper ->
|
||||||
|
when (wrapper) {
|
||||||
|
is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID)
|
||||||
|
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
||||||
|
Timber.d("Updating invitation notification ${wrapper.meta.key}")
|
||||||
|
notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleNotifications.forEach { wrapper ->
|
||||||
|
when (wrapper) {
|
||||||
|
is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID)
|
||||||
|
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
||||||
|
Timber.d("Updating simple notification ${wrapper.meta.key}")
|
||||||
|
notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, wrapper.notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notificationDisplayer.showNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, summaryNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,25 +22,25 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ======== Build summary notification =========
|
||||||
|
* On Android 7.0 (API level 24) and higher, the system automatically builds a summary for
|
||||||
|
* your group using snippets of text from each notification. The user can expand this
|
||||||
|
* notification to see each separate notification.
|
||||||
|
* To support older versions, which cannot show a nested group of notifications,
|
||||||
|
* you must create an extra notification that acts as the summary.
|
||||||
|
* This appears as the only notification and the system hides all the others.
|
||||||
|
* So this summary should include a snippet from all the other notifications,
|
||||||
|
* which the user can tap to open your app.
|
||||||
|
* The behavior of the group summary may vary on some device types such as wearables.
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
class SummaryGroupMessageCreator @Inject constructor(
|
class SummaryGroupMessageCreator @Inject constructor(
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val notificationUtils: NotificationUtils
|
private val notificationUtils: NotificationUtils
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
|
||||||
* ======== Build summary notification =========
|
|
||||||
* On Android 7.0 (API level 24) and higher, the system automatically builds a summary for
|
|
||||||
* your group using snippets of text from each notification. The user can expand this
|
|
||||||
* notification to see each separate notification.
|
|
||||||
* To support older versions, which cannot show a nested group of notifications,
|
|
||||||
* you must create an extra notification that acts as the summary.
|
|
||||||
* This appears as the only notification and the system hides all the others.
|
|
||||||
* So this summary should include a snippet from all the other notifications,
|
|
||||||
* which the user can tap to open your app.
|
|
||||||
* The behavior of the group summary may vary on some device types such as wearables.
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
fun createSummaryNotification(roomNotifications: List<RoomNotification.Message.Meta>,
|
fun createSummaryNotification(roomNotifications: List<RoomNotification.Message.Meta>,
|
||||||
invitationNotifications: List<OneShotNotification.Append.Meta>,
|
invitationNotifications: List<OneShotNotification.Append.Meta>,
|
||||||
simpleNotifications: List<OneShotNotification.Append.Meta>,
|
simpleNotifications: List<OneShotNotification.Append.Meta>,
|
||||||
|
|
|
@ -116,7 +116,7 @@ class NotificationFactoryTest {
|
||||||
|
|
||||||
val result = emptyRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)
|
val result = emptyRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)
|
||||||
|
|
||||||
result shouldBeEqualTo listOf(RoomNotification.EmptyRoom(
|
result shouldBeEqualTo listOf(RoomNotification.Removed(
|
||||||
roomId = A_ROOM_ID
|
roomId = A_ROOM_ID
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ class NotificationFactoryTest {
|
||||||
|
|
||||||
val result = redactedRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)
|
val result = redactedRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)
|
||||||
|
|
||||||
result shouldBeEqualTo listOf(RoomNotification.EmptyRoom(
|
result shouldBeEqualTo listOf(RoomNotification.Removed(
|
||||||
roomId = A_ROOM_ID
|
roomId = A_ROOM_ID
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.notifications
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import im.vector.app.test.fakes.FakeNotifiableEventProcessor
|
||||||
|
import im.vector.app.test.fakes.FakeNotificationDisplayer
|
||||||
|
import im.vector.app.test.fakes.FakeNotificationFactory
|
||||||
|
import im.vector.app.test.fakes.FakeVectorPreferences
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
private const val A_CURRENT_ROOM_ID = "current-room-id"
|
||||||
|
private const val MY_USER_ID = "my-user-id"
|
||||||
|
private const val MY_USER_DISPLAY_NAME = "display-name"
|
||||||
|
private const val MY_USER_AVATAR_URL = "avatar-url"
|
||||||
|
private const val AN_EVENT_ID = "event-id"
|
||||||
|
private const val A_ROOM_ID = "room-id"
|
||||||
|
private const val USE_COMPLETE_NOTIFICATION_FORMAT = true
|
||||||
|
|
||||||
|
private val AN_EVENT_LIST = mutableListOf<NotifiableEvent>()
|
||||||
|
private val A_PROCESSED_EVENTS = ProcessedNotificationEvents(emptyMap(), emptyMap(), emptyMap())
|
||||||
|
private val A_SUMMARY_NOTIFICATION = mockk<Notification>()
|
||||||
|
private val A_NOTIFICATION = mockk<Notification>()
|
||||||
|
private val MESSAGE_META = RoomNotification.Message.Meta(
|
||||||
|
summaryLine = "ignored", messageCount = 1, latestTimestamp = -1, roomId = A_ROOM_ID, shouldBing = false
|
||||||
|
)
|
||||||
|
private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", summaryLine = "ignored", isNoisy = false)
|
||||||
|
|
||||||
|
class NotificationRendererTest {
|
||||||
|
|
||||||
|
private val notifiableEventProcessor = FakeNotifiableEventProcessor()
|
||||||
|
private val notificationDisplayer = FakeNotificationDisplayer()
|
||||||
|
private val preferences = FakeVectorPreferences().also {
|
||||||
|
it.givenUseCompleteNotificationFormat(USE_COMPLETE_NOTIFICATION_FORMAT)
|
||||||
|
}
|
||||||
|
private val notificationFactory = FakeNotificationFactory()
|
||||||
|
|
||||||
|
private val notificationRenderer = NotificationRenderer(
|
||||||
|
notifiableEventProcessor = notifiableEventProcessor.instance,
|
||||||
|
notificationDisplayer = notificationDisplayer.instance,
|
||||||
|
vectorPreferences = preferences.instance,
|
||||||
|
notificationFactory = notificationFactory.instance
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no notifications when rendering then cancels summary notification`() {
|
||||||
|
givenNoNotifications()
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifySummaryCancelled()
|
||||||
|
notificationDisplayer.verifyNoOtherInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() {
|
||||||
|
givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a room message group notification is added when rendering then show the message notification and update summary`() {
|
||||||
|
givenNotifications(roomNotifications = listOf(RoomNotification.Message(
|
||||||
|
A_NOTIFICATION,
|
||||||
|
MESSAGE_META
|
||||||
|
)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, A_NOTIFICATION)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() {
|
||||||
|
givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
cancelNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a simple notification is added when rendering then show the simple notification and update summary`() {
|
||||||
|
givenNotifications(simpleNotifications = listOf(OneShotNotification.Append(
|
||||||
|
A_NOTIFICATION,
|
||||||
|
ONE_SHOT_META.copy(key = AN_EVENT_ID)
|
||||||
|
)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
showNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() {
|
||||||
|
givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() {
|
||||||
|
givenNotifications(simpleNotifications = listOf(OneShotNotification.Append(
|
||||||
|
A_NOTIFICATION,
|
||||||
|
ONE_SHOT_META.copy(key = A_ROOM_ID)
|
||||||
|
)))
|
||||||
|
|
||||||
|
renderEventsAsNotifications()
|
||||||
|
|
||||||
|
notificationDisplayer.verifyInOrder {
|
||||||
|
showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION)
|
||||||
|
showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderEventsAsNotifications() {
|
||||||
|
notificationRenderer.render(
|
||||||
|
currentRoomId = A_CURRENT_ROOM_ID,
|
||||||
|
myUserId = MY_USER_ID,
|
||||||
|
myUserDisplayName = MY_USER_DISPLAY_NAME,
|
||||||
|
myUserAvatarUrl = MY_USER_AVATAR_URL,
|
||||||
|
eventList = AN_EVENT_LIST
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenNoNotifications() {
|
||||||
|
givenNotifications(emptyList(), emptyList(), emptyList(), USE_COMPLETE_NOTIFICATION_FORMAT, A_SUMMARY_NOTIFICATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenNotifications(roomNotifications: List<RoomNotification> = emptyList(),
|
||||||
|
invitationNotifications: List<OneShotNotification> = emptyList(),
|
||||||
|
simpleNotifications: List<OneShotNotification> = emptyList(),
|
||||||
|
useCompleteNotificationFormat: Boolean = USE_COMPLETE_NOTIFICATION_FORMAT,
|
||||||
|
summaryNotification: Notification = A_SUMMARY_NOTIFICATION) {
|
||||||
|
notifiableEventProcessor.givenProcessedEventsFor(AN_EVENT_LIST, A_CURRENT_ROOM_ID, A_PROCESSED_EVENTS)
|
||||||
|
notificationFactory.givenNotificationsFor(
|
||||||
|
processedEvents = A_PROCESSED_EVENTS,
|
||||||
|
myUserId = MY_USER_ID,
|
||||||
|
myUserDisplayName = MY_USER_DISPLAY_NAME,
|
||||||
|
myUserAvatarUrl = MY_USER_AVATAR_URL,
|
||||||
|
useCompleteNotificationFormat = useCompleteNotificationFormat,
|
||||||
|
roomNotifications = roomNotifications,
|
||||||
|
invitationNotifications = invitationNotifications,
|
||||||
|
simpleNotifications = simpleNotifications,
|
||||||
|
summaryNotification = summaryNotification
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.notifications.NotifiableEvent
|
||||||
|
import im.vector.app.features.notifications.NotifiableEventProcessor
|
||||||
|
import im.vector.app.features.notifications.ProcessedNotificationEvents
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeNotifiableEventProcessor {
|
||||||
|
|
||||||
|
val instance = mockk<NotifiableEventProcessor>()
|
||||||
|
|
||||||
|
fun givenProcessedEventsFor(events: MutableList<NotifiableEvent>, currentRoomId: String?, processedEvents: ProcessedNotificationEvents) {
|
||||||
|
every { instance.modifyAndProcess(events, currentRoomId) } returns processedEvents
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.notifications.NotificationDisplayer
|
||||||
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
|
import io.mockk.confirmVerified
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import io.mockk.verifyOrder
|
||||||
|
|
||||||
|
class FakeNotificationDisplayer {
|
||||||
|
|
||||||
|
val instance = mockk<NotificationDisplayer>(relaxed = true)
|
||||||
|
|
||||||
|
fun verifySummaryCancelled() {
|
||||||
|
verify { instance.cancelNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyNoOtherInteractions() {
|
||||||
|
confirmVerified(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyInOrder(verifyBlock: NotificationDisplayer.() -> Unit) {
|
||||||
|
verifyOrder { verifyBlock(instance) }
|
||||||
|
verifyNoOtherInteractions()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import im.vector.app.features.notifications.NotificationFactory
|
||||||
|
import im.vector.app.features.notifications.OneShotNotification
|
||||||
|
import im.vector.app.features.notifications.ProcessedNotificationEvents
|
||||||
|
import im.vector.app.features.notifications.RoomNotification
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeNotificationFactory {
|
||||||
|
|
||||||
|
val instance = mockk<NotificationFactory>()
|
||||||
|
|
||||||
|
fun givenNotificationsFor(processedEvents: ProcessedNotificationEvents,
|
||||||
|
myUserId: String,
|
||||||
|
myUserDisplayName: String,
|
||||||
|
myUserAvatarUrl: String?,
|
||||||
|
useCompleteNotificationFormat: Boolean,
|
||||||
|
roomNotifications: List<RoomNotification>,
|
||||||
|
invitationNotifications: List<OneShotNotification>,
|
||||||
|
simpleNotifications: List<OneShotNotification>,
|
||||||
|
summaryNotification: Notification) {
|
||||||
|
with(instance) {
|
||||||
|
every { processedEvents.roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) } returns roomNotifications
|
||||||
|
every { processedEvents.invitationEvents.toNotifications(myUserId) } returns invitationNotifications
|
||||||
|
every { processedEvents.simpleEvents.toNotifications(myUserId) } returns simpleNotifications
|
||||||
|
|
||||||
|
every {
|
||||||
|
createSummaryNotification(
|
||||||
|
roomNotifications,
|
||||||
|
invitationNotifications,
|
||||||
|
simpleNotifications,
|
||||||
|
useCompleteNotificationFormat
|
||||||
|
)
|
||||||
|
} returns summaryNotification
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeVectorPreferences {
|
||||||
|
|
||||||
|
val instance = mockk<VectorPreferences>()
|
||||||
|
|
||||||
|
fun givenUseCompleteNotificationFormat(value: Boolean) {
|
||||||
|
every { instance.useCompleteNotificationFormat() } returns value
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue