Merge develop into the PreviewParam rename

The 174 commits develop gained since this branch was cut touch preview data
in three ways, and each is resolved so the flipped konsist rule still holds
across the merged tree.

Two screens the branch had renamed were deleted upstream by "Remove the
account provider search screen and homeserver resolver": the
changeaccountprovider and searchaccountprovider packages. Their deletion
wins, so ChangeAccountProviderStatePreviewParam, SearchAccountProviderStatePreviewParam
and the two views that used them go with them rather than coming back
under the new name.

Two preview classes develop added still carried the old suffix and are
renamed here, along with the `@PreviewParameter` reference in each view:
GlobalSearchStateProvider -> GlobalSearchStatePreviewParam and
SendFailureDialogStateProvider -> SendFailureDialogStatePreviewParam.

Everything else merged textually. No production class extending
PreviewParameterProvider is left with a `Provider` name, and no reference
to any of the 194 renamed names survives outside the CHANGES.md entry that
records a past pull request title.
This commit is contained in:
hayaksi1
2026-08-24 10:55:40 +03:00
397 changed files with 6635 additions and 4221 deletions
@@ -15,7 +15,6 @@ import com.freeletics.flowredux.dsl.FlowReduxStateMachine
import io.element.android.features.verifysession.impl.util.andLogStateChange
import io.element.android.features.verifysession.impl.util.logReceivedEvents
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
@@ -23,6 +22,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS
import io.element.android.libraries.matrix.api.verification.VerificationRequest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.timeout
@@ -95,10 +95,13 @@ class OutgoingVerificationStateMachine(
// If a key backup exists, wait until it's restored or a timeout happens
val hasBackup = encryptionService.doesBackupExistOnServer().getOrNull().orFalse()
if (hasBackup) {
tryOrNull {
encryptionService.recoveryStateStateFlow.filter { it == RecoveryState.ENABLED }
try {
encryptionService.recoveryStateStateFlow
.filter { it == RecoveryState.ENABLED || it == RecoveryState.DISABLED || it == RecoveryState.INCOMPLETE }
.timeout(10.seconds)
.first()
} catch (_: TimeoutCancellationException) {
// The backup was not restored in time, proceed to the Completed state anyway
}
}
state.override { State.Completed.andLogStateChange() }
@@ -16,6 +16,7 @@ import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificat
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
@@ -216,6 +217,85 @@ class OutgoingVerificationPresenterTest {
}
}
@Test
fun `present - When verification is approved and a backup exists but recovery is disabled, the flow completes`() = runTest {
val service = unverifiedSessionService(
requestUserVerificationLambda = { },
startSasVerificationLambda = { },
approveVerificationLambda = { },
)
val encryptionService = FakeEncryptionService().apply {
givenDoesBackupExistOnServerResult(Result.success(true))
recoveryStateStateFlow.value = RecoveryState.DISABLED
}
val presenter = createOutgoingVerificationPresenter(
service = service,
verificationRequest = anOutgoingUserVerificationRequest(),
encryptionService = encryptionService,
)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish)
assertThat(awaitItem().step).isEqualTo(Step.Completed)
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - When verification is approved and a backup exists but recovery is incomplete, the flow completes`() = runTest {
val service = unverifiedSessionService(
requestUserVerificationLambda = { },
startSasVerificationLambda = { },
approveVerificationLambda = { },
)
val encryptionService = FakeEncryptionService().apply {
givenDoesBackupExistOnServerResult(Result.success(true))
recoveryStateStateFlow.value = RecoveryState.INCOMPLETE
}
val presenter = createOutgoingVerificationPresenter(
service = service,
verificationRequest = anOutgoingUserVerificationRequest(),
encryptionService = encryptionService,
)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish)
assertThat(awaitItem().step).isEqualTo(Step.Completed)
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - When verification is approved but the recovery state is not determined, the flow completes after the timeout`() = runTest {
val service = unverifiedSessionService(
requestUserVerificationLambda = { },
startSasVerificationLambda = { },
approveVerificationLambda = { },
)
val encryptionService = FakeEncryptionService().apply {
givenDoesBackupExistOnServerResult(Result.success(true))
}
val presenter = createOutgoingVerificationPresenter(
service = service,
verificationRequest = anOutgoingUserVerificationRequest(),
encryptionService = encryptionService,
)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish)
// The recovery state never leaves UNKNOWN, so the 10 seconds timeout fires and the flow completes anyway
this@runTest.advanceUntilIdle()
assertThat(awaitItem().step).isEqualTo(Step.Completed)
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - When verification is declined, the flow is canceled`() = runTest {
val service = unverifiedSessionService(