Merge pull request #5607 from element-hq/feature/bma/notificationStyle
Update notification style
This commit is contained in:
@@ -7,8 +7,8 @@
|
||||
|
||||
package io.element.android.appconfig
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.toColorInt
|
||||
|
||||
object NotificationConfig {
|
||||
/**
|
||||
@@ -27,5 +27,5 @@ object NotificationConfig {
|
||||
const val SHOW_QUICK_REPLY_ACTION = true
|
||||
|
||||
@ColorInt
|
||||
val NOTIFICATION_ACCENT_COLOR: Int = Color.parseColor("#FF0DBD8B")
|
||||
val NOTIFICATION_ACCENT_COLOR: Int = "#FF0DBD8B".toColorInt()
|
||||
}
|
||||
|
||||
Submodule enterprise updated: 867d1118e1...19d78b589d
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.features.enterprise.api
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -24,6 +25,8 @@ interface EnterpriseService {
|
||||
*/
|
||||
suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?)
|
||||
|
||||
fun brandColorsFlow(sessionId: SessionId?): Flow<Color?>
|
||||
|
||||
fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark>
|
||||
|
||||
fun firebasePushGateway(): String?
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.features.enterprise.impl
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
@@ -27,6 +28,10 @@ class DefaultEnterpriseService : EnterpriseService {
|
||||
|
||||
override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit
|
||||
|
||||
override fun brandColorsFlow(sessionId: SessionId?): Flow<Color?> {
|
||||
return flowOf(null)
|
||||
}
|
||||
|
||||
override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
|
||||
return flowOf(SemanticColorsLightDark.default)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,16 @@ class DefaultEnterpriseServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `brandColorsFlow always emits null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
defaultEnterpriseService.brandColorsFlow(null).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isNull()
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `semanticColorsFlow always emits the same value for a session`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.features.enterprise.test
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
@@ -27,6 +28,7 @@ class FakeEnterpriseService(
|
||||
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
|
||||
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
|
||||
) : EnterpriseService {
|
||||
private val brandColorState = MutableStateFlow<Color?>(null)
|
||||
private val semanticColorsState = MutableStateFlow(initialSemanticColors)
|
||||
|
||||
override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask {
|
||||
@@ -45,6 +47,10 @@ class FakeEnterpriseService(
|
||||
overrideBrandColorResult(sessionId, brandColor)
|
||||
}
|
||||
|
||||
override fun brandColorsFlow(sessionId: SessionId?): Flow<Color?> {
|
||||
return brandColorState.asStateFlow()
|
||||
}
|
||||
|
||||
override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
|
||||
return semanticColorsState.asStateFlow()
|
||||
}
|
||||
|
||||
@@ -98,3 +98,5 @@ const val A_TIMESTAMP = 567L
|
||||
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
||||
|
||||
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
||||
|
||||
const val A_COLOR_INT = 0xFF0000
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies {
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.libraries.workmanager.api)
|
||||
implementation(projects.features.call.api)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.features.lockscreen.api)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
api(projects.libraries.pushproviders.api)
|
||||
@@ -77,6 +78,7 @@ dependencies {
|
||||
testImplementation(projects.libraries.troubleshoot.test)
|
||||
testImplementation(projects.libraries.workmanager.test)
|
||||
testImplementation(projects.features.call.test)
|
||||
testImplementation(projects.features.enterprise.test)
|
||||
testImplementation(projects.features.lockscreen.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
|
||||
@@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||
import android.app.Notification
|
||||
import android.graphics.Typeface
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import coil3.ImageLoader
|
||||
@@ -31,17 +32,29 @@ interface NotificationDataFactory {
|
||||
messages: List<NotifiableMessageEvent>,
|
||||
currentUser: MatrixUser,
|
||||
imageLoader: ImageLoader,
|
||||
@ColorInt color: Int,
|
||||
): List<RoomNotification>
|
||||
|
||||
@JvmName("toNotificationInvites")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
fun toNotifications(invites: List<InviteNotifiableEvent>): List<OneShotNotification>
|
||||
fun toNotifications(
|
||||
invites: List<InviteNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification>
|
||||
|
||||
@JvmName("toNotificationSimpleEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
fun toNotifications(simpleEvents: List<SimpleNotifiableEvent>): List<OneShotNotification>
|
||||
fun toNotifications(
|
||||
simpleEvents: List<SimpleNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification>
|
||||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
fun toNotifications(fallback: List<FallbackNotifiableEvent>): List<OneShotNotification>
|
||||
fun toNotifications(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification>
|
||||
|
||||
fun createSummaryNotification(
|
||||
currentUser: MatrixUser,
|
||||
@@ -49,6 +62,7 @@ interface NotificationDataFactory {
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): SummaryNotification
|
||||
}
|
||||
|
||||
@@ -64,6 +78,7 @@ class DefaultNotificationDataFactory(
|
||||
messages: List<NotifiableMessageEvent>,
|
||||
currentUser: MatrixUser,
|
||||
imageLoader: ImageLoader,
|
||||
@ColorInt color: Int,
|
||||
): List<RoomNotification> {
|
||||
val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() }
|
||||
.groupBy { it.roomId }
|
||||
@@ -76,6 +91,7 @@ class DefaultNotificationDataFactory(
|
||||
roomId = roomId,
|
||||
imageLoader = imageLoader,
|
||||
existingNotification = getExistingNotificationForMessages(currentUser.userId, roomId),
|
||||
color = color,
|
||||
)
|
||||
RoomNotification(
|
||||
notification = notification,
|
||||
@@ -96,11 +112,14 @@ class DefaultNotificationDataFactory(
|
||||
|
||||
@JvmName("toNotificationInvites")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(invites: List<InviteNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
invites: List<InviteNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return invites.map { event ->
|
||||
OneShotNotification(
|
||||
key = event.roomId.value,
|
||||
notification = notificationCreator.createRoomInvitationNotification(event),
|
||||
notification = notificationCreator.createRoomInvitationNotification(event, color),
|
||||
summaryLine = event.description,
|
||||
isNoisy = event.noisy,
|
||||
timestamp = event.timestamp
|
||||
@@ -110,11 +129,14 @@ class DefaultNotificationDataFactory(
|
||||
|
||||
@JvmName("toNotificationSimpleEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(simpleEvents: List<SimpleNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
simpleEvents: List<SimpleNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return simpleEvents.map { event ->
|
||||
OneShotNotification(
|
||||
key = event.eventId.value,
|
||||
notification = notificationCreator.createSimpleEventNotification(event),
|
||||
notification = notificationCreator.createSimpleEventNotification(event, color),
|
||||
summaryLine = event.description,
|
||||
isNoisy = event.noisy,
|
||||
timestamp = event.timestamp
|
||||
@@ -124,11 +146,14 @@ class DefaultNotificationDataFactory(
|
||||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(fallback: List<FallbackNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return fallback.map { event ->
|
||||
OneShotNotification(
|
||||
key = event.eventId.value,
|
||||
notification = notificationCreator.createFallbackNotification(event),
|
||||
notification = notificationCreator.createFallbackNotification(event, color),
|
||||
summaryLine = event.description.orEmpty(),
|
||||
isNoisy = false,
|
||||
timestamp = event.timestamp
|
||||
@@ -142,6 +167,7 @@ class DefaultNotificationDataFactory(
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): SummaryNotification {
|
||||
return when {
|
||||
roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty() -> SummaryNotification.Removed
|
||||
@@ -152,6 +178,7 @@ class DefaultNotificationDataFactory(
|
||||
invitationNotifications = invitationNotifications,
|
||||
simpleNotifications = simpleNotifications,
|
||||
fallbackNotifications = fallbackNotifications,
|
||||
color = color,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import coil3.ImageLoader
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
||||
@@ -18,6 +21,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import kotlinx.coroutines.flow.first
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("NotificationRenderer", LoggerTag.NotificationLoggerTag)
|
||||
@@ -26,6 +30,7 @@ private val loggerTag = LoggerTag("NotificationRenderer", LoggerTag.Notification
|
||||
class NotificationRenderer(
|
||||
private val notificationDisplayer: NotificationDisplayer,
|
||||
private val notificationDataFactory: NotificationDataFactory,
|
||||
private val enterpriseService: EnterpriseService,
|
||||
) {
|
||||
suspend fun render(
|
||||
currentUser: MatrixUser,
|
||||
@@ -33,17 +38,20 @@ class NotificationRenderer(
|
||||
eventsToProcess: List<NotifiableEvent>,
|
||||
imageLoader: ImageLoader,
|
||||
) {
|
||||
val color = enterpriseService.brandColorsFlow(currentUser.userId).first()?.toArgb()
|
||||
?: NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||
val groupedEvents = eventsToProcess.groupByType()
|
||||
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader)
|
||||
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents)
|
||||
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents)
|
||||
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents)
|
||||
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader, color)
|
||||
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, color)
|
||||
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, color)
|
||||
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, color)
|
||||
val summaryNotification = notificationDataFactory.createSummaryNotification(
|
||||
currentUser = currentUser,
|
||||
roomNotifications = roomNotifications,
|
||||
invitationNotifications = invitationNotifications,
|
||||
simpleNotifications = simpleNotifications,
|
||||
fallbackNotifications = fallbackNotifications,
|
||||
color = color,
|
||||
)
|
||||
|
||||
// Remove summary first to avoid briefly displaying it after dismissing the last notification
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import android.app.Notification
|
||||
import android.graphics.Bitmap
|
||||
import androidx.annotation.ColorInt
|
||||
import coil3.ImageLoader
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
@@ -28,6 +29,7 @@ interface RoomGroupMessageCreator {
|
||||
roomId: RoomId,
|
||||
imageLoader: ImageLoader,
|
||||
existingNotification: Notification?,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
}
|
||||
|
||||
@@ -43,6 +45,7 @@ class DefaultRoomGroupMessageCreator(
|
||||
roomId: RoomId,
|
||||
imageLoader: ImageLoader,
|
||||
existingNotification: Notification?,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val lastKnownRoomEvent = events.last()
|
||||
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)"
|
||||
@@ -60,24 +63,25 @@ class DefaultRoomGroupMessageCreator(
|
||||
val smartReplyErrors = events.filter { it.isSmartReplyError() }
|
||||
val roomIsDm = !roomIsGroup
|
||||
return notificationCreator.createMessagesListNotification(
|
||||
RoomEventGroupInfo(
|
||||
sessionId = currentUser.userId,
|
||||
roomId = roomId,
|
||||
roomDisplayName = roomName,
|
||||
isDm = roomIsDm,
|
||||
hasSmartReplyError = smartReplyErrors.isNotEmpty(),
|
||||
shouldBing = events.any { it.noisy },
|
||||
customSound = events.last().soundName,
|
||||
isUpdated = events.last().isUpdated,
|
||||
),
|
||||
threadId = lastKnownRoomEvent.threadId,
|
||||
largeIcon = largeBitmap,
|
||||
lastMessageTimestamp = lastMessageTimestamp,
|
||||
tickerText = tickerText,
|
||||
currentUser = currentUser,
|
||||
existingNotification = existingNotification,
|
||||
imageLoader = imageLoader,
|
||||
events = events,
|
||||
RoomEventGroupInfo(
|
||||
sessionId = currentUser.userId,
|
||||
roomId = roomId,
|
||||
roomDisplayName = roomName,
|
||||
isDm = roomIsDm,
|
||||
hasSmartReplyError = smartReplyErrors.isNotEmpty(),
|
||||
shouldBing = events.any { it.noisy },
|
||||
customSound = events.last().soundName,
|
||||
isUpdated = events.last().isUpdated,
|
||||
),
|
||||
threadId = lastKnownRoomEvent.threadId,
|
||||
largeIcon = largeBitmap,
|
||||
lastMessageTimestamp = lastMessageTimestamp,
|
||||
tickerText = tickerText,
|
||||
currentUser = currentUser,
|
||||
existingNotification = existingNotification,
|
||||
imageLoader = imageLoader,
|
||||
events = events,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import android.app.Notification
|
||||
import androidx.annotation.ColorInt
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -22,6 +23,7 @@ interface SummaryGroupMessageCreator {
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ class DefaultSummaryGroupMessageCreator(
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val summaryIsNoisy = roomNotifications.any { it.shouldBing } ||
|
||||
invitationNotifications.any { it.isNoisy } ||
|
||||
@@ -61,7 +64,8 @@ class DefaultSummaryGroupMessageCreator(
|
||||
currentUser,
|
||||
sumTitle,
|
||||
noisy = summaryIsNoisy,
|
||||
lastMessageTimestamp = lastMessageTimestamp
|
||||
lastMessageTimestamp = lastMessageTimestamp,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,13 @@ package io.element.android.libraries.push.impl.notifications.factories
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import coil3.ImageLoader
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
@@ -57,18 +54,22 @@ interface NotificationCreator {
|
||||
existingNotification: Notification?,
|
||||
imageLoader: ImageLoader,
|
||||
events: List<NotifiableMessageEvent>,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
|
||||
fun createRoomInvitationNotification(
|
||||
inviteNotifiableEvent: InviteNotifiableEvent
|
||||
inviteNotifiableEvent: InviteNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
|
||||
fun createSimpleEventNotification(
|
||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
|
||||
fun createFallbackNotification(
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
|
||||
/**
|
||||
@@ -78,10 +79,13 @@ interface NotificationCreator {
|
||||
currentUser: MatrixUser,
|
||||
compatSummary: String,
|
||||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long
|
||||
lastMessageTimestamp: Long,
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
|
||||
fun createDiagnosticNotification(): Notification
|
||||
fun createDiagnosticNotification(
|
||||
@ColorInt color: Int,
|
||||
): Notification
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
@@ -97,8 +101,6 @@ class DefaultNotificationCreator(
|
||||
private val acceptInvitationActionFactory: AcceptInvitationActionFactory,
|
||||
private val rejectInvitationActionFactory: RejectInvitationActionFactory
|
||||
) : NotificationCreator {
|
||||
private val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||
|
||||
/**
|
||||
* Create a notification for a Room.
|
||||
*/
|
||||
@@ -112,15 +114,14 @@ class DefaultNotificationCreator(
|
||||
existingNotification: Notification?,
|
||||
imageLoader: ImageLoader,
|
||||
events: List<NotifiableMessageEvent>,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
// Build the pending intent for when the notification is clicked
|
||||
val openIntent = when {
|
||||
threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo, threadId)
|
||||
else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId)
|
||||
}
|
||||
|
||||
val smallIcon = CommonDrawables.ic_notification
|
||||
|
||||
val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION }
|
||||
val channelId = if (containsMissedCall) {
|
||||
notificationChannels.getChannelForIncomingCall(false)
|
||||
@@ -176,7 +177,7 @@ class DefaultNotificationCreator(
|
||||
)
|
||||
.setSmallIcon(smallIcon)
|
||||
// Set primary color (important for Wear 2.0 Notifications).
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
// Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for
|
||||
// 'importance' which is set in the NotificationChannel. The integers representing
|
||||
// 'priority' are different from 'importance', so make sure you don't mix them.
|
||||
@@ -189,7 +190,7 @@ class DefaultNotificationCreator(
|
||||
setSound(it)
|
||||
}
|
||||
*/
|
||||
setLights(accentColor, 500, 500)
|
||||
setLights(color, 500, 500)
|
||||
} else {
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
}
|
||||
@@ -221,7 +222,8 @@ class DefaultNotificationCreator(
|
||||
}
|
||||
|
||||
override fun createRoomInvitationNotification(
|
||||
inviteNotifiableEvent: InviteNotifiableEvent
|
||||
inviteNotifiableEvent: InviteNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val smallIcon = CommonDrawables.ic_notification
|
||||
val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy)
|
||||
@@ -232,7 +234,7 @@ class DefaultNotificationCreator(
|
||||
.setGroup(inviteNotifiableEvent.sessionId.value)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
.apply {
|
||||
addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent))
|
||||
addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent))
|
||||
@@ -247,7 +249,7 @@ class DefaultNotificationCreator(
|
||||
setSound(it)
|
||||
}
|
||||
*/
|
||||
setLights(accentColor, 500, 500)
|
||||
setLights(color, 500, 500)
|
||||
} else {
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
}
|
||||
@@ -264,9 +266,9 @@ class DefaultNotificationCreator(
|
||||
|
||||
override fun createSimpleEventNotification(
|
||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val smallIcon = CommonDrawables.ic_notification
|
||||
|
||||
val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy)
|
||||
return NotificationCompat.Builder(context, channelId)
|
||||
.setOnlyAlertOnce(true)
|
||||
@@ -275,7 +277,7 @@ class DefaultNotificationCreator(
|
||||
.setGroup(simpleNotifiableEvent.sessionId.value)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(simpleNotifiableEvent.sessionId, simpleNotifiableEvent.roomId))
|
||||
.apply {
|
||||
@@ -287,7 +289,7 @@ class DefaultNotificationCreator(
|
||||
setSound(it)
|
||||
}
|
||||
*/
|
||||
setLights(accentColor, 500, 500)
|
||||
setLights(color, 500, 500)
|
||||
} else {
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
}
|
||||
@@ -297,9 +299,9 @@ class DefaultNotificationCreator(
|
||||
|
||||
override fun createFallbackNotification(
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val smallIcon = CommonDrawables.ic_notification
|
||||
|
||||
val channelId = notificationChannels.getChannelIdForMessage(false)
|
||||
return NotificationCompat.Builder(context, channelId)
|
||||
.setOnlyAlertOnce(true)
|
||||
@@ -308,7 +310,7 @@ class DefaultNotificationCreator(
|
||||
.setGroup(fallbackNotifiableEvent.sessionId.value)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
.setAutoCancel(true)
|
||||
.setWhen(fallbackNotifiableEvent.timestamp)
|
||||
// Ideally we'd use `createOpenRoomPendingIntent` here, but the broken notification might apply to an invite
|
||||
@@ -332,7 +334,8 @@ class DefaultNotificationCreator(
|
||||
currentUser: MatrixUser,
|
||||
compatSummary: String,
|
||||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long
|
||||
lastMessageTimestamp: Long,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val smallIcon = CommonDrawables.ic_notification
|
||||
val channelId = notificationChannels.getChannelIdForMessage(noisy)
|
||||
@@ -345,7 +348,7 @@ class DefaultNotificationCreator(
|
||||
.setGroup(currentUser.userId.value)
|
||||
// set this notification as the summary for the group
|
||||
.setGroupSummary(true)
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
.apply {
|
||||
if (noisy) {
|
||||
// Compat
|
||||
@@ -355,7 +358,7 @@ class DefaultNotificationCreator(
|
||||
setSound(it)
|
||||
}
|
||||
*/
|
||||
setLights(accentColor, 500, 500)
|
||||
setLights(color, 500, 500)
|
||||
} else {
|
||||
// compat
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
@@ -366,14 +369,15 @@ class DefaultNotificationCreator(
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun createDiagnosticNotification(): Notification {
|
||||
override fun createDiagnosticNotification(
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
val intent = pendingIntentFactory.createTestPendingIntent()
|
||||
return NotificationCompat.Builder(context, notificationChannels.getChannelIdForTest())
|
||||
.setContentTitle(buildMeta.applicationName)
|
||||
.setContentText(stringProvider.getString(R.string.notification_test_push_notification_content))
|
||||
.setSmallIcon(CommonDrawables.ic_notification)
|
||||
.setLargeIcon(getBitmap(R.drawable.element_logo_green))
|
||||
.setColor(accentColor)
|
||||
.setColor(color)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setAutoCancel(true)
|
||||
@@ -461,16 +465,6 @@ class DefaultNotificationCreator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBitmap(@DrawableRes drawableRes: Int): Bitmap? {
|
||||
val drawable = ResourcesCompat.getDrawable(context.resources, drawableRes, null) ?: return null
|
||||
val canvas = Canvas()
|
||||
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
|
||||
canvas.setBitmap(bitmap)
|
||||
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
|
||||
drawable.draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MESSAGE_EVENT_ID = "message_event_id"
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import androidx.core.app.NotificationCompat
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
||||
import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationActionIds
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
@@ -46,7 +46,7 @@ class AcceptInvitationActionFactory(
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
return NotificationCompat.Action.Builder(
|
||||
R.drawable.vector_notification_accept_invitation,
|
||||
CompoundDrawables.ic_compound_check,
|
||||
stringProvider.getString(CommonStrings.action_accept),
|
||||
pendingIntent
|
||||
).build()
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
||||
import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationActionIds
|
||||
@@ -46,7 +47,7 @@ class MarkAsReadActionFactory(
|
||||
)
|
||||
|
||||
return NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_material_done_all_white,
|
||||
CompoundDrawables.ic_compound_mark_as_read,
|
||||
stringProvider.getString(R.string.notification_room_action_mark_as_read),
|
||||
pendingIntent
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.core.app.RemoteInput
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
||||
import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
@@ -45,7 +46,7 @@ class QuickReplyActionFactory(
|
||||
.build()
|
||||
|
||||
return NotificationCompat.Action.Builder(
|
||||
R.drawable.vector_notification_quick_reply,
|
||||
CompoundDrawables.ic_compound_reply,
|
||||
stringProvider.getString(R.string.notification_room_action_quick_reply),
|
||||
replyPendingIntent
|
||||
)
|
||||
|
||||
@@ -14,8 +14,8 @@ import androidx.core.app.NotificationCompat
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
||||
import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationActionIds
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
@@ -46,7 +46,7 @@ class RejectInvitationActionFactory(
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
return NotificationCompat.Action.Builder(
|
||||
R.drawable.vector_notification_reject_invitation,
|
||||
CompoundDrawables.ic_compound_close,
|
||||
stringProvider.getString(CommonStrings.action_reject),
|
||||
pendingIntent
|
||||
).build()
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import dev.zacsweers.metro.ContributesIntoSet
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
||||
@@ -25,13 +29,15 @@ import kotlinx.coroutines.withTimeout
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@ContributesIntoSet(AppScope::class)
|
||||
@ContributesIntoSet(SessionScope::class)
|
||||
@Inject
|
||||
class NotificationTest(
|
||||
private val sessionId: SessionId,
|
||||
private val notificationCreator: NotificationCreator,
|
||||
private val notificationDisplayer: NotificationDisplayer,
|
||||
private val notificationClickHandler: NotificationClickHandler,
|
||||
private val stringProvider: StringProvider,
|
||||
private val enterpriseService: EnterpriseService,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 50
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
@@ -43,7 +49,9 @@ class NotificationTest(
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val notification = notificationCreator.createDiagnosticNotification()
|
||||
val color = enterpriseService.brandColorsFlow(sessionId).first()?.toArgb()
|
||||
?: NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||
val notification = notificationCreator.createDiagnosticNotification(color)
|
||||
val result = notificationDisplayer.displayDiagnosticNotification(notification)
|
||||
if (result) {
|
||||
coroutineScope.listenToNotificationClick()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M23.04,3.84C23.04,1.7192 24.7593,0 26.88,0C41.0185,0 52.48,11.4615 52.48,25.6C52.48,27.7208 50.7608,29.44 48.64,29.44C46.5193,29.44 44.8,27.7208 44.8,25.6C44.8,15.7031 36.777,7.68 26.88,7.68C24.7593,7.68 23.04,5.9608 23.04,3.84Z"
|
||||
android:fillColor="#0DBD8B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M40.96,60.16C40.96,62.2808 39.2407,64 37.12,64C22.9815,64 11.52,52.5385 11.52,38.4C11.52,36.2792 13.2392,34.56 15.36,34.56C17.4807,34.56 19.2,36.2792 19.2,38.4C19.2,48.2969 27.223,56.32 37.12,56.32C39.2407,56.32 40.96,58.0392 40.96,60.16Z"
|
||||
android:fillColor="#0DBD8B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M3.84,40.96C1.7192,40.96 -0,39.2407 -0,37.12C-0,22.9815 11.4615,11.52 25.6,11.52C27.7208,11.52 29.44,13.2392 29.44,15.36C29.44,17.4807 27.7208,19.2 25.6,19.2C15.7031,19.2 7.68,27.223 7.68,37.12C7.68,39.2407 5.9608,40.96 3.84,40.96Z"
|
||||
android:fillColor="#0DBD8B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M60.16,23.04C62.2808,23.04 64,24.7593 64,26.88C64,41.0185 52.5385,52.48 38.4,52.48C36.2792,52.48 34.56,50.7608 34.56,48.64C34.56,46.5193 36.2792,44.8 38.4,44.8C48.2969,44.8 56.32,36.777 56.32,26.88C56.32,24.7593 58.0392,23.04 60.16,23.04Z"
|
||||
android:fillColor="#0DBD8B"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 398 B |
Binary file not shown.
|
Before Width: | Height: | Size: 473 B |
Binary file not shown.
|
Before Width: | Height: | Size: 269 B |
Binary file not shown.
|
Before Width: | Height: | Size: 309 B |
@@ -13,6 +13,7 @@ import androidx.core.app.NotificationCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.test.A_COLOR_INT
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_TIMESTAMP
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
@@ -52,6 +53,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
assertThat(result.number).isEqualTo(1)
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -74,6 +76,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_DEFAULT)
|
||||
@@ -138,6 +141,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
assertThat(result.number).isEqualTo(1)
|
||||
assertThat(fakeImageLoader.getCoilRequests()).containsExactlyElementsIn(expectedCoilRequests)
|
||||
@@ -156,6 +160,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
assertThat(result.number).isEqualTo(2)
|
||||
assertThat(result.`when`).isEqualTo(A_TIMESTAMP + 10)
|
||||
@@ -184,6 +189,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
val actionTitles = result.actions?.map { it.title }
|
||||
assertThat(actionTitles).isEqualTo(
|
||||
@@ -208,6 +214,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
assertThat(result.number).isEqualTo(1)
|
||||
assertThat(result.`when`).isEqualTo(A_TIMESTAMP)
|
||||
|
||||
@@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||
import android.app.Notification
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
@@ -199,6 +200,7 @@ class DefaultNotificationDrawerManagerTest {
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
stringProvider = FakeStringProvider(),
|
||||
),
|
||||
enterpriseService = FakeEnterpriseService(),
|
||||
),
|
||||
appNavigationStateService = appNavigationStateService,
|
||||
coroutineScope = this,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
@@ -55,6 +56,7 @@ class DefaultOnMissedCallNotificationHandlerTest {
|
||||
notificationRenderer = NotificationRenderer(
|
||||
notificationDisplayer = FakeNotificationDisplayer(),
|
||||
notificationDataFactory = dataFactory,
|
||||
enterpriseService = FakeEnterpriseService(),
|
||||
),
|
||||
appNavigationStateService = FakeAppNavigationStateService(),
|
||||
coroutineScope = backgroundScope,
|
||||
|
||||
@@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||
import android.app.Notification
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_COLOR_INT
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator
|
||||
@@ -47,6 +48,7 @@ class DefaultSummaryGroupMessageCreatorTest {
|
||||
invitationNotifications = emptyList(),
|
||||
simpleNotifications = emptyList(),
|
||||
fallbackNotifications = emptyList(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
|
||||
notificationCreator.createSummaryListNotificationResult.assertions()
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_COLOR_INT
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider
|
||||
@@ -53,7 +54,7 @@ class NotificationDataFactoryTest {
|
||||
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
||||
val roomInvitation = listOf(AN_INVITATION_EVENT)
|
||||
|
||||
val result = toNotifications(roomInvitation)
|
||||
val result = toNotifications(roomInvitation, A_COLOR_INT)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
@@ -73,7 +74,7 @@ class NotificationDataFactoryTest {
|
||||
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
||||
val roomInvitation = listOf(A_SIMPLE_EVENT)
|
||||
|
||||
val result = toNotifications(roomInvitation)
|
||||
val result = toNotifications(roomInvitation, A_COLOR_INT)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
@@ -93,11 +94,12 @@ class NotificationDataFactoryTest {
|
||||
val events = listOf(A_MESSAGE_EVENT)
|
||||
val expectedNotification = RoomNotification(
|
||||
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
events,
|
||||
A_ROOM_ID,
|
||||
FakeImageLoader().getImageLoader(),
|
||||
null,
|
||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
events = events,
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = FakeImageLoader().getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
),
|
||||
roomId = A_ROOM_ID,
|
||||
summaryLine = "A room name: Bob Hello world!",
|
||||
@@ -112,6 +114,7 @@ class NotificationDataFactoryTest {
|
||||
messages = roomWithMessage,
|
||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
|
||||
assertThat(result.size).isEqualTo(1)
|
||||
@@ -128,6 +131,7 @@ class NotificationDataFactoryTest {
|
||||
messages = redactedRoom,
|
||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
|
||||
assertThat(result).isEmpty()
|
||||
@@ -145,11 +149,12 @@ class NotificationDataFactoryTest {
|
||||
val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted")))
|
||||
val expectedNotification = RoomNotification(
|
||||
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
withRedactedRemoved,
|
||||
A_ROOM_ID,
|
||||
FakeImageLoader().getImageLoader(),
|
||||
null,
|
||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
events = withRedactedRemoved,
|
||||
roomId = A_ROOM_ID,
|
||||
imageLoader = FakeImageLoader().getImageLoader(),
|
||||
existingNotification = null,
|
||||
color = A_COLOR_INT,
|
||||
),
|
||||
roomId = A_ROOM_ID,
|
||||
summaryLine = "A room name: Bob Hello world!",
|
||||
@@ -163,6 +168,7 @@ class NotificationDataFactoryTest {
|
||||
messages = roomWithRedactedMessage,
|
||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
imageLoader = fakeImageLoader.getImageLoader(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
|
||||
assertThat(result.size).isEqualTo(1)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
@@ -58,6 +59,7 @@ class NotificationRendererTest {
|
||||
private val notificationRenderer = NotificationRenderer(
|
||||
notificationDisplayer = notificationDisplayer,
|
||||
notificationDataFactory = notificationDataFactory,
|
||||
enterpriseService = FakeEnterpriseService(),
|
||||
)
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_COLOR_INT
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
@@ -50,7 +51,9 @@ class DefaultNotificationCreatorTest {
|
||||
@Test
|
||||
fun `test createDiagnosticNotification`() {
|
||||
val sut = createNotificationCreator()
|
||||
val result = sut.createDiagnosticNotification()
|
||||
val result = sut.createDiagnosticNotification(
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedGroup = null,
|
||||
expectedCategory = NotificationCompat.CATEGORY_STATUS,
|
||||
@@ -72,7 +75,8 @@ class DefaultNotificationCreatorTest {
|
||||
isUpdated = false,
|
||||
timestamp = A_FAKE_TIMESTAMP,
|
||||
cause = null,
|
||||
)
|
||||
),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
@@ -97,7 +101,8 @@ class DefaultNotificationCreatorTest {
|
||||
canBeReplaced = false,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
)
|
||||
),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
@@ -122,7 +127,8 @@ class DefaultNotificationCreatorTest {
|
||||
canBeReplaced = false,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
)
|
||||
),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
@@ -148,7 +154,8 @@ class DefaultNotificationCreatorTest {
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
roomName = "roomName",
|
||||
)
|
||||
),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
@@ -181,7 +188,8 @@ class DefaultNotificationCreatorTest {
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
roomName = "roomName",
|
||||
)
|
||||
),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
@@ -197,6 +205,7 @@ class DefaultNotificationCreatorTest {
|
||||
compatSummary = "compatSummary",
|
||||
noisy = false,
|
||||
lastMessageTimestamp = 123_456L,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedGroup = matrixUser.userId.value,
|
||||
@@ -212,6 +221,7 @@ class DefaultNotificationCreatorTest {
|
||||
compatSummary = "compatSummary",
|
||||
noisy = true,
|
||||
lastMessageTimestamp = 123_456L,
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedGroup = matrixUser.userId.value,
|
||||
@@ -240,6 +250,7 @@ class DefaultNotificationCreatorTest {
|
||||
existingNotification = null,
|
||||
imageLoader = FakeImageLoader().getImageLoader(),
|
||||
events = emptyList(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions()
|
||||
}
|
||||
@@ -266,6 +277,7 @@ class DefaultNotificationCreatorTest {
|
||||
existingNotification = null,
|
||||
imageLoader = FakeImageLoader().getImageLoader(),
|
||||
events = emptyList(),
|
||||
color = A_COLOR_INT,
|
||||
)
|
||||
result.commonAssertions()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications.fake
|
||||
|
||||
import android.app.Notification
|
||||
import android.graphics.Bitmap
|
||||
import androidx.annotation.ColorInt
|
||||
import coil3.ImageLoader
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -44,22 +45,32 @@ class FakeNotificationCreator(
|
||||
currentUser: MatrixUser,
|
||||
existingNotification: Notification?,
|
||||
imageLoader: ImageLoader,
|
||||
events: List<NotifiableMessageEvent>
|
||||
events: List<NotifiableMessageEvent>,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createMessagesListNotificationResult(
|
||||
listOf(roomInfo, threadId, largeIcon, lastMessageTimestamp, tickerText, currentUser, existingNotification, imageLoader, events)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createRoomInvitationNotification(inviteNotifiableEvent: InviteNotifiableEvent): Notification {
|
||||
override fun createRoomInvitationNotification(
|
||||
inviteNotifiableEvent: InviteNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createRoomInvitationNotificationResult(inviteNotifiableEvent)
|
||||
}
|
||||
|
||||
override fun createSimpleEventNotification(simpleNotifiableEvent: SimpleNotifiableEvent): Notification {
|
||||
override fun createSimpleEventNotification(
|
||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createSimpleNotificationResult(simpleNotifiableEvent)
|
||||
}
|
||||
|
||||
override fun createFallbackNotification(fallbackNotifiableEvent: FallbackNotifiableEvent): Notification {
|
||||
override fun createFallbackNotification(
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createFallbackNotificationResult(fallbackNotifiableEvent)
|
||||
}
|
||||
|
||||
@@ -67,12 +78,15 @@ class FakeNotificationCreator(
|
||||
currentUser: MatrixUser,
|
||||
compatSummary: String,
|
||||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long
|
||||
lastMessageTimestamp: Long,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createSummaryListNotificationResult(currentUser, compatSummary, noisy, lastMessageTimestamp)
|
||||
}
|
||||
|
||||
override fun createDiagnosticNotification(): Notification {
|
||||
override fun createDiagnosticNotification(
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createDiagnosticNotificationResult()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications.fake
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
import coil3.ImageLoader
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationDataFactory
|
||||
@@ -33,31 +34,45 @@ class FakeNotificationDataFactory(
|
||||
List<OneShotNotification>,
|
||||
List<OneShotNotification>,
|
||||
SummaryNotification
|
||||
> = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
||||
> = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
||||
var inviteToNotificationsResult: LambdaOneParamRecorder<List<InviteNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||
var simpleEventToNotificationsResult: LambdaOneParamRecorder<List<SimpleNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||
var fallbackEventToNotificationsResult: LambdaOneParamRecorder<List<FallbackNotifiableEvent>, List<OneShotNotification>> =
|
||||
lambdaRecorder { _ -> emptyList() },
|
||||
) : NotificationDataFactory {
|
||||
override suspend fun toNotifications(messages: List<NotifiableMessageEvent>, currentUser: MatrixUser, imageLoader: ImageLoader): List<RoomNotification> {
|
||||
override suspend fun toNotifications(
|
||||
messages: List<NotifiableMessageEvent>,
|
||||
currentUser: MatrixUser,
|
||||
imageLoader: ImageLoader,
|
||||
@ColorInt color: Int,
|
||||
): List<RoomNotification> {
|
||||
return messageEventToNotificationsResult(messages, currentUser, imageLoader)
|
||||
}
|
||||
|
||||
@JvmName("toNotificationInvites")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(invites: List<InviteNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
invites: List<InviteNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return inviteToNotificationsResult(invites)
|
||||
}
|
||||
|
||||
@JvmName("toNotificationSimpleEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(simpleEvents: List<SimpleNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
simpleEvents: List<SimpleNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return simpleEventToNotificationsResult(simpleEvents)
|
||||
}
|
||||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(fallback: List<FallbackNotifiableEvent>): List<OneShotNotification> {
|
||||
override fun toNotifications(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
@ColorInt color: Int,
|
||||
): List<OneShotNotification> {
|
||||
return fallbackEventToNotificationsResult(fallback)
|
||||
}
|
||||
|
||||
@@ -67,6 +82,7 @@ class FakeNotificationDataFactory(
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): SummaryNotification {
|
||||
return summaryToNotificationsResult(
|
||||
currentUser,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.push.impl.notifications.fake
|
||||
|
||||
import android.app.Notification
|
||||
import androidx.annotation.ColorInt
|
||||
import coil3.ImageLoader
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -19,14 +20,15 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeRoomGroupMessageCreator(
|
||||
var createRoomMessageResult: LambdaFiveParamsRecorder<MatrixUser, List<NotifiableMessageEvent>, RoomId, ImageLoader, Notification?, Notification> =
|
||||
lambdaRecorder { _, _, _, _, _, -> A_NOTIFICATION }
|
||||
lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }
|
||||
) : RoomGroupMessageCreator {
|
||||
override suspend fun createRoomMessage(
|
||||
currentUser: MatrixUser,
|
||||
events: List<NotifiableMessageEvent>,
|
||||
roomId: RoomId,
|
||||
imageLoader: ImageLoader,
|
||||
existingNotification: Notification?
|
||||
existingNotification: Notification?,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createRoomMessageResult(currentUser, events, roomId, imageLoader, existingNotification)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.push.impl.notifications.fake
|
||||
|
||||
import android.app.Notification
|
||||
import androidx.annotation.ColorInt
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.push.impl.notifications.OneShotNotification
|
||||
import io.element.android.libraries.push.impl.notifications.RoomNotification
|
||||
@@ -18,8 +19,7 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeSummaryGroupMessageCreator(
|
||||
var createSummaryNotificationResult: LambdaFiveParamsRecorder<
|
||||
MatrixUser, List<RoomNotification>, List<OneShotNotification>, List<OneShotNotification>, List<OneShotNotification>, Notification
|
||||
> =
|
||||
MatrixUser, List<RoomNotification>, List<OneShotNotification>, List<OneShotNotification>, List<OneShotNotification>, Notification> =
|
||||
lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }
|
||||
) : SummaryGroupMessageCreator {
|
||||
override fun createSummaryNotification(
|
||||
@@ -28,6 +28,7 @@ class FakeSummaryGroupMessageCreator(
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
@ColorInt color: Int,
|
||||
): Notification {
|
||||
return createSummaryNotificationResult(
|
||||
currentUser,
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
package io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
@@ -64,10 +66,12 @@ class NotificationTestTest {
|
||||
|
||||
private fun createNotificationTest(): NotificationTest {
|
||||
return NotificationTest(
|
||||
sessionId = A_SESSION_ID,
|
||||
notificationCreator = notificationCreator,
|
||||
notificationDisplayer = fakeNotificationDisplayer,
|
||||
notificationClickHandler = notificationClickHandler,
|
||||
stringProvider = FakeStringProvider(),
|
||||
enterpriseService = FakeEnterpriseService(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user