Merge branch 'develop' into feature/bma/notificationFallbackCounter

This commit is contained in:
Benoit Marty 2026-02-11 21:10:14 +01:00
commit 3a86605ee9
7 changed files with 157 additions and 50 deletions

View file

@ -1,3 +1,77 @@
Changes in Element X v26.02.0
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.02.0 -->
## What's Changed
### ✨ Features
* When a background SDK task fails, react in the client by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6166
* Enable space feature flags by default by @ganfra in https://github.com/element-hq/element-x-android/pull/6171
### 🙌 Improvements
* Improve space management with pagination and partial failure handling by @ganfra in https://github.com/element-hq/element-x-android/pull/6099
* Iterate on QrCode login error buttons by @bmarty in https://github.com/element-hq/element-x-android/pull/6101
* Update icon shown for world_readable rooms by @richvdh in https://github.com/element-hq/element-x-android/pull/6111
* QRCode login: treat not found error as expired error. by @bmarty in https://github.com/element-hq/element-x-android/pull/6161
* Iterate on Space related UI by @ganfra in https://github.com/element-hq/element-x-android/pull/6150
### 🔒 Security
* Ensure aspect ratio of images in the timeline is restricted by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6168
### 🐛 Bugfixes
* Ensure that Element Call activity is not closed when using an external link by @bmarty in https://github.com/element-hq/element-x-android/pull/6114
* Refresh a Space's room list after creating a room in it by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6135
* When creating a DM, set room history visibility to `invited` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6138
* Fix back navigation after creating a room in a space by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6134
* Fix `LinkifyHelper` index out of bounds with parenthesis by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6140
* Change role screen won't be dismissed until changes take effect by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6141
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6122
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6155
### 🧱 Build
* Try fixing Maestro tests (again) by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6149
* Add a stale bot for X-Needs-Info issues. by @bmarty in https://github.com/element-hq/element-x-android/pull/6153
* [Release script] Ensure that the release version will match the next Monday date by @bmarty in https://github.com/element-hq/element-x-android/pull/6152
### 🚧 In development 🚧
* Add Space Filters feature for Room List by @ganfra in https://github.com/element-hq/element-x-android/pull/6136
* Add history sharing badges to room details by @kaylendog in https://github.com/element-hq/element-x-android/pull/6132
### Dependency upgrades
* Update dependency androidx.work:work-runtime-ktx to v2.11.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6105
* Update metro to v0.10.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6106
* Update camera to v1.5.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6103
* Update activity to v1.12.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6104
* Update dependency com.posthog:posthog-android to v3.30.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6120
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6102
* Update roborazzi to v1.58.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6124
* Update kover by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6139
* Update dependency com.posthog:posthog-android to v3.31.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6145
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6142
* Update media3 to v1.9.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6151
* Update dependency org.matrix.rustcomponents:sdk-android to v26.02.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6144
* Update firebaseAppDistribution to v5.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6146
* Update dependency com.google.firebase:firebase-bom to v34.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6148
* Update dependency io.sentry:sentry-android to v8.32.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6157
* Update metro to v0.10.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6164
* Update dependency org.matrix.rustcomponents:sdk-android to v26.2.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6169
* chore(deps): update plugin paparazzi to v2.0.0-alpha04 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6048
* fix(deps): update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6173
* fix(deps): update haze to v1.7.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6175
### Others
* Improve favorite wording and icon of room by @bmarty in https://github.com/element-hq/element-x-android/pull/6097
* Add special flow for leaving a space as the last owner by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6112
* Remove `runBlocking` in `ThreadedMessagesNode` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6108
* Revert "Add "call.pro.element.io" in the list of known hosts for Element Call." by @bmarty in https://github.com/element-hq/element-x-android/pull/6118
* Refactor room list filtering to use Rust SDK by @ganfra in https://github.com/element-hq/element-x-android/pull/6117
* Ensure http 429 are retried 3 times before failing. by @bmarty in https://github.com/element-hq/element-x-android/pull/6119
* Remove `JoinRule.Private` from the codebase by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6129
* Fix voice message recording not starting after permission is granted by @kknappe in https://github.com/element-hq/element-x-android/pull/6109
* Use correct bg color. by @bmarty in https://github.com/element-hq/element-x-android/pull/6165
* Document "Developer options" and remove outdated instructions by @MadLittleMods in https://github.com/element-hq/element-x-android/pull/6162
* Update SpaceFilterButton selected state color by @ganfra in https://github.com/element-hq/element-x-android/pull/6178
## New Contributors
* @kknappe made their first contribution in https://github.com/element-hq/element-x-android/pull/6109
* @MadLittleMods made their first contribution in https://github.com/element-hq/element-x-android/pull/6162
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.01.2...v26.02.0
Changes in Element X v26.01.2
=============================

View file

@ -0,0 +1,2 @@
Main changes in this version: bug fixes and improvements.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -109,10 +109,12 @@ fun SpaceView(
modifier: Modifier = Modifier,
acceptDeclineInviteView: @Composable () -> Unit,
) {
BackHandler {
var handledBack by remember { mutableStateOf(false) }
BackHandler(enabled = !handledBack) {
if (state.isManageMode) {
state.eventSink(SpaceEvents.ExitManageMode)
} else {
handledBack = true
onBackClick()
}
}

View file

@ -24,9 +24,10 @@ import io.element.android.libraries.push.api.notifications.NotificationIdProvide
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreEventInRoom
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.NavigationState
import io.element.android.services.appnavstate.api.currentSessionId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -46,28 +47,30 @@ class DefaultNotificationDrawerManager(
private val matrixClientProvider: MatrixClientProvider,
private val imageLoaderHolder: ImageLoaderHolder,
private val activeNotificationsProvider: ActiveNotificationsProvider,
sessionObserver: SessionObserver,
) : NotificationCleaner {
// TODO EAx add a setting per user for this
private var useCompleteNotificationFormat = true
private val sessionListener = object : SessionListener {
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
// User signed out, clear all notifications related to the session.
clearAllEvents(SessionId(userId))
}
}
init {
// Observe application state
coroutineScope.launch {
appNavigationStateService.appNavigationState
.collect { onAppNavigationStateChange(it.navigationState) }
}
sessionObserver.addListener(sessionListener)
}
private var currentAppNavigationState: NavigationState? = null
private fun onAppNavigationStateChange(navigationState: NavigationState) {
when (navigationState) {
NavigationState.Root -> {
currentAppNavigationState?.currentSessionId()?.let { sessionId ->
// User signed out, clear all notifications related to the session.
clearAllEvents(sessionId)
}
}
NavigationState.Root -> {}
is NavigationState.Session -> {}
is NavigationState.Space -> {}
is NavigationState.Room -> {
@ -85,7 +88,6 @@ class DefaultNotificationDrawerManager(
)
}
}
currentAppNavigationState = navigationState
}
/**

View file

@ -31,7 +31,9 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMe
import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver
import io.element.android.services.appnavstate.api.AppNavigationState
import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.NavigationState
@ -204,34 +206,69 @@ class DefaultNotificationDrawerManagerTest {
)
}
private fun TestScope.createDefaultNotificationDrawerManager(
notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(),
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(),
roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(),
summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(),
activeNotificationsProvider: FakeActiveNotificationsProvider = FakeActiveNotificationsProvider(),
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
sessionStore: SessionStore = InMemorySessionStore(),
enterpriseService: EnterpriseService = FakeEnterpriseService(),
): DefaultNotificationDrawerManager {
return DefaultNotificationDrawerManager(
@Test
fun `when a session is signed out, clearAllEvent is invoked`() = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val notificationDisplayer = FakeNotificationDisplayer(
cancelNotificationResult = cancelNotificationResult,
)
val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID)
val activeNotificationsProvider = FakeActiveNotificationsProvider(
getNotificationsForSessionResult = {
listOf(
mockk {
every { id } returns summaryId
every { tag } returns null
},
)
},
countResult = { 1 },
)
val sessionObserver = FakeSessionObserver()
createDefaultNotificationDrawerManager(
notificationDisplayer = notificationDisplayer,
notificationRenderer = NotificationRenderer(
notificationDisplayer = FakeNotificationDisplayer(),
notificationDataFactory = DefaultNotificationDataFactory(
notificationCreator = FakeNotificationCreator(),
roomGroupMessageCreator = roomGroupMessageCreator,
summaryGroupMessageCreator = summaryGroupMessageCreator,
activeNotificationsProvider = activeNotificationsProvider,
),
enterpriseService = enterpriseService,
sessionStore = sessionStore,
),
appNavigationStateService = appNavigationStateService,
coroutineScope = backgroundScope,
matrixClientProvider = matrixClientProvider,
imageLoaderHolder = FakeImageLoaderHolder(),
activeNotificationsProvider = activeNotificationsProvider,
sessionObserver = sessionObserver,
)
// Simulate a session sign out
sessionObserver.onSessionDeleted(A_SESSION_ID.value)
// Verify we asked to cancel the notification with summaryId
cancelNotificationResult.assertions().isCalledExactly(1).withSequence(
listOf(value(null), value(summaryId)),
)
}
}
fun TestScope.createDefaultNotificationDrawerManager(
notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(),
notificationRenderer: NotificationRenderer? = null,
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(),
roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(),
summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(),
activeNotificationsProvider: FakeActiveNotificationsProvider = FakeActiveNotificationsProvider(),
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
sessionStore: SessionStore = InMemorySessionStore(),
enterpriseService: EnterpriseService = FakeEnterpriseService(),
sessionObserver: SessionObserver = FakeSessionObserver(),
): DefaultNotificationDrawerManager {
return DefaultNotificationDrawerManager(
notificationDisplayer = notificationDisplayer,
notificationRenderer = notificationRenderer ?: NotificationRenderer(
notificationDisplayer = FakeNotificationDisplayer(),
notificationDataFactory = DefaultNotificationDataFactory(
notificationCreator = FakeNotificationCreator(),
roomGroupMessageCreator = roomGroupMessageCreator,
summaryGroupMessageCreator = summaryGroupMessageCreator,
activeNotificationsProvider = activeNotificationsProvider,
),
enterpriseService = enterpriseService,
sessionStore = sessionStore,
),
appNavigationStateService = appNavigationStateService,
coroutineScope = backgroundScope,
matrixClientProvider = matrixClientProvider,
imageLoaderHolder = FakeImageLoaderHolder(),
activeNotificationsProvider = activeNotificationsProvider,
sessionObserver = sessionObserver,
)
}

View file

@ -16,13 +16,9 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
import io.element.android.libraries.matrix.test.notification.aNotificationData
import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder
import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDataFactory
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@ -47,16 +43,10 @@ class DefaultOnMissedCallNotificationHandlerTest {
})
val defaultOnMissedCallNotificationHandler = DefaultOnMissedCallNotificationHandler(
matrixClientProvider = matrixClientProvider,
defaultNotificationDrawerManager = DefaultNotificationDrawerManager(
notificationDisplayer = FakeNotificationDisplayer(),
defaultNotificationDrawerManager = createDefaultNotificationDrawerManager(
notificationRenderer = createNotificationRenderer(
notificationDataFactory = dataFactory,
),
appNavigationStateService = FakeAppNavigationStateService(),
coroutineScope = backgroundScope,
matrixClientProvider = FakeMatrixClientProvider(),
imageLoaderHolder = FakeImageLoaderHolder(),
activeNotificationsProvider = FakeActiveNotificationsProvider(),
),
callNotificationEventResolver = FakeCallNotificationEventResolver(resolveEventLambda = { _, _, _ ->
Result.success(aNotifiableMessageEvent())

View file

@ -39,13 +39,13 @@ private const val versionYear = 26
* Month of the version on 2 digits. Value must be in [1,12].
* Do not update this value. it is updated by the release script.
*/
private const val versionMonth = 1
private const val versionMonth = 2
/**
* Release number in the month. Value must be in [0,99].
* Do not update this value. it is updated by the release script.
*/
private const val versionReleaseNumber = 2
private const val versionReleaseNumber = 0
object Versions {
/**