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 6c51cd615d9..2486f272d37 100644
--- a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt
+++ b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt
@@ -81,6 +81,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 196c555f110..45abc55c3e0 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
@@ -610,6 +612,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 f60ea33f4b5..09df03f8b10 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
@@ -650,6 +650,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 41f0727a4e1..a8d49cf15a9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1903,6 +1903,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 db5852fb98c..daba36cb668 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
@@ -52,5 +52,6 @@ enum class SupportPage(
LEGAL_HOLD("legal_hold", R.string.url_legal_hold_learn_more),
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)
+ CELLS_ALL_FILES("cells_all_files", R.string.empty_screen_learn_more_link_all_files_screen),
+ 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 f062c97de8b..92786b6cf99 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
Learn more about legal hold
Connecting not possible
You can not connect to this user due to legal hold.
diff --git a/kalium b/kalium
index e68500602d0..7056798774e 160000
--- a/kalium
+++ b/kalium
@@ -1 +1 @@
-Subproject commit e68500602d0914e1263045aadeb69a414517f896
+Subproject commit 7056798774ebcd741dd5a605d108f9bb26bf52b4