diff --git a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt
index 6e11306d309..07976650ba8 100644
--- a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt
+++ b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt
@@ -84,6 +84,9 @@ class SystemMessageContentMapper @Inject constructor(
content,
members
)
+
+ is MessageContent.AdminlessDeleteReminder ->
+ UIMessageContent.SystemMessage.AdminlessDeleteReminder(content.deletionScheduledFor)
}
private fun mapConversationConversationAppsAccessChanged(
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt
index 82710693b57..23447b7ea5f 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt
@@ -19,6 +19,7 @@
package com.wire.android.ui.home.conversations.messages.item
+import android.text.format.DateFormat
import androidx.annotation.DrawableRes
import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
@@ -63,6 +64,7 @@ import com.wire.android.ui.theme.wireDimensions
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.CustomTabsHelper
import com.wire.android.util.SupportPage
+import com.wire.android.util.formatMonthDayShortTime
import com.wire.android.util.supportUrlResource
import com.wire.android.util.ui.MarkdownTextStyle
import com.wire.android.util.ui.UIText
@@ -641,6 +643,26 @@ private fun SystemMessage.buildContent(isWireCellsEnabled: Boolean) = when (this
}
}
}
+
+ is SystemMessage.AdminlessDeleteReminder -> buildContent(
+ iconResId = commonR.drawable.ic_info,
+ iconTintColor = MaterialTheme.wireColorScheme.error,
+ learnMorePage = SupportPage.ADMINLESS_GROUP_DELETE,
+ ) {
+ val is24Hour = DateFormat.is24HourFormat(LocalContext.current)
+ val markdownTextStyle = DefaultMarkdownTextStyle.copy(
+ normalColor = MaterialTheme.wireColorScheme.error,
+ boldColor = MaterialTheme.wireColorScheme.error
+ )
+ buildAnnotatedString {
+ append(
+ stringResource(
+ id = R.string.label_system_message_adminless_delete_reminder,
+ formatArgs = arrayOf(deletionScheduledFor.formatMonthDayShortTime(is24Hour))
+ ).toMarkdownAnnotatedString(markdownTextStyle)
+ )
+ }
+ }
}
private fun AnnotatedString.Builder.appendVerticalSpace() = withStyle(ParagraphStyle()) { append(" ") }
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt
index 389ac48d5cd..76626329d67 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt
@@ -29,6 +29,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.UIText
import com.wire.android.util.ui.toUIText
import com.wire.kalium.logic.data.conversation.Conversation
+import kotlinx.datetime.Instant
@PreviewMultipleThemes
@Composable
@@ -486,3 +487,17 @@ fun PreviewSystemMessageConversationMessageAppsAccessEnabled() {
)
}
}
+
+@PreviewMultipleThemes
+@Composable
+fun PreviewSystemMessageAdminlessDeleteReminder() {
+ WireTheme {
+ SystemMessageItem(
+ message = mockMessageWithKnock.copy(
+ messageContent = UIMessageContent.SystemMessage.AdminlessDeleteReminder(
+ deletionScheduledFor = Instant.parse("2026-04-23T12:00:00Z")
+ )
+ )
+ )
+ }
+}
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt
index 0ff927dd5f1..888d4d9132a 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt
@@ -656,6 +656,11 @@ sealed interface UIMessageContent {
val isAuthorSelfUser: Boolean = false,
val isAccessEnabled: Boolean
) : SystemMessage
+
+ @Serializable
+ data class AdminlessDeleteReminder(
+ val deletionScheduledFor: Instant
+ ) : SystemMessage
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0d4c932c8aa..1c610a90e20 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1951,6 +1951,7 @@ In group conversations, the group admin can overwrite this setting.
%1$s disabled **apps** for this conversation
Added apps have access to the content of this conversation.
You were promoted to group admin
+ This group will be automatically deleted on %1$s, as there are no eligible group admins.
More information about this backend
diff --git a/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt
index 6911ac9a13b..1eb8e62dced 100644
--- a/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt
+++ b/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt
@@ -44,6 +44,7 @@ import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.util.Locale
+import kotlinx.datetime.Instant
@OptIn(ExperimentalCoroutinesApi::class)
@ExtendWith(CoroutineTestExtension::class)
@@ -75,6 +76,18 @@ class SystemMessageContentMapperTest {
assertTrue(uiContent is SystemMessage.ConversationMessageTimerDeactivated)
}
+ @Test
+ fun givenAdminlessDeleteReminder_whenMappingToSystemMessage_thenScheduledDeletionIsPreserved() = runTest {
+ val (_, mapper) = Arrangement().arrange()
+ val scheduledDeletion = Instant.parse("2026-04-23T12:00:00Z")
+ val content = MessageContent.AdminlessDeleteReminder(scheduledDeletion)
+
+ val uiContent = mapper.mapMessage(TestMessage.SYSTEM_MESSAGE.copy(content = content), emptyList())
+
+ assertIs(uiContent!!)
+ assertEquals(scheduledDeletion, uiContent.deletionScheduledFor)
+ }
+
@Test
fun givenMemberDetails_whenMappingToSystemMessageMemberName_thenCorrectValuesShouldBeReturned() = runTest {
// Given
diff --git a/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt b/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt
index 26c4db066d2..1936f71b2e3 100644
--- a/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt
+++ b/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt
@@ -17,9 +17,11 @@
*/
package com.wire.android.util
+import android.text.format.DateFormat
import androidx.compose.runtime.Stable
import kotlinx.datetime.Instant
import kotlinx.datetime.toJavaInstant
+import java.text.SimpleDateFormat
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
@@ -39,6 +41,10 @@ fun Instant.formatMediumDateTime(): String = DateAndTimeParsers.formatMediumDate
@Stable
fun Instant.formatFullDateShortTime(): String = DateAndTimeParsers.formatFullDateShortTime(this)
+@Stable
+fun Instant.formatMonthDayShortTime(is24Hour: Boolean): String =
+ DateAndTimeParsers.formatMonthDayShortTime(this, is24Hour)
+
@Stable
fun Instant.uiMessageDateTime(): String = DateAndTimeParsers.uiMessageDateTime(this)
@@ -107,6 +113,16 @@ class DateAndTimeParsers private constructor() {
fun formatFullDateShortTime(instant: Instant): String = fullDateShortTimeFormatter.format(instant.toJavaInstant())
+ fun formatMonthDayShortTime(instant: Instant, is24Hour: Boolean): String {
+ val locale = Locale.getDefault()
+ val date = Date.from(instant.toJavaInstant())
+ val datePattern = DateFormat.getBestDateTimePattern(locale, "MMMMd")
+ val timePattern = DateFormat.getBestDateTimePattern(locale, if (is24Hour) "Hm" else "hm")
+ val formattedDate = SimpleDateFormat(datePattern, locale).format(date)
+ val formattedTime = SimpleDateFormat(timePattern, locale).format(date)
+ return "$formattedDate, $formattedTime"
+ }
+
fun cellTimeFormat(instant: Instant): String {
val timeFormatter = java.text.DateFormat.getTimeInstance(
java.text.DateFormat.SHORT,
diff --git a/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt b/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt
index e568fcae017..ecc132507a5 100644
--- a/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt
+++ b/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt
@@ -53,5 +53,6 @@ enum class SupportPage(
SEARCH("search", R.string.url_learn_about_search),
CELLS_CONVERSATION("cells_conversation", R.string.empty_screen_learn_more_link_conversation),
CELLS_ALL_FILES("cells_all_files", R.string.empty_screen_learn_more_link_all_files_screen),
- FILE_PERMISSIONS_IN_SHARED_DRIVE("file_permissions_in_shared_drive", R.string.file_permissions_in_shared_drive)
+ FILE_PERMISSIONS_IN_SHARED_DRIVE("file_permissions_in_shared_drive", R.string.file_permissions_in_shared_drive),
+ ADMINLESS_GROUP_DELETE("adminless_group_delete", R.string.url_system_message_adminless_group_delete),
}
diff --git a/core/ui-common/src/main/res/values/strings.xml b/core/ui-common/src/main/res/values/strings.xml
index 77319e45f29..e17cb776726 100644
--- a/core/ui-common/src/main/res/values/strings.xml
+++ b/core/ui-common/src/main/res/values/strings.xml
@@ -59,6 +59,7 @@
https://support.wire.com/hc/articles/360002018278
https://support.wire.com/hc/articles/32207745256221
https://support.wire.com/hc/articles/32207800433309
+ https://support.wire.com/hc/en-us/articles/37518608388125-Prevent-adminless-groups
https://support.wire.com/hc/articles/36679600377373
Learn more about legal hold
Connecting not possible
diff --git a/kalium b/kalium
index 734b8ec308d..01f330447ee 160000
--- a/kalium
+++ b/kalium
@@ -1 +1 @@
-Subproject commit 734b8ec308df6a5bca78de67aeb5bd94a6109ec5
+Subproject commit 01f330447ee40b72223e7faeedb1b86ff6d25c38