Merge branch 'develop' into feature/fga/space_flow_inject_room
This commit is contained in:
commit
0aff4e9779
75 changed files with 561 additions and 337 deletions
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
package io.element.android.appconfig
|
package io.element.android.appconfig
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
|
|
||||||
object NotificationConfig {
|
object NotificationConfig {
|
||||||
/**
|
/**
|
||||||
|
|
@ -27,5 +27,5 @@ object NotificationConfig {
|
||||||
const val SHOW_QUICK_REPLY_ACTION = true
|
const val SHOW_QUICK_REPLY_ACTION = true
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
val NOTIFICATION_ACCENT_COLOR: Int = Color.parseColor("#FF0DBD8B")
|
val NOTIFICATION_ACCENT_COLOR: Int = "#FF0DBD8B".toColorInt()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 867d1118e157ba89a4f5462f8d9c13e206f10026
|
Subproject commit 19d78b589dfbca08b1e8306bff1a236fa2cdf528
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.enterprise.api
|
package io.element.android.features.enterprise.api
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -24,6 +25,8 @@ interface EnterpriseService {
|
||||||
*/
|
*/
|
||||||
suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?)
|
suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?)
|
||||||
|
|
||||||
|
fun brandColorsFlow(sessionId: SessionId?): Flow<Color?>
|
||||||
|
|
||||||
fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark>
|
fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark>
|
||||||
|
|
||||||
fun firebasePushGateway(): String?
|
fun firebasePushGateway(): String?
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.enterprise.impl
|
package io.element.android.features.enterprise.impl
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||||
|
|
@ -27,6 +28,10 @@ class DefaultEnterpriseService : EnterpriseService {
|
||||||
|
|
||||||
override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit
|
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> {
|
override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
|
||||||
return flowOf(SemanticColorsLightDark.default)
|
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
|
@Test
|
||||||
fun `semanticColorsFlow always emits the same value for a session`() = runTest {
|
fun `semanticColorsFlow always emits the same value for a session`() = runTest {
|
||||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.enterprise.test
|
package io.element.android.features.enterprise.test
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||||
import io.element.android.features.enterprise.api.BugReportUrl
|
import io.element.android.features.enterprise.api.BugReportUrl
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
|
|
@ -27,6 +28,7 @@ class FakeEnterpriseService(
|
||||||
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
|
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
|
||||||
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
|
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
|
||||||
) : EnterpriseService {
|
) : EnterpriseService {
|
||||||
|
private val brandColorState = MutableStateFlow<Color?>(null)
|
||||||
private val semanticColorsState = MutableStateFlow(initialSemanticColors)
|
private val semanticColorsState = MutableStateFlow(initialSemanticColors)
|
||||||
|
|
||||||
override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask {
|
override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask {
|
||||||
|
|
@ -45,6 +47,10 @@ class FakeEnterpriseService(
|
||||||
overrideBrandColorResult(sessionId, brandColor)
|
overrideBrandColorResult(sessionId, brandColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun brandColorsFlow(sessionId: SessionId?): Flow<Color?> {
|
||||||
|
return brandColorState.asStateFlow()
|
||||||
|
}
|
||||||
|
|
||||||
override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
|
override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
|
||||||
return semanticColorsState.asStateFlow()
|
return semanticColorsState.asStateFlow()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package io.element.android.features.home.impl.spaces
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -57,20 +58,24 @@ fun HomeSpacesView(
|
||||||
item {
|
item {
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
state.spaceRooms.forEach { spaceRoom ->
|
itemsIndexed(
|
||||||
item(spaceRoom.roomId) {
|
items = state.spaceRooms,
|
||||||
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED
|
key = { _, spaceRoom -> spaceRoom.roomId }
|
||||||
SpaceRoomItemView(
|
) { index, spaceRoom ->
|
||||||
spaceRoom = spaceRoom,
|
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED
|
||||||
showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites,
|
SpaceRoomItemView(
|
||||||
hideAvatars = isInvitation && state.hideInvitesAvatar,
|
spaceRoom = spaceRoom,
|
||||||
onClick = {
|
showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites,
|
||||||
onSpaceClick(spaceRoom.roomId)
|
hideAvatars = isInvitation && state.hideInvitesAvatar,
|
||||||
},
|
onClick = {
|
||||||
onLongClick = {
|
onSpaceClick(spaceRoom.roomId)
|
||||||
// TODO
|
},
|
||||||
},
|
onLongClick = {
|
||||||
)
|
// TODO
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if (index != state.spaceRooms.lastIndex) {
|
||||||
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.space.impl.root
|
package io.element.android.features.space.impl.root
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
|
@ -14,6 +15,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -186,32 +188,36 @@ private fun SpaceViewContent(
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.children.forEach { spaceRoom ->
|
itemsIndexed(
|
||||||
item {
|
items = state.children,
|
||||||
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED
|
key = { _, spaceRoom -> spaceRoom.roomId }
|
||||||
val isCurrentlyJoining = state.isJoining(spaceRoom.roomId)
|
) { index, spaceRoom ->
|
||||||
SpaceRoomItemView(
|
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED
|
||||||
spaceRoom = spaceRoom,
|
val isCurrentlyJoining = state.isJoining(spaceRoom.roomId)
|
||||||
showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites,
|
SpaceRoomItemView(
|
||||||
hideAvatars = isInvitation && state.hideInvitesAvatar,
|
spaceRoom = spaceRoom,
|
||||||
onClick = {
|
showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites,
|
||||||
onRoomClick(spaceRoom)
|
hideAvatars = isInvitation && state.hideInvitesAvatar,
|
||||||
|
onClick = {
|
||||||
|
onRoomClick(spaceRoom)
|
||||||
|
},
|
||||||
|
onLongClick = {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
trailingAction = spaceRoom.trailingAction(isCurrentlyJoining = isCurrentlyJoining) {
|
||||||
|
state.eventSink(SpaceEvents.Join(spaceRoom))
|
||||||
|
},
|
||||||
|
bottomAction = spaceRoom.inviteButtons(
|
||||||
|
onAcceptClick = {
|
||||||
|
state.eventSink(SpaceEvents.AcceptInvite(spaceRoom))
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onDeclineClick = {
|
||||||
// TODO
|
state.eventSink(SpaceEvents.DeclineInvite(spaceRoom))
|
||||||
},
|
}
|
||||||
trailingAction = spaceRoom.trailingAction(isCurrentlyJoining = isCurrentlyJoining) {
|
|
||||||
state.eventSink(SpaceEvents.Join(spaceRoom))
|
|
||||||
},
|
|
||||||
bottomAction = spaceRoom.inviteButtons(
|
|
||||||
onAcceptClick = {
|
|
||||||
state.eventSink(SpaceEvents.AcceptInvite(spaceRoom))
|
|
||||||
},
|
|
||||||
onDeclineClick = {
|
|
||||||
state.eventSink(SpaceEvents.DeclineInvite(spaceRoom))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
if (index != state.children.lastIndex) {
|
||||||
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.hasMoreToLoad) {
|
if (state.hasMoreToLoad) {
|
||||||
|
|
@ -266,7 +272,7 @@ private fun SpaceViewTopBar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(roundedCornerShape)
|
.clip(roundedCornerShape)
|
||||||
// TODO enable when screen ready for space
|
// TODO enable when screen ready for space
|
||||||
// .clickable(onClick = onDetailsClick)
|
.clickable(enabled = false, onClick = onDetailsClick)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -338,10 +344,10 @@ private fun SpaceAvatarAndNameRow(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 8.dp)
|
.padding(horizontal = 8.dp)
|
||||||
.semantics {
|
.semantics {
|
||||||
heading()
|
heading()
|
||||||
},
|
},
|
||||||
text = name ?: stringResource(CommonStrings.common_no_space_name),
|
text = name ?: stringResource(CommonStrings.common_no_space_name),
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
fontStyle = FontStyle.Italic.takeIf { name == null },
|
fontStyle = FontStyle.Italic.takeIf { name == null },
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import kotlinx.serialization.json.Json
|
||||||
/**
|
/**
|
||||||
* Provides a Json instance configured to ignore unknown keys.
|
* Provides a Json instance configured to ignore unknown keys.
|
||||||
*/
|
*/
|
||||||
interface JsonProvider : Provider<Json>
|
fun interface JsonProvider : Provider<Json>
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
|
|
|
||||||
|
|
@ -98,3 +98,5 @@ const val A_TIMESTAMP = 567L
|
||||||
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
||||||
|
|
||||||
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
||||||
|
|
||||||
|
const val A_COLOR_INT = 0xFF0000
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||||
import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction
|
import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.unreadIndicator
|
import io.element.android.libraries.designsystem.theme.unreadIndicator
|
||||||
|
|
@ -81,56 +80,50 @@ fun SpaceRoomItemView(
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
)
|
)
|
||||||
.onKeyboardContextMenuAction { onLongClick }
|
.onKeyboardContextMenuAction { onLongClick }
|
||||||
Box(modifier = modifier.then(clickModifier)) {
|
Column(
|
||||||
Column(
|
modifier = modifier
|
||||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
|
.then(clickModifier)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
) {
|
||||||
|
SpaceRoomItemScaffold(
|
||||||
|
avatarData = spaceRoom.getAvatarData(AvatarSize.SpaceListItem),
|
||||||
|
isSpace = spaceRoom.isSpace,
|
||||||
|
hideAvatars = hideAvatars,
|
||||||
|
heroes = spaceRoom.heroes
|
||||||
|
.map { hero -> hero.getAvatarData(AvatarSize.SpaceListItem) }
|
||||||
|
.toImmutableList(),
|
||||||
|
trailingAction = trailingAction,
|
||||||
) {
|
) {
|
||||||
SpaceRoomItemScaffold(
|
NameAndIndicatorRow(
|
||||||
avatarData = spaceRoom.getAvatarData(AvatarSize.SpaceListItem),
|
name = spaceRoom.displayName,
|
||||||
isSpace = spaceRoom.isSpace,
|
showIndicator = showUnreadIndicator
|
||||||
hideAvatars = hideAvatars,
|
)
|
||||||
heroes = spaceRoom.heroes
|
Spacer(modifier = Modifier.height(1.dp))
|
||||||
.map { hero -> hero.getAvatarData(AvatarSize.SpaceListItem) }
|
SubtitleRow(
|
||||||
.toImmutableList(),
|
visibilityIcon = spaceRoom.visibilityIcon(),
|
||||||
trailingAction = trailingAction,
|
subtitle = spaceRoom.subtitle()
|
||||||
) {
|
)
|
||||||
NameAndIndicatorRow(
|
Spacer(modifier = Modifier.height(1.dp))
|
||||||
name = spaceRoom.displayName,
|
val info = spaceRoom.info()
|
||||||
showIndicator = showUnreadIndicator
|
if (info.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
|
text = info,
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(1.dp))
|
|
||||||
SubtitleRow(
|
|
||||||
visibilityIcon = spaceRoom.visibilityIcon(),
|
|
||||||
subtitle = spaceRoom.subtitle()
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(1.dp))
|
|
||||||
val info = spaceRoom.info()
|
|
||||||
if (info.isNotBlank()) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
|
||||||
text = info,
|
|
||||||
color = ElementTheme.colors.textSecondary,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bottomAction != null) {
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
// Match the padding of the text content (avatar + spacer)
|
|
||||||
Box(modifier = Modifier.padding(start = AvatarSize.SpaceListItem.dp + 16.dp)) {
|
|
||||||
bottomAction()
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HorizontalDivider(
|
if (bottomAction != null) {
|
||||||
modifier = Modifier
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
// Match the padding of the text content (padding + avatar + spacer)
|
// Match the padding of the text content (avatar + spacer)
|
||||||
.padding(start = AvatarSize.SpaceListItem.dp + 16.dp + 16.dp)
|
Box(modifier = Modifier.padding(start = AvatarSize.SpaceListItem.dp + 16.dp)) {
|
||||||
.align(Alignment.BottomCenter)
|
bottomAction()
|
||||||
)
|
}
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,7 +257,6 @@ internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class
|
||||||
hideAvatars = false,
|
hideAvatars = false,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
modifier = Modifier.fillMaxWidth().padding(8.dp),
|
|
||||||
bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) {
|
bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) {
|
||||||
{ InviteButtonsRowMolecule({}, {}) }
|
{ InviteButtonsRowMolecule({}, {}) }
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ dependencies {
|
||||||
implementation(projects.libraries.troubleshoot.api)
|
implementation(projects.libraries.troubleshoot.api)
|
||||||
implementation(projects.libraries.workmanager.api)
|
implementation(projects.libraries.workmanager.api)
|
||||||
implementation(projects.features.call.api)
|
implementation(projects.features.call.api)
|
||||||
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.features.lockscreen.api)
|
implementation(projects.features.lockscreen.api)
|
||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
api(projects.libraries.pushproviders.api)
|
api(projects.libraries.pushproviders.api)
|
||||||
|
|
@ -77,6 +78,7 @@ dependencies {
|
||||||
testImplementation(projects.libraries.troubleshoot.test)
|
testImplementation(projects.libraries.troubleshoot.test)
|
||||||
testImplementation(projects.libraries.workmanager.test)
|
testImplementation(projects.libraries.workmanager.test)
|
||||||
testImplementation(projects.features.call.test)
|
testImplementation(projects.features.call.test)
|
||||||
|
testImplementation(projects.features.enterprise.test)
|
||||||
testImplementation(projects.features.lockscreen.test)
|
testImplementation(projects.features.lockscreen.test)
|
||||||
testImplementation(projects.features.networkmonitor.test)
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
testImplementation(projects.services.appnavstate.test)
|
testImplementation(projects.services.appnavstate.test)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.text.buildSpannedString
|
import androidx.core.text.buildSpannedString
|
||||||
import androidx.core.text.inSpans
|
import androidx.core.text.inSpans
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
|
|
@ -31,17 +32,29 @@ interface NotificationDataFactory {
|
||||||
messages: List<NotifiableMessageEvent>,
|
messages: List<NotifiableMessageEvent>,
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
|
@ColorInt color: Int,
|
||||||
): List<RoomNotification>
|
): List<RoomNotification>
|
||||||
|
|
||||||
@JvmName("toNotificationInvites")
|
@JvmName("toNotificationInvites")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
fun toNotifications(invites: List<InviteNotifiableEvent>): List<OneShotNotification>
|
fun toNotifications(
|
||||||
|
invites: List<InviteNotifiableEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): List<OneShotNotification>
|
||||||
|
|
||||||
@JvmName("toNotificationSimpleEvents")
|
@JvmName("toNotificationSimpleEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
fun toNotifications(simpleEvents: List<SimpleNotifiableEvent>): List<OneShotNotification>
|
fun toNotifications(
|
||||||
|
simpleEvents: List<SimpleNotifiableEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): List<OneShotNotification>
|
||||||
|
|
||||||
@JvmName("toNotificationFallbackEvents")
|
@JvmName("toNotificationFallbackEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
fun toNotifications(fallback: List<FallbackNotifiableEvent>): List<OneShotNotification>
|
fun toNotifications(
|
||||||
|
fallback: List<FallbackNotifiableEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): List<OneShotNotification>
|
||||||
|
|
||||||
fun createSummaryNotification(
|
fun createSummaryNotification(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
|
|
@ -49,6 +62,7 @@ interface NotificationDataFactory {
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): SummaryNotification
|
): SummaryNotification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,6 +78,7 @@ class DefaultNotificationDataFactory(
|
||||||
messages: List<NotifiableMessageEvent>,
|
messages: List<NotifiableMessageEvent>,
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
|
@ColorInt color: Int,
|
||||||
): List<RoomNotification> {
|
): List<RoomNotification> {
|
||||||
val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() }
|
val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() }
|
||||||
.groupBy { it.roomId }
|
.groupBy { it.roomId }
|
||||||
|
|
@ -76,6 +91,7 @@ class DefaultNotificationDataFactory(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
imageLoader = imageLoader,
|
imageLoader = imageLoader,
|
||||||
existingNotification = getExistingNotificationForMessages(currentUser.userId, roomId),
|
existingNotification = getExistingNotificationForMessages(currentUser.userId, roomId),
|
||||||
|
color = color,
|
||||||
)
|
)
|
||||||
RoomNotification(
|
RoomNotification(
|
||||||
notification = notification,
|
notification = notification,
|
||||||
|
|
@ -96,11 +112,14 @@ class DefaultNotificationDataFactory(
|
||||||
|
|
||||||
@JvmName("toNotificationInvites")
|
@JvmName("toNotificationInvites")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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 ->
|
return invites.map { event ->
|
||||||
OneShotNotification(
|
OneShotNotification(
|
||||||
key = event.roomId.value,
|
key = event.roomId.value,
|
||||||
notification = notificationCreator.createRoomInvitationNotification(event),
|
notification = notificationCreator.createRoomInvitationNotification(event, color),
|
||||||
summaryLine = event.description,
|
summaryLine = event.description,
|
||||||
isNoisy = event.noisy,
|
isNoisy = event.noisy,
|
||||||
timestamp = event.timestamp
|
timestamp = event.timestamp
|
||||||
|
|
@ -110,11 +129,14 @@ class DefaultNotificationDataFactory(
|
||||||
|
|
||||||
@JvmName("toNotificationSimpleEvents")
|
@JvmName("toNotificationSimpleEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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 ->
|
return simpleEvents.map { event ->
|
||||||
OneShotNotification(
|
OneShotNotification(
|
||||||
key = event.eventId.value,
|
key = event.eventId.value,
|
||||||
notification = notificationCreator.createSimpleEventNotification(event),
|
notification = notificationCreator.createSimpleEventNotification(event, color),
|
||||||
summaryLine = event.description,
|
summaryLine = event.description,
|
||||||
isNoisy = event.noisy,
|
isNoisy = event.noisy,
|
||||||
timestamp = event.timestamp
|
timestamp = event.timestamp
|
||||||
|
|
@ -124,11 +146,14 @@ class DefaultNotificationDataFactory(
|
||||||
|
|
||||||
@JvmName("toNotificationFallbackEvents")
|
@JvmName("toNotificationFallbackEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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 ->
|
return fallback.map { event ->
|
||||||
OneShotNotification(
|
OneShotNotification(
|
||||||
key = event.eventId.value,
|
key = event.eventId.value,
|
||||||
notification = notificationCreator.createFallbackNotification(event),
|
notification = notificationCreator.createFallbackNotification(event, color),
|
||||||
summaryLine = event.description.orEmpty(),
|
summaryLine = event.description.orEmpty(),
|
||||||
isNoisy = false,
|
isNoisy = false,
|
||||||
timestamp = event.timestamp
|
timestamp = event.timestamp
|
||||||
|
|
@ -142,6 +167,7 @@ class DefaultNotificationDataFactory(
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): SummaryNotification {
|
): SummaryNotification {
|
||||||
return when {
|
return when {
|
||||||
roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty() -> SummaryNotification.Removed
|
roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty() -> SummaryNotification.Removed
|
||||||
|
|
@ -152,6 +178,7 @@ class DefaultNotificationDataFactory(
|
||||||
invitationNotifications = invitationNotifications,
|
invitationNotifications = invitationNotifications,
|
||||||
simpleNotifications = simpleNotifications,
|
simpleNotifications = simpleNotifications,
|
||||||
fallbackNotifications = fallbackNotifications,
|
fallbackNotifications = fallbackNotifications,
|
||||||
|
color = color,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import dev.zacsweers.metro.Inject
|
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.core.log.logger.LoggerTag
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
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.NotifiableMessageEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
private val loggerTag = LoggerTag("NotificationRenderer", LoggerTag.NotificationLoggerTag)
|
private val loggerTag = LoggerTag("NotificationRenderer", LoggerTag.NotificationLoggerTag)
|
||||||
|
|
@ -26,6 +30,7 @@ private val loggerTag = LoggerTag("NotificationRenderer", LoggerTag.Notification
|
||||||
class NotificationRenderer(
|
class NotificationRenderer(
|
||||||
private val notificationDisplayer: NotificationDisplayer,
|
private val notificationDisplayer: NotificationDisplayer,
|
||||||
private val notificationDataFactory: NotificationDataFactory,
|
private val notificationDataFactory: NotificationDataFactory,
|
||||||
|
private val enterpriseService: EnterpriseService,
|
||||||
) {
|
) {
|
||||||
suspend fun render(
|
suspend fun render(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
|
|
@ -33,17 +38,20 @@ class NotificationRenderer(
|
||||||
eventsToProcess: List<NotifiableEvent>,
|
eventsToProcess: List<NotifiableEvent>,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
) {
|
) {
|
||||||
|
val color = enterpriseService.brandColorsFlow(currentUser.userId).first()?.toArgb()
|
||||||
|
?: NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||||
val groupedEvents = eventsToProcess.groupByType()
|
val groupedEvents = eventsToProcess.groupByType()
|
||||||
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader)
|
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader, color)
|
||||||
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents)
|
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, color)
|
||||||
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents)
|
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, color)
|
||||||
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents)
|
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, color)
|
||||||
val summaryNotification = notificationDataFactory.createSummaryNotification(
|
val summaryNotification = notificationDataFactory.createSummaryNotification(
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
roomNotifications = roomNotifications,
|
roomNotifications = roomNotifications,
|
||||||
invitationNotifications = invitationNotifications,
|
invitationNotifications = invitationNotifications,
|
||||||
simpleNotifications = simpleNotifications,
|
simpleNotifications = simpleNotifications,
|
||||||
fallbackNotifications = fallbackNotifications,
|
fallbackNotifications = fallbackNotifications,
|
||||||
|
color = color,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remove summary first to avoid briefly displaying it after dismissing the last notification
|
// Remove summary first to avoid briefly displaying it after dismissing the last notification
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import dev.zacsweers.metro.SingleIn
|
import dev.zacsweers.metro.SingleIn
|
||||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
|
||||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
||||||
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
||||||
import io.element.android.libraries.push.impl.workmanager.SyncNotificationWorkManagerRequest
|
import io.element.android.libraries.push.impl.workmanager.SyncNotificationWorkManagerRequest
|
||||||
|
import io.element.android.libraries.push.impl.workmanager.WorkerDataConverter
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -48,7 +48,7 @@ class DefaultNotificationResolverQueue(
|
||||||
private val appCoroutineScope: CoroutineScope,
|
private val appCoroutineScope: CoroutineScope,
|
||||||
private val workManagerScheduler: WorkManagerScheduler,
|
private val workManagerScheduler: WorkManagerScheduler,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
private val json: JsonProvider,
|
private val workerDataConverter: WorkerDataConverter,
|
||||||
) : NotificationResolverQueue {
|
) : NotificationResolverQueue {
|
||||||
companion object {
|
companion object {
|
||||||
private const val BATCH_WINDOW_MS = 250L
|
private const val BATCH_WINDOW_MS = 250L
|
||||||
|
|
@ -99,7 +99,7 @@ class DefaultNotificationResolverQueue(
|
||||||
SyncNotificationWorkManagerRequest(
|
SyncNotificationWorkManagerRequest(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
notificationEventRequests = requests,
|
notificationEventRequests = requests,
|
||||||
json = json,
|
workerDataConverter = workerDataConverter,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
|
|
@ -28,6 +29,7 @@ interface RoomGroupMessageCreator {
|
||||||
roomId: RoomId,
|
roomId: RoomId,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
existingNotification: Notification?,
|
existingNotification: Notification?,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,6 +45,7 @@ class DefaultRoomGroupMessageCreator(
|
||||||
roomId: RoomId,
|
roomId: RoomId,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
existingNotification: Notification?,
|
existingNotification: Notification?,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val lastKnownRoomEvent = events.last()
|
val lastKnownRoomEvent = events.last()
|
||||||
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)"
|
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 smartReplyErrors = events.filter { it.isSmartReplyError() }
|
||||||
val roomIsDm = !roomIsGroup
|
val roomIsDm = !roomIsGroup
|
||||||
return notificationCreator.createMessagesListNotification(
|
return notificationCreator.createMessagesListNotification(
|
||||||
RoomEventGroupInfo(
|
RoomEventGroupInfo(
|
||||||
sessionId = currentUser.userId,
|
sessionId = currentUser.userId,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
roomDisplayName = roomName,
|
roomDisplayName = roomName,
|
||||||
isDm = roomIsDm,
|
isDm = roomIsDm,
|
||||||
hasSmartReplyError = smartReplyErrors.isNotEmpty(),
|
hasSmartReplyError = smartReplyErrors.isNotEmpty(),
|
||||||
shouldBing = events.any { it.noisy },
|
shouldBing = events.any { it.noisy },
|
||||||
customSound = events.last().soundName,
|
customSound = events.last().soundName,
|
||||||
isUpdated = events.last().isUpdated,
|
isUpdated = events.last().isUpdated,
|
||||||
),
|
),
|
||||||
threadId = lastKnownRoomEvent.threadId,
|
threadId = lastKnownRoomEvent.threadId,
|
||||||
largeIcon = largeBitmap,
|
largeIcon = largeBitmap,
|
||||||
lastMessageTimestamp = lastMessageTimestamp,
|
lastMessageTimestamp = lastMessageTimestamp,
|
||||||
tickerText = tickerText,
|
tickerText = tickerText,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
existingNotification = existingNotification,
|
existingNotification = existingNotification,
|
||||||
imageLoader = imageLoader,
|
imageLoader = imageLoader,
|
||||||
events = events,
|
events = events,
|
||||||
|
color = color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -22,6 +23,7 @@ interface SummaryGroupMessageCreator {
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +47,7 @@ class DefaultSummaryGroupMessageCreator(
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val summaryIsNoisy = roomNotifications.any { it.shouldBing } ||
|
val summaryIsNoisy = roomNotifications.any { it.shouldBing } ||
|
||||||
invitationNotifications.any { it.isNoisy } ||
|
invitationNotifications.any { it.isNoisy } ||
|
||||||
|
|
@ -61,7 +64,8 @@ class DefaultSummaryGroupMessageCreator(
|
||||||
currentUser,
|
currentUser,
|
||||||
sumTitle,
|
sumTitle,
|
||||||
noisy = summaryIsNoisy,
|
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.app.Notification
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||||
import androidx.core.app.Person
|
import androidx.core.app.Person
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.appconfig.NotificationConfig
|
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||||
|
|
@ -57,18 +54,22 @@ interface NotificationCreator {
|
||||||
existingNotification: Notification?,
|
existingNotification: Notification?,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
events: List<NotifiableMessageEvent>,
|
events: List<NotifiableMessageEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
|
|
||||||
fun createRoomInvitationNotification(
|
fun createRoomInvitationNotification(
|
||||||
inviteNotifiableEvent: InviteNotifiableEvent
|
inviteNotifiableEvent: InviteNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
|
|
||||||
fun createSimpleEventNotification(
|
fun createSimpleEventNotification(
|
||||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
|
|
||||||
fun createFallbackNotification(
|
fun createFallbackNotification(
|
||||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,10 +79,13 @@ interface NotificationCreator {
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
compatSummary: String,
|
compatSummary: String,
|
||||||
noisy: Boolean,
|
noisy: Boolean,
|
||||||
lastMessageTimestamp: Long
|
lastMessageTimestamp: Long,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification
|
): Notification
|
||||||
|
|
||||||
fun createDiagnosticNotification(): Notification
|
fun createDiagnosticNotification(
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
|
|
@ -97,8 +101,6 @@ class DefaultNotificationCreator(
|
||||||
private val acceptInvitationActionFactory: AcceptInvitationActionFactory,
|
private val acceptInvitationActionFactory: AcceptInvitationActionFactory,
|
||||||
private val rejectInvitationActionFactory: RejectInvitationActionFactory
|
private val rejectInvitationActionFactory: RejectInvitationActionFactory
|
||||||
) : NotificationCreator {
|
) : NotificationCreator {
|
||||||
private val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a notification for a Room.
|
* Create a notification for a Room.
|
||||||
*/
|
*/
|
||||||
|
|
@ -112,15 +114,14 @@ class DefaultNotificationCreator(
|
||||||
existingNotification: Notification?,
|
existingNotification: Notification?,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
events: List<NotifiableMessageEvent>,
|
events: List<NotifiableMessageEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
// Build the pending intent for when the notification is clicked
|
// Build the pending intent for when the notification is clicked
|
||||||
val openIntent = when {
|
val openIntent = when {
|
||||||
threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo, threadId)
|
threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo, threadId)
|
||||||
else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId)
|
else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val smallIcon = CommonDrawables.ic_notification
|
val smallIcon = CommonDrawables.ic_notification
|
||||||
|
|
||||||
val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION }
|
val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION }
|
||||||
val channelId = if (containsMissedCall) {
|
val channelId = if (containsMissedCall) {
|
||||||
notificationChannels.getChannelForIncomingCall(false)
|
notificationChannels.getChannelForIncomingCall(false)
|
||||||
|
|
@ -176,7 +177,7 @@ class DefaultNotificationCreator(
|
||||||
)
|
)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
// Set primary color (important for Wear 2.0 Notifications).
|
// 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
|
// Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for
|
||||||
// 'importance' which is set in the NotificationChannel. The integers representing
|
// 'importance' which is set in the NotificationChannel. The integers representing
|
||||||
// 'priority' are different from 'importance', so make sure you don't mix them.
|
// 'priority' are different from 'importance', so make sure you don't mix them.
|
||||||
|
|
@ -189,7 +190,7 @@ class DefaultNotificationCreator(
|
||||||
setSound(it)
|
setSound(it)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
setLights(accentColor, 500, 500)
|
setLights(color, 500, 500)
|
||||||
} else {
|
} else {
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +222,8 @@ class DefaultNotificationCreator(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createRoomInvitationNotification(
|
override fun createRoomInvitationNotification(
|
||||||
inviteNotifiableEvent: InviteNotifiableEvent
|
inviteNotifiableEvent: InviteNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val smallIcon = CommonDrawables.ic_notification
|
val smallIcon = CommonDrawables.ic_notification
|
||||||
val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy)
|
val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy)
|
||||||
|
|
@ -232,7 +234,7 @@ class DefaultNotificationCreator(
|
||||||
.setGroup(inviteNotifiableEvent.sessionId.value)
|
.setGroup(inviteNotifiableEvent.sessionId.value)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setColor(accentColor)
|
.setColor(color)
|
||||||
.apply {
|
.apply {
|
||||||
addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent))
|
addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent))
|
||||||
addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent))
|
addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent))
|
||||||
|
|
@ -247,7 +249,7 @@ class DefaultNotificationCreator(
|
||||||
setSound(it)
|
setSound(it)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
setLights(accentColor, 500, 500)
|
setLights(color, 500, 500)
|
||||||
} else {
|
} else {
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
}
|
}
|
||||||
|
|
@ -264,9 +266,9 @@ class DefaultNotificationCreator(
|
||||||
|
|
||||||
override fun createSimpleEventNotification(
|
override fun createSimpleEventNotification(
|
||||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val smallIcon = CommonDrawables.ic_notification
|
val smallIcon = CommonDrawables.ic_notification
|
||||||
|
|
||||||
val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy)
|
val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy)
|
||||||
return NotificationCompat.Builder(context, channelId)
|
return NotificationCompat.Builder(context, channelId)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
|
|
@ -275,7 +277,7 @@ class DefaultNotificationCreator(
|
||||||
.setGroup(simpleNotifiableEvent.sessionId.value)
|
.setGroup(simpleNotifiableEvent.sessionId.value)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setColor(accentColor)
|
.setColor(color)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(simpleNotifiableEvent.sessionId, simpleNotifiableEvent.roomId))
|
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(simpleNotifiableEvent.sessionId, simpleNotifiableEvent.roomId))
|
||||||
.apply {
|
.apply {
|
||||||
|
|
@ -287,7 +289,7 @@ class DefaultNotificationCreator(
|
||||||
setSound(it)
|
setSound(it)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
setLights(accentColor, 500, 500)
|
setLights(color, 500, 500)
|
||||||
} else {
|
} else {
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
}
|
}
|
||||||
|
|
@ -297,9 +299,9 @@ class DefaultNotificationCreator(
|
||||||
|
|
||||||
override fun createFallbackNotification(
|
override fun createFallbackNotification(
|
||||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val smallIcon = CommonDrawables.ic_notification
|
val smallIcon = CommonDrawables.ic_notification
|
||||||
|
|
||||||
val channelId = notificationChannels.getChannelIdForMessage(false)
|
val channelId = notificationChannels.getChannelIdForMessage(false)
|
||||||
return NotificationCompat.Builder(context, channelId)
|
return NotificationCompat.Builder(context, channelId)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
|
|
@ -308,7 +310,7 @@ class DefaultNotificationCreator(
|
||||||
.setGroup(fallbackNotifiableEvent.sessionId.value)
|
.setGroup(fallbackNotifiableEvent.sessionId.value)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setColor(accentColor)
|
.setColor(color)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setWhen(fallbackNotifiableEvent.timestamp)
|
.setWhen(fallbackNotifiableEvent.timestamp)
|
||||||
// Ideally we'd use `createOpenRoomPendingIntent` here, but the broken notification might apply to an invite
|
// Ideally we'd use `createOpenRoomPendingIntent` here, but the broken notification might apply to an invite
|
||||||
|
|
@ -332,7 +334,8 @@ class DefaultNotificationCreator(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
compatSummary: String,
|
compatSummary: String,
|
||||||
noisy: Boolean,
|
noisy: Boolean,
|
||||||
lastMessageTimestamp: Long
|
lastMessageTimestamp: Long,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
val smallIcon = CommonDrawables.ic_notification
|
val smallIcon = CommonDrawables.ic_notification
|
||||||
val channelId = notificationChannels.getChannelIdForMessage(noisy)
|
val channelId = notificationChannels.getChannelIdForMessage(noisy)
|
||||||
|
|
@ -345,7 +348,7 @@ class DefaultNotificationCreator(
|
||||||
.setGroup(currentUser.userId.value)
|
.setGroup(currentUser.userId.value)
|
||||||
// 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(color)
|
||||||
.apply {
|
.apply {
|
||||||
if (noisy) {
|
if (noisy) {
|
||||||
// Compat
|
// Compat
|
||||||
|
|
@ -355,7 +358,7 @@ class DefaultNotificationCreator(
|
||||||
setSound(it)
|
setSound(it)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
setLights(accentColor, 500, 500)
|
setLights(color, 500, 500)
|
||||||
} else {
|
} else {
|
||||||
// compat
|
// compat
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
|
|
@ -366,14 +369,15 @@ class DefaultNotificationCreator(
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createDiagnosticNotification(): Notification {
|
override fun createDiagnosticNotification(
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): Notification {
|
||||||
val intent = pendingIntentFactory.createTestPendingIntent()
|
val intent = pendingIntentFactory.createTestPendingIntent()
|
||||||
return NotificationCompat.Builder(context, notificationChannels.getChannelIdForTest())
|
return NotificationCompat.Builder(context, notificationChannels.getChannelIdForTest())
|
||||||
.setContentTitle(buildMeta.applicationName)
|
.setContentTitle(buildMeta.applicationName)
|
||||||
.setContentText(stringProvider.getString(R.string.notification_test_push_notification_content))
|
.setContentText(stringProvider.getString(R.string.notification_test_push_notification_content))
|
||||||
.setSmallIcon(CommonDrawables.ic_notification)
|
.setSmallIcon(CommonDrawables.ic_notification)
|
||||||
.setLargeIcon(getBitmap(R.drawable.element_logo_green))
|
.setColor(color)
|
||||||
.setColor(accentColor)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||||
.setAutoCancel(true)
|
.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 {
|
companion object {
|
||||||
const val MESSAGE_EVENT_ID = "message_event_id"
|
const val MESSAGE_EVENT_ID = "message_event_id"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import androidx.core.app.NotificationCompat
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
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.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.NotificationActionIds
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
||||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||||
|
|
@ -46,7 +46,7 @@ class AcceptInvitationActionFactory(
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
return NotificationCompat.Action.Builder(
|
return NotificationCompat.Action.Builder(
|
||||||
R.drawable.vector_notification_accept_invitation,
|
CompoundDrawables.ic_compound_check,
|
||||||
stringProvider.getString(CommonStrings.action_accept),
|
stringProvider.getString(CommonStrings.action_accept),
|
||||||
pendingIntent
|
pendingIntent
|
||||||
).build()
|
).build()
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
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.di.annotations.ApplicationContext
|
||||||
import io.element.android.libraries.push.impl.R
|
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.NotificationActionIds
|
||||||
|
|
@ -46,7 +47,7 @@ class MarkAsReadActionFactory(
|
||||||
)
|
)
|
||||||
|
|
||||||
return NotificationCompat.Action.Builder(
|
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),
|
stringProvider.getString(R.string.notification_room_action_mark_as_read),
|
||||||
pendingIntent
|
pendingIntent
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import androidx.core.app.RemoteInput
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
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.di.annotations.ApplicationContext
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -45,7 +46,7 @@ class QuickReplyActionFactory(
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return NotificationCompat.Action.Builder(
|
return NotificationCompat.Action.Builder(
|
||||||
R.drawable.vector_notification_quick_reply,
|
CompoundDrawables.ic_compound_reply,
|
||||||
stringProvider.getString(R.string.notification_room_action_quick_reply),
|
stringProvider.getString(R.string.notification_room_action_quick_reply),
|
||||||
replyPendingIntent
|
replyPendingIntent
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import androidx.core.app.NotificationCompat
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.androidutils.uri.createIgnoredUri
|
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.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.NotificationActionIds
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver
|
||||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||||
|
|
@ -46,7 +46,7 @@ class RejectInvitationActionFactory(
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
return NotificationCompat.Action.Builder(
|
return NotificationCompat.Action.Builder(
|
||||||
R.drawable.vector_notification_reject_invitation,
|
CompoundDrawables.ic_compound_close,
|
||||||
stringProvider.getString(CommonStrings.action_reject),
|
stringProvider.getString(CommonStrings.action_reject),
|
||||||
pendingIntent
|
pendingIntent
|
||||||
).build()
|
).build()
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.troubleshoot
|
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.ContributesIntoSet
|
||||||
import dev.zacsweers.metro.Inject
|
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.R
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
|
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
|
||||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
||||||
|
|
@ -25,13 +29,15 @@ import kotlinx.coroutines.withTimeout
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@ContributesIntoSet(AppScope::class)
|
@ContributesIntoSet(SessionScope::class)
|
||||||
@Inject
|
@Inject
|
||||||
class NotificationTest(
|
class NotificationTest(
|
||||||
|
private val sessionId: SessionId,
|
||||||
private val notificationCreator: NotificationCreator,
|
private val notificationCreator: NotificationCreator,
|
||||||
private val notificationDisplayer: NotificationDisplayer,
|
private val notificationDisplayer: NotificationDisplayer,
|
||||||
private val notificationClickHandler: NotificationClickHandler,
|
private val notificationClickHandler: NotificationClickHandler,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
|
private val enterpriseService: EnterpriseService,
|
||||||
) : NotificationTroubleshootTest {
|
) : NotificationTroubleshootTest {
|
||||||
override val order = 50
|
override val order = 50
|
||||||
private val delegate = NotificationTroubleshootTestDelegate(
|
private val delegate = NotificationTroubleshootTestDelegate(
|
||||||
|
|
@ -43,7 +49,9 @@ class NotificationTest(
|
||||||
|
|
||||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||||
delegate.start()
|
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)
|
val result = notificationDisplayer.displayDiagnosticNotification(notification)
|
||||||
if (result) {
|
if (result) {
|
||||||
coroutineScope.listenToNotificationClick()
|
coroutineScope.listenToNotificationClick()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import dev.zacsweers.metro.ContributesIntoMap
|
||||||
import dev.zacsweers.metro.binding
|
import dev.zacsweers.metro.binding
|
||||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||||
|
|
@ -47,20 +46,11 @@ class FetchNotificationsWorker(
|
||||||
private val workManagerScheduler: WorkManagerScheduler,
|
private val workManagerScheduler: WorkManagerScheduler,
|
||||||
private val syncOnNotifiableEvent: SyncOnNotifiableEvent,
|
private val syncOnNotifiableEvent: SyncOnNotifiableEvent,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val json: JsonProvider,
|
private val workerDataConverter: WorkerDataConverter,
|
||||||
) : CoroutineWorker(context, workerParams) {
|
) : CoroutineWorker(context, workerParams) {
|
||||||
override suspend fun doWork(): Result = withContext(coroutineDispatchers.io) {
|
override suspend fun doWork(): Result = withContext(coroutineDispatchers.io) {
|
||||||
Timber.d("FetchNotificationsWorker started")
|
Timber.d("FetchNotificationsWorker started")
|
||||||
val rawRequestsJson = inputData.getString("requests") ?: return@withContext Result.failure()
|
val requests = workerDataConverter.deserialize(inputData) ?: return@withContext Result.failure()
|
||||||
val requests = runCatchingExceptions {
|
|
||||||
json().decodeFromString<List<SyncNotificationWorkManagerRequest.Data>>(rawRequestsJson).map { it.toRequest() }
|
|
||||||
}.getOrElse {
|
|
||||||
Timber.e(it, "Failed to deserialize notification requests")
|
|
||||||
return@withContext Result.failure()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Deserialized ${requests.size} requests")
|
|
||||||
|
|
||||||
// Wait for network to be available, but not more than 10 seconds
|
// Wait for network to be available, but not more than 10 seconds
|
||||||
val hasNetwork = withTimeoutOrNull(10.seconds) {
|
val hasNetwork = withTimeoutOrNull(10.seconds) {
|
||||||
networkMonitor.connectivity.first { it == NetworkStatus.Connected }
|
networkMonitor.connectivity.first { it == NetworkStatus.Connected }
|
||||||
|
|
@ -97,7 +87,7 @@ class FetchNotificationsWorker(
|
||||||
SyncNotificationWorkManagerRequest(
|
SyncNotificationWorkManagerRequest(
|
||||||
sessionId = failedSessionId,
|
sessionId = failedSessionId,
|
||||||
notificationEventRequests = requestsToRetry,
|
notificationEventRequests = requestsToRetry,
|
||||||
json = json,
|
workerDataConverter = workerDataConverter,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,6 @@ package io.element.android.libraries.push.impl.workmanager
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.OutOfQuotaPolicy
|
import androidx.work.OutOfQuotaPolicy
|
||||||
import androidx.work.WorkRequest
|
import androidx.work.WorkRequest
|
||||||
import androidx.work.workDataOf
|
|
||||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
||||||
|
|
@ -28,24 +23,19 @@ import java.security.InvalidParameterException
|
||||||
class SyncNotificationWorkManagerRequest(
|
class SyncNotificationWorkManagerRequest(
|
||||||
private val sessionId: SessionId,
|
private val sessionId: SessionId,
|
||||||
private val notificationEventRequests: List<NotificationEventRequest>,
|
private val notificationEventRequests: List<NotificationEventRequest>,
|
||||||
private val json: JsonProvider,
|
private val workerDataConverter: WorkerDataConverter,
|
||||||
) : WorkManagerRequest {
|
) : WorkManagerRequest {
|
||||||
override fun build(): Result<WorkRequest> {
|
override fun build(): Result<WorkRequest> {
|
||||||
if (notificationEventRequests.isEmpty()) {
|
if (notificationEventRequests.isEmpty()) {
|
||||||
return Result.failure(InvalidParameterException("notificationEventRequests cannot be empty"))
|
return Result.failure(InvalidParameterException("notificationEventRequests cannot be empty"))
|
||||||
}
|
}
|
||||||
|
val data = workerDataConverter.serialize(notificationEventRequests).getOrElse {
|
||||||
val json = runCatchingExceptions { json().encodeToString(notificationEventRequests.map { it.toData() }) }
|
return Result.failure(it)
|
||||||
.getOrElse {
|
}
|
||||||
Timber.e(it, "Failed to serialize notification requests")
|
|
||||||
return Result.failure(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Scheduling ${notificationEventRequests.size} notification requests with WorkManager for $sessionId")
|
Timber.d("Scheduling ${notificationEventRequests.size} notification requests with WorkManager for $sessionId")
|
||||||
|
|
||||||
return Result.success(
|
return Result.success(
|
||||||
OneTimeWorkRequestBuilder<FetchNotificationsWorker>()
|
OneTimeWorkRequestBuilder<FetchNotificationsWorker>()
|
||||||
.setInputData(workDataOf("requests" to json))
|
.setInputData(data)
|
||||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||||
.setTraceTag(workManagerTag(sessionId, WorkManagerRequestType.NOTIFICATION_SYNC))
|
.setTraceTag(workManagerTag(sessionId, WorkManagerRequestType.NOTIFICATION_SYNC))
|
||||||
// TODO investigate using this instead of the resolver queue
|
// TODO investigate using this instead of the resolver queue
|
||||||
|
|
@ -64,23 +54,5 @@ class SyncNotificationWorkManagerRequest(
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
@SerialName("provider_info")
|
@SerialName("provider_info")
|
||||||
val providerInfo: String,
|
val providerInfo: String,
|
||||||
) {
|
|
||||||
fun toRequest(): NotificationEventRequest {
|
|
||||||
return NotificationEventRequest(
|
|
||||||
sessionId = SessionId(sessionId),
|
|
||||||
roomId = RoomId(roomId),
|
|
||||||
eventId = EventId(eventId),
|
|
||||||
providerInfo = providerInfo,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun NotificationEventRequest.toData(): SyncNotificationWorkManagerRequest.Data {
|
|
||||||
return SyncNotificationWorkManagerRequest.Data(
|
|
||||||
sessionId = sessionId.value,
|
|
||||||
roomId = roomId.value,
|
|
||||||
eventId = eventId.value,
|
|
||||||
providerInfo = providerInfo,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.push.impl.workmanager
|
||||||
|
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.workDataOf
|
||||||
|
import dev.zacsweers.metro.Inject
|
||||||
|
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||||
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
class WorkerDataConverter(
|
||||||
|
private val json: JsonProvider,
|
||||||
|
) {
|
||||||
|
fun serialize(notificationEventRequests: List<NotificationEventRequest>): Result<Data> {
|
||||||
|
return runCatchingExceptions { json().encodeToString(notificationEventRequests.map { it.toData() }) }
|
||||||
|
.onFailure {
|
||||||
|
Timber.e(it, "Failed to serialize notification requests")
|
||||||
|
}
|
||||||
|
.map { str ->
|
||||||
|
workDataOf(REQUESTS_KEY to str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deserialize(data: Data): List<NotificationEventRequest>? {
|
||||||
|
val rawRequestsJson = data.getString(REQUESTS_KEY) ?: return null
|
||||||
|
return runCatchingExceptions {
|
||||||
|
json().decodeFromString<List<SyncNotificationWorkManagerRequest.Data>>(rawRequestsJson).map { it.toRequest() }
|
||||||
|
}.fold(
|
||||||
|
onSuccess = {
|
||||||
|
Timber.d("Deserialized ${it.size} requests")
|
||||||
|
it
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
Timber.e(it, "Failed to deserialize notification requests")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val REQUESTS_KEY = "requests"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NotificationEventRequest.toData(): SyncNotificationWorkManagerRequest.Data {
|
||||||
|
return SyncNotificationWorkManagerRequest.Data(
|
||||||
|
sessionId = sessionId.value,
|
||||||
|
roomId = roomId.value,
|
||||||
|
eventId = eventId.value,
|
||||||
|
providerInfo = providerInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SyncNotificationWorkManagerRequest.Data.toRequest(): NotificationEventRequest {
|
||||||
|
return NotificationEventRequest(
|
||||||
|
sessionId = SessionId(sessionId),
|
||||||
|
roomId = RoomId(roomId),
|
||||||
|
eventId = EventId(eventId),
|
||||||
|
providerInfo = providerInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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 com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
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_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_TIMESTAMP
|
import io.element.android.libraries.matrix.test.A_TIMESTAMP
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
|
|
@ -52,6 +53,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
assertThat(result.number).isEqualTo(1)
|
assertThat(result.number).isEqualTo(1)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
|
@ -74,6 +76,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_DEFAULT)
|
assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
|
@ -138,6 +141,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
assertThat(result.number).isEqualTo(1)
|
assertThat(result.number).isEqualTo(1)
|
||||||
assertThat(fakeImageLoader.getCoilRequests()).containsExactlyElementsIn(expectedCoilRequests)
|
assertThat(fakeImageLoader.getCoilRequests()).containsExactlyElementsIn(expectedCoilRequests)
|
||||||
|
|
@ -156,6 +160,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
assertThat(result.number).isEqualTo(2)
|
assertThat(result.number).isEqualTo(2)
|
||||||
assertThat(result.`when`).isEqualTo(A_TIMESTAMP + 10)
|
assertThat(result.`when`).isEqualTo(A_TIMESTAMP + 10)
|
||||||
|
|
@ -184,6 +189,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
val actionTitles = result.actions?.map { it.title }
|
val actionTitles = result.actions?.map { it.title }
|
||||||
assertThat(actionTitles).isEqualTo(
|
assertThat(actionTitles).isEqualTo(
|
||||||
|
|
@ -208,6 +214,7 @@ class DefaultBaseRoomGroupMessageCreatorTest {
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
assertThat(result.number).isEqualTo(1)
|
assertThat(result.number).isEqualTo(1)
|
||||||
assertThat(result.`when`).isEqualTo(A_TIMESTAMP)
|
assertThat(result.`when`).isEqualTo(A_TIMESTAMP)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.google.common.truth.Truth.assertThat
|
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_AVATAR_URL
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
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_ROOM_ID
|
||||||
|
|
@ -199,6 +200,7 @@ class DefaultNotificationDrawerManagerTest {
|
||||||
activeNotificationsProvider = activeNotificationsProvider,
|
activeNotificationsProvider = activeNotificationsProvider,
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
),
|
),
|
||||||
|
enterpriseService = FakeEnterpriseService(),
|
||||||
),
|
),
|
||||||
appNavigationStateService = appNavigationStateService,
|
appNavigationStateService = appNavigationStateService,
|
||||||
coroutineScope = this,
|
coroutineScope = this,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications
|
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.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
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_SESSION_ID
|
||||||
|
|
@ -55,6 +56,7 @@ class DefaultOnMissedCallNotificationHandlerTest {
|
||||||
notificationRenderer = NotificationRenderer(
|
notificationRenderer = NotificationRenderer(
|
||||||
notificationDisplayer = FakeNotificationDisplayer(),
|
notificationDisplayer = FakeNotificationDisplayer(),
|
||||||
notificationDataFactory = dataFactory,
|
notificationDataFactory = dataFactory,
|
||||||
|
enterpriseService = FakeEnterpriseService(),
|
||||||
),
|
),
|
||||||
appNavigationStateService = FakeAppNavigationStateService(),
|
appNavigationStateService = FakeAppNavigationStateService(),
|
||||||
coroutineScope = backgroundScope,
|
coroutineScope = backgroundScope,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import com.google.common.truth.Truth.assertThat
|
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.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator
|
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator
|
||||||
|
|
@ -47,6 +48,7 @@ class DefaultSummaryGroupMessageCreatorTest {
|
||||||
invitationNotifications = emptyList(),
|
invitationNotifications = emptyList(),
|
||||||
simpleNotifications = emptyList(),
|
simpleNotifications = emptyList(),
|
||||||
fallbackNotifications = emptyList(),
|
fallbackNotifications = emptyList(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
|
|
||||||
notificationCreator.createSummaryListNotificationResult.assertions()
|
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.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
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.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_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider
|
import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider
|
||||||
|
|
@ -53,7 +54,7 @@ class NotificationDataFactoryTest {
|
||||||
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
||||||
val roomInvitation = listOf(AN_INVITATION_EVENT)
|
val roomInvitation = listOf(AN_INVITATION_EVENT)
|
||||||
|
|
||||||
val result = toNotifications(roomInvitation)
|
val result = toNotifications(roomInvitation, A_COLOR_INT)
|
||||||
|
|
||||||
assertThat(result).isEqualTo(
|
assertThat(result).isEqualTo(
|
||||||
listOf(
|
listOf(
|
||||||
|
|
@ -73,7 +74,7 @@ class NotificationDataFactoryTest {
|
||||||
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT)
|
||||||
val roomInvitation = listOf(A_SIMPLE_EVENT)
|
val roomInvitation = listOf(A_SIMPLE_EVENT)
|
||||||
|
|
||||||
val result = toNotifications(roomInvitation)
|
val result = toNotifications(roomInvitation, A_COLOR_INT)
|
||||||
|
|
||||||
assertThat(result).isEqualTo(
|
assertThat(result).isEqualTo(
|
||||||
listOf(
|
listOf(
|
||||||
|
|
@ -93,11 +94,12 @@ class NotificationDataFactoryTest {
|
||||||
val events = listOf(A_MESSAGE_EVENT)
|
val events = listOf(A_MESSAGE_EVENT)
|
||||||
val expectedNotification = RoomNotification(
|
val expectedNotification = RoomNotification(
|
||||||
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
||||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||||
events,
|
events = events,
|
||||||
A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
FakeImageLoader().getImageLoader(),
|
imageLoader = FakeImageLoader().getImageLoader(),
|
||||||
null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
),
|
),
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
summaryLine = "A room name: Bob Hello world!",
|
summaryLine = "A room name: Bob Hello world!",
|
||||||
|
|
@ -112,6 +114,7 @@ class NotificationDataFactoryTest {
|
||||||
messages = roomWithMessage,
|
messages = roomWithMessage,
|
||||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertThat(result.size).isEqualTo(1)
|
assertThat(result.size).isEqualTo(1)
|
||||||
|
|
@ -128,6 +131,7 @@ class NotificationDataFactoryTest {
|
||||||
messages = redactedRoom,
|
messages = redactedRoom,
|
||||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertThat(result).isEmpty()
|
assertThat(result).isEmpty()
|
||||||
|
|
@ -145,11 +149,12 @@ class NotificationDataFactoryTest {
|
||||||
val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted")))
|
val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted")))
|
||||||
val expectedNotification = RoomNotification(
|
val expectedNotification = RoomNotification(
|
||||||
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
notification = fakeRoomGroupMessageCreator.createRoomMessage(
|
||||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||||
withRedactedRemoved,
|
events = withRedactedRemoved,
|
||||||
A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
FakeImageLoader().getImageLoader(),
|
imageLoader = FakeImageLoader().getImageLoader(),
|
||||||
null,
|
existingNotification = null,
|
||||||
|
color = A_COLOR_INT,
|
||||||
),
|
),
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
summaryLine = "A room name: Bob Hello world!",
|
summaryLine = "A room name: Bob Hello world!",
|
||||||
|
|
@ -163,6 +168,7 @@ class NotificationDataFactoryTest {
|
||||||
messages = roomWithRedactedMessage,
|
messages = roomWithRedactedMessage,
|
||||||
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||||
imageLoader = fakeImageLoader.getImageLoader(),
|
imageLoader = fakeImageLoader.getImageLoader(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertThat(result.size).isEqualTo(1)
|
assertThat(result.size).isEqualTo(1)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications
|
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.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
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_ROOM_ID
|
||||||
|
|
@ -58,6 +59,7 @@ class NotificationRendererTest {
|
||||||
private val notificationRenderer = NotificationRenderer(
|
private val notificationRenderer = NotificationRenderer(
|
||||||
notificationDisplayer = notificationDisplayer,
|
notificationDisplayer = notificationDisplayer,
|
||||||
notificationDataFactory = notificationDataFactory,
|
notificationDataFactory = notificationDataFactory,
|
||||||
|
enterpriseService = FakeEnterpriseService(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
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.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_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||||
|
|
@ -50,7 +51,9 @@ class DefaultNotificationCreatorTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test createDiagnosticNotification`() {
|
fun `test createDiagnosticNotification`() {
|
||||||
val sut = createNotificationCreator()
|
val sut = createNotificationCreator()
|
||||||
val result = sut.createDiagnosticNotification()
|
val result = sut.createDiagnosticNotification(
|
||||||
|
color = A_COLOR_INT,
|
||||||
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedGroup = null,
|
expectedGroup = null,
|
||||||
expectedCategory = NotificationCompat.CATEGORY_STATUS,
|
expectedCategory = NotificationCompat.CATEGORY_STATUS,
|
||||||
|
|
@ -72,7 +75,8 @@ class DefaultNotificationCreatorTest {
|
||||||
isUpdated = false,
|
isUpdated = false,
|
||||||
timestamp = A_FAKE_TIMESTAMP,
|
timestamp = A_FAKE_TIMESTAMP,
|
||||||
cause = null,
|
cause = null,
|
||||||
)
|
),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedCategory = null,
|
expectedCategory = null,
|
||||||
|
|
@ -97,7 +101,8 @@ class DefaultNotificationCreatorTest {
|
||||||
canBeReplaced = false,
|
canBeReplaced = false,
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
isUpdated = false,
|
isUpdated = false,
|
||||||
)
|
),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedCategory = null,
|
expectedCategory = null,
|
||||||
|
|
@ -122,7 +127,8 @@ class DefaultNotificationCreatorTest {
|
||||||
canBeReplaced = false,
|
canBeReplaced = false,
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
isUpdated = false,
|
isUpdated = false,
|
||||||
)
|
),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedCategory = null,
|
expectedCategory = null,
|
||||||
|
|
@ -148,7 +154,8 @@ class DefaultNotificationCreatorTest {
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
isUpdated = false,
|
isUpdated = false,
|
||||||
roomName = "roomName",
|
roomName = "roomName",
|
||||||
)
|
),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedCategory = null,
|
expectedCategory = null,
|
||||||
|
|
@ -181,7 +188,8 @@ class DefaultNotificationCreatorTest {
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
isUpdated = false,
|
isUpdated = false,
|
||||||
roomName = "roomName",
|
roomName = "roomName",
|
||||||
)
|
),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedCategory = null,
|
expectedCategory = null,
|
||||||
|
|
@ -197,6 +205,7 @@ class DefaultNotificationCreatorTest {
|
||||||
compatSummary = "compatSummary",
|
compatSummary = "compatSummary",
|
||||||
noisy = false,
|
noisy = false,
|
||||||
lastMessageTimestamp = 123_456L,
|
lastMessageTimestamp = 123_456L,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedGroup = matrixUser.userId.value,
|
expectedGroup = matrixUser.userId.value,
|
||||||
|
|
@ -212,6 +221,7 @@ class DefaultNotificationCreatorTest {
|
||||||
compatSummary = "compatSummary",
|
compatSummary = "compatSummary",
|
||||||
noisy = true,
|
noisy = true,
|
||||||
lastMessageTimestamp = 123_456L,
|
lastMessageTimestamp = 123_456L,
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions(
|
result.commonAssertions(
|
||||||
expectedGroup = matrixUser.userId.value,
|
expectedGroup = matrixUser.userId.value,
|
||||||
|
|
@ -240,6 +250,7 @@ class DefaultNotificationCreatorTest {
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
imageLoader = FakeImageLoader().getImageLoader(),
|
imageLoader = FakeImageLoader().getImageLoader(),
|
||||||
events = emptyList(),
|
events = emptyList(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions()
|
result.commonAssertions()
|
||||||
}
|
}
|
||||||
|
|
@ -266,6 +277,7 @@ class DefaultNotificationCreatorTest {
|
||||||
existingNotification = null,
|
existingNotification = null,
|
||||||
imageLoader = FakeImageLoader().getImageLoader(),
|
imageLoader = FakeImageLoader().getImageLoader(),
|
||||||
events = emptyList(),
|
events = emptyList(),
|
||||||
|
color = A_COLOR_INT,
|
||||||
)
|
)
|
||||||
result.commonAssertions()
|
result.commonAssertions()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -44,22 +45,32 @@ class FakeNotificationCreator(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
existingNotification: Notification?,
|
existingNotification: Notification?,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
events: List<NotifiableMessageEvent>
|
events: List<NotifiableMessageEvent>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
return createMessagesListNotificationResult(
|
return createMessagesListNotificationResult(
|
||||||
listOf(roomInfo, threadId, largeIcon, lastMessageTimestamp, tickerText, currentUser, existingNotification, imageLoader, events)
|
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)
|
return createRoomInvitationNotificationResult(inviteNotifiableEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createSimpleEventNotification(simpleNotifiableEvent: SimpleNotifiableEvent): Notification {
|
override fun createSimpleEventNotification(
|
||||||
|
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): Notification {
|
||||||
return createSimpleNotificationResult(simpleNotifiableEvent)
|
return createSimpleNotificationResult(simpleNotifiableEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFallbackNotification(fallbackNotifiableEvent: FallbackNotifiableEvent): Notification {
|
override fun createFallbackNotification(
|
||||||
|
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): Notification {
|
||||||
return createFallbackNotificationResult(fallbackNotifiableEvent)
|
return createFallbackNotificationResult(fallbackNotifiableEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,12 +78,15 @@ class FakeNotificationCreator(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
compatSummary: String,
|
compatSummary: String,
|
||||||
noisy: Boolean,
|
noisy: Boolean,
|
||||||
lastMessageTimestamp: Long
|
lastMessageTimestamp: Long,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
return createSummaryListNotificationResult(currentUser, compatSummary, noisy, lastMessageTimestamp)
|
return createSummaryListNotificationResult(currentUser, compatSummary, noisy, lastMessageTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createDiagnosticNotification(): Notification {
|
override fun createDiagnosticNotification(
|
||||||
|
@ColorInt color: Int,
|
||||||
|
): Notification {
|
||||||
return createDiagnosticNotificationResult()
|
return createDiagnosticNotificationResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications.fake
|
package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDataFactory
|
import io.element.android.libraries.push.impl.notifications.NotificationDataFactory
|
||||||
|
|
@ -33,31 +34,45 @@ class FakeNotificationDataFactory(
|
||||||
List<OneShotNotification>,
|
List<OneShotNotification>,
|
||||||
List<OneShotNotification>,
|
List<OneShotNotification>,
|
||||||
SummaryNotification
|
SummaryNotification
|
||||||
> = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
> = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
||||||
var inviteToNotificationsResult: LambdaOneParamRecorder<List<InviteNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
var inviteToNotificationsResult: LambdaOneParamRecorder<List<InviteNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||||
var simpleEventToNotificationsResult: LambdaOneParamRecorder<List<SimpleNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
var simpleEventToNotificationsResult: LambdaOneParamRecorder<List<SimpleNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||||
var fallbackEventToNotificationsResult: LambdaOneParamRecorder<List<FallbackNotifiableEvent>, List<OneShotNotification>> =
|
var fallbackEventToNotificationsResult: LambdaOneParamRecorder<List<FallbackNotifiableEvent>, List<OneShotNotification>> =
|
||||||
lambdaRecorder { _ -> emptyList() },
|
lambdaRecorder { _ -> emptyList() },
|
||||||
) : NotificationDataFactory {
|
) : 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)
|
return messageEventToNotificationsResult(messages, currentUser, imageLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("toNotificationInvites")
|
@JvmName("toNotificationInvites")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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)
|
return inviteToNotificationsResult(invites)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("toNotificationSimpleEvents")
|
@JvmName("toNotificationSimpleEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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)
|
return simpleEventToNotificationsResult(simpleEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("toNotificationFallbackEvents")
|
@JvmName("toNotificationFallbackEvents")
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
@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)
|
return fallbackEventToNotificationsResult(fallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +82,7 @@ class FakeNotificationDataFactory(
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): SummaryNotification {
|
): SummaryNotification {
|
||||||
return summaryToNotificationsResult(
|
return summaryToNotificationsResult(
|
||||||
currentUser,
|
currentUser,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.fake
|
package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -19,14 +20,15 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
||||||
class FakeRoomGroupMessageCreator(
|
class FakeRoomGroupMessageCreator(
|
||||||
var createRoomMessageResult: LambdaFiveParamsRecorder<MatrixUser, List<NotifiableMessageEvent>, RoomId, ImageLoader, Notification?, Notification> =
|
var createRoomMessageResult: LambdaFiveParamsRecorder<MatrixUser, List<NotifiableMessageEvent>, RoomId, ImageLoader, Notification?, Notification> =
|
||||||
lambdaRecorder { _, _, _, _, _, -> A_NOTIFICATION }
|
lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }
|
||||||
) : RoomGroupMessageCreator {
|
) : RoomGroupMessageCreator {
|
||||||
override suspend fun createRoomMessage(
|
override suspend fun createRoomMessage(
|
||||||
currentUser: MatrixUser,
|
currentUser: MatrixUser,
|
||||||
events: List<NotifiableMessageEvent>,
|
events: List<NotifiableMessageEvent>,
|
||||||
roomId: RoomId,
|
roomId: RoomId,
|
||||||
imageLoader: ImageLoader,
|
imageLoader: ImageLoader,
|
||||||
existingNotification: Notification?
|
existingNotification: Notification?,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
return createRoomMessageResult(currentUser, events, roomId, imageLoader, existingNotification)
|
return createRoomMessageResult(currentUser, events, roomId, imageLoader, existingNotification)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.fake
|
package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
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.OneShotNotification
|
||||||
import io.element.android.libraries.push.impl.notifications.RoomNotification
|
import io.element.android.libraries.push.impl.notifications.RoomNotification
|
||||||
|
|
@ -18,8 +19,7 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
||||||
class FakeSummaryGroupMessageCreator(
|
class FakeSummaryGroupMessageCreator(
|
||||||
var createSummaryNotificationResult: LambdaFiveParamsRecorder<
|
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 }
|
lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }
|
||||||
) : SummaryGroupMessageCreator {
|
) : SummaryGroupMessageCreator {
|
||||||
override fun createSummaryNotification(
|
override fun createSummaryNotification(
|
||||||
|
|
@ -28,6 +28,7 @@ class FakeSummaryGroupMessageCreator(
|
||||||
invitationNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
simpleNotifications: List<OneShotNotification>,
|
||||||
fallbackNotifications: List<OneShotNotification>,
|
fallbackNotifications: List<OneShotNotification>,
|
||||||
|
@ColorInt color: Int,
|
||||||
): Notification {
|
): Notification {
|
||||||
return createSummaryNotificationResult(
|
return createSummaryNotificationResult(
|
||||||
currentUser,
|
currentUser,
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven
|
||||||
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
||||||
import io.element.android.libraries.push.impl.test.DefaultTestPush
|
import io.element.android.libraries.push.impl.test.DefaultTestPush
|
||||||
import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler
|
import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler
|
||||||
|
import io.element.android.libraries.push.impl.workmanager.WorkerDataConverter
|
||||||
import io.element.android.libraries.pushproviders.api.PushData
|
import io.element.android.libraries.pushproviders.api.PushData
|
||||||
import io.element.android.libraries.pushstore.api.UserPushStore
|
import io.element.android.libraries.pushstore.api.UserPushStore
|
||||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||||
|
|
@ -715,7 +716,7 @@ class DefaultPushHandlerTest {
|
||||||
appCoroutineScope = backgroundScope,
|
appCoroutineScope = backgroundScope,
|
||||||
workManagerScheduler = workManagerScheduler,
|
workManagerScheduler = workManagerScheduler,
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
json = DefaultJsonProvider(),
|
workerDataConverter = WorkerDataConverter(DefaultJsonProvider()),
|
||||||
),
|
),
|
||||||
appCoroutineScope = backgroundScope,
|
appCoroutineScope = backgroundScope,
|
||||||
fallbackNotificationFactory = FallbackNotificationFactory(
|
fallbackNotificationFactory = FallbackNotificationFactory(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
package io.element.android.libraries.push.impl.troubleshoot
|
package io.element.android.libraries.push.impl.troubleshoot
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
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.FakeNotificationCreator
|
||||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
||||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||||
|
|
@ -64,10 +66,12 @@ class NotificationTestTest {
|
||||||
|
|
||||||
private fun createNotificationTest(): NotificationTest {
|
private fun createNotificationTest(): NotificationTest {
|
||||||
return NotificationTest(
|
return NotificationTest(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
notificationCreator = notificationCreator,
|
notificationCreator = notificationCreator,
|
||||||
notificationDisplayer = fakeNotificationDisplayer,
|
notificationDisplayer = fakeNotificationDisplayer,
|
||||||
notificationClickHandler = notificationClickHandler,
|
notificationClickHandler = notificationClickHandler,
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
|
enterpriseService = FakeEnterpriseService(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ class FetchNotificationWorkerTest {
|
||||||
workManagerScheduler = workManagerScheduler,
|
workManagerScheduler = workManagerScheduler,
|
||||||
syncOnNotifiableEvent = syncOnNotifiableEvent,
|
syncOnNotifiableEvent = syncOnNotifiableEvent,
|
||||||
coroutineDispatchers = testCoroutineDispatchers(),
|
coroutineDispatchers = testCoroutineDispatchers(),
|
||||||
json = DefaultJsonProvider(),
|
workerDataConverter = WorkerDataConverter(DefaultJsonProvider()),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun TestScope.createWorkerParams(
|
private fun TestScope.createWorkerParams(
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,24 @@ class SyncNotificationWorkManagerRequestTest {
|
||||||
assertThat(result.isFailure).isTrue()
|
assertThat(result.isFailure).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add test for invalid serialization (how?)
|
@Test
|
||||||
|
fun `build - invalid serialization`() = runTest {
|
||||||
|
val request = createSyncNotificationWorkManagerRequest(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
notificationEventRequests = listOf(aNotificationEventRequest()),
|
||||||
|
workerDataConverter = WorkerDataConverter({ error("error during serialization") })
|
||||||
|
)
|
||||||
|
val result = request.build()
|
||||||
|
assertThat(result.isFailure).isTrue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSyncNotificationWorkManagerRequest(
|
private fun createSyncNotificationWorkManagerRequest(
|
||||||
sessionId: SessionId,
|
sessionId: SessionId,
|
||||||
notificationEventRequests: List<NotificationEventRequest>,
|
notificationEventRequests: List<NotificationEventRequest>,
|
||||||
|
workerDataConverter: WorkerDataConverter = WorkerDataConverter(DefaultJsonProvider())
|
||||||
) = SyncNotificationWorkManagerRequest(
|
) = SyncNotificationWorkManagerRequest(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
notificationEventRequests = notificationEventRequests,
|
notificationEventRequests = notificationEventRequests,
|
||||||
json = DefaultJsonProvider(),
|
workerDataConverter = workerDataConverter,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.push.impl.workmanager
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.androidutils.json.DefaultJsonProvider
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||||
|
import io.element.android.libraries.push.api.push.NotificationEventRequest
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class WorkerDataConverterTest {
|
||||||
|
@Test
|
||||||
|
fun `ensure identity when serializing - deserializing an empty list`() {
|
||||||
|
testIdentity(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ensure identity when serializing - deserializing a list`() {
|
||||||
|
testIdentity(
|
||||||
|
listOf(
|
||||||
|
NotificationEventRequest(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
eventId = AN_EVENT_ID,
|
||||||
|
providerInfo = "info1",
|
||||||
|
),
|
||||||
|
NotificationEventRequest(
|
||||||
|
sessionId = A_SESSION_ID_2,
|
||||||
|
roomId = A_ROOM_ID_2,
|
||||||
|
eventId = AN_EVENT_ID_2,
|
||||||
|
providerInfo = "info2",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testIdentity(data: List<NotificationEventRequest>) {
|
||||||
|
val sut = WorkerDataConverter(DefaultJsonProvider())
|
||||||
|
val serialized = sut.serialize(data).getOrThrow()
|
||||||
|
val result = sut.deserialize(serialized)
|
||||||
|
assertThat(result).isEqualTo(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:40570506b75d7cb582cee70eb3d6453b673087a17a18d8ef148e3ff5edb6f1b9
|
oid sha256:388207cd5b424fbb95f070eac393db9330ae1b641795d1bb815874435ef9f623
|
||||||
size 89228
|
size 89027
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:491d0d8f7d7e269b13548c48a6eacb8fa4df5e2798aae5b3938e7eb7368ce3f9
|
oid sha256:542d8ba6a6031fe2789cf111f333eef22acf95281f57421ace2c7b5b0a599cc2
|
||||||
size 41251
|
size 41140
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3fb98e398abcb465b94d4138eecbccf076e229cb0807f6543d4f77ebb0353499
|
oid sha256:134e561fc4082339725c241a79fa55e0b5b1e134c046d4454cb7a9e71ea5e1b7
|
||||||
size 87390
|
size 87174
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3bee6454b9fab1a579e86bece32c0d6134d08fed5a63fecf247a58d7acba142d
|
oid sha256:c31cd78bc054610be05012cdba7eb0cbc770435b0e12bc065f6eae4a773ca39e
|
||||||
size 40125
|
size 40121
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ef2b3724cdd28175824554f800019fa663be341f0854751b50321e9b73203b6a
|
oid sha256:27c5418d421ca6cf0069e34ca3e22ca807203d252b9c1424eca447f070fbbbdf
|
||||||
size 54275
|
size 54177
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1b6fc1c78e05737b3231172b2b72b532b769ae9928ddd6674a864bee56566f6e
|
oid sha256:fc4c11b4d2c83b179409083ca36fcb95e44b7d8c51abd23e9c07f4d3be8a339c
|
||||||
size 52623
|
size 52626
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0534ab33ee18f03d219eb5b1490d41ccd1e3a4eb6bf734f31383a75110e368b3
|
oid sha256:7e8b65396dedff81056157620e2390c8d69954ee288266b575ac61aba16c2bec
|
||||||
size 63084
|
size 62590
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:85971dfed5e1cb01f5b04f43cc998d9cb69b6f06a24e3ea28f32c74aa3445e94
|
oid sha256:b8e1fb3b8b62ea8583a5bc9a18f39dbe71684e7d019bf63b22b873851b219209
|
||||||
size 63755
|
size 63270
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b5e587b7c669fa8fd02b8202318cd405b6fbb3bc91928d56f4bf2ef12f3bbbcc
|
oid sha256:9ff5ca08d441240de9e5adc35e41bceeb0f462979777345da4b2865e9a80012e
|
||||||
size 61863
|
size 61405
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a25dcf4e97dddb032713e49e13f46836fd541382aad2a9aa9b683dbcfdd93ccc
|
oid sha256:2f35b63ac49c799a5b3b5ad47652fe0f199efbbd2a285f782170c1edcd9ae723
|
||||||
size 62409
|
size 61955
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ed823d5ab8f1e0b1af536c240b2d50ba741de64d443e54d998a4e2d02e373e05
|
oid sha256:4d62c0c47ea78b89611244bb6e37ffdf2298b7b161de69ede59871089bd946c1
|
||||||
size 16202
|
size 16617
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:026c69cb2b201c1d753cd6197cdc124e208a3d3c9290d40e4de840ba0ac41cc0
|
oid sha256:efab4a3c85b9f762647c5e577c23a49f8ff40fd754171c90b670313f4790cdcd
|
||||||
size 13054
|
size 13063
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0da8a3a836e8b3eebb48d00fa2b5080caf80a32579d775bebd7564a525423460
|
oid sha256:4a5255962b310c60c62192391f4bf184955827022687f74efa21afda623e0b80
|
||||||
size 9019
|
size 8902
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a8b5298b33db0c0e74c6a786598b1826767f5ba3bcf9187846b5b9332b81a3e3
|
oid sha256:8be1a1667726344d330d52b8b4844d9007d291b8f2a3aaf1e296a16be98c6b2d
|
||||||
size 23146
|
size 23741
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7607784f41e0e7f6b4b1b4c983ed59aa466b01dd388a149b070a0540f6493d7f
|
oid sha256:ba2b344818e0b8d4b9224639be09c06de8585622be8dda20ad2aa1bb28e0e44d
|
||||||
size 18065
|
size 18052
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d234be8428b62e19b4192a17f1c9c21eeee25c7fc683f1597631b0599fc34b32
|
oid sha256:8ab0601bf05f66e91b38774cb77b744bd426a2b083ab43b930a402e3c2b9ddb1
|
||||||
size 13625
|
size 13486
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a725468cea9dfbf210d7367277b3a4d30438caf4ace60304e9942e4509c71972
|
oid sha256:a53eb404b797a91747a92a1fa2ae9ca23cbecda6c8d96991b38c28bb43cb51dc
|
||||||
size 34014
|
size 33603
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3c325ae3939648b0684e3ce2105e46445359748bf5d765593fa5eaca5d7e7082
|
oid sha256:aabda293c6a242618e176b402e0d5bf8d84f1c6a75d3537b8e42f3abe2f68212
|
||||||
size 38852
|
size 38567
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:82ea9e352708d800c28d2651220294c1a12bd09b98cda6676a14680c337f3b6e
|
oid sha256:bea4702ff62ea222a2ac4a2801b0fb0dc603b8e9f2ce97843d927b21f98d7994
|
||||||
size 11094
|
size 11136
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:85f42e6853aac517879a0d0379e655c7a23526e8e8a44f5c806323239df65639
|
oid sha256:821e281a6bcbb637e713b31fdb6e8bf3b30eef41507d25adb84d9bed4d6d9be0
|
||||||
size 15810
|
size 16083
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:288b0d6fbce8b5e90bca734635705217c01bbb2e82f516d66cd0d0cb5f854f94
|
oid sha256:4b78de1a677347827c1447cb459c76a20398846f4f96430b97ef3c3024d6b5b4
|
||||||
size 12568
|
size 12519
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0f931739c849cb283722149f9d89286633d7321d78b013281bb3333ae6af9cfd
|
oid sha256:8cc0851e3ea07010cebc83ac764239e9ab6e37ab2a2ad74039e542f117bf08b9
|
||||||
size 9122
|
size 9110
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:35dcf251e3bb3613c1cf5fcc4bfed6b8bb837dd721404ac80b72d804830cd483
|
oid sha256:3c41209aa36b563a8cd8c5a6788612d17464524b930ca8591e9e29b581a520de
|
||||||
size 22375
|
size 22747
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:283a7f62058a6f4d29b94a7a0d35b381a3e5038cb59bf545179b807f005aa3dd
|
oid sha256:53cc573decde4598fcc2c02cd6c7a925af464e215e8589bba71923cb54c0c687
|
||||||
size 17240
|
size 17148
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8fb4b4a6588f44d637f770456ca621e2c47bb9e730a8d24ace74028b74056309
|
oid sha256:c8d2611a87e3b9804c0538d4f3d7988f39f37f038b2a4b0c66f0676b4d03a1a9
|
||||||
size 13032
|
size 13017
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:083774bc84a79daac071eac0fca284ce49af7fb3ae21c141684fb588bd8842ed
|
oid sha256:c8f58003ba5ee7357c33a8e8d995f172ee12f50a6d07fadd6dbc3423bc357272
|
||||||
size 32868
|
size 32582
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4430144e9fb2637619b0fc9cd41ef5a3be7b5a3af7831caaba8bdeb9b96555ae
|
oid sha256:f6c3d7f0f259fff0089dd4d9f1b9071f43de8e21d5039fe95ab7b9cc37e69a00
|
||||||
size 37560
|
size 37269
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e68cc7f49af74626e7019b2a4eeac37877b94f0bcba31a21fa3e20b6fde1244d
|
oid sha256:f1f195d869be456f5eeaf9a6395f7d6e16b7449612c75b2493fcf9ff003ef512
|
||||||
size 10724
|
size 10810
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue