diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 1d92cf00c9..a4f20fb97d 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.qrcode) implementation(projects.libraries.oidc.api) + implementation(projects.libraries.uiUtils) implementation(libs.androidx.browser) implementation(platform(libs.network.retrofit.bom)) implementation(libs.androidx.webkit) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt index 6ea5bfa488..5e0cc054fc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt @@ -12,5 +12,6 @@ sealed interface OnBoardingEvents { val defaultAccountProvider: String ) : OnBoardingEvents + data object OnVersionClick : OnBoardingEvents data object ClearError : OnBoardingEvents } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt index f4696133fe..37dce8e4b5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt @@ -9,9 +9,12 @@ package io.element.android.features.login.impl.screens.onboarding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -24,6 +27,7 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.ui.utils.MultipleTapToUnlock class OnBoardingPresenter @AssistedInject constructor( @Assisted private val params: OnBoardingNode.Params, @@ -40,6 +44,8 @@ class OnBoardingPresenter @AssistedInject constructor( ): OnBoardingPresenter } + private val multipleTapToUnlock = MultipleTapToUnlock() + @Composable override fun present(): OnBoardingState { val localCoroutineScope = rememberCoroutineScope() @@ -70,6 +76,7 @@ class OnBoardingPresenter @AssistedInject constructor( featureFlagService.isFeatureEnabled(FeatureFlags.QrCodeLogin) } val canReportBug = remember { rageshakeFeatureAvailability.isAvailable() } + var showReportBug by rememberSaveable { mutableStateOf(false) } val loginMode by loginHelper.collectLoginMode() @@ -82,6 +89,13 @@ class OnBoardingPresenter @AssistedInject constructor( loginHint = params.loginHint?.takeIf { forcedAccountProvider == null }, ) OnBoardingEvents.ClearError -> loginHelper.clearError() + OnBoardingEvents.OnVersionClick -> { + if (canReportBug) { + if (multipleTapToUnlock.unlock(localCoroutineScope)) { + showReportBug = true + } + } + } } } @@ -91,8 +105,9 @@ class OnBoardingPresenter @AssistedInject constructor( mustChooseAccountProvider = mustChooseAccountProvider, canLoginWithQrCode = canLoginWithQrCode, canCreateAccount = defaultAccountProvider == null && canConnectToAnyHomeserver && OnBoardingConfig.CAN_CREATE_ACCOUNT, - canReportBug = canReportBug, + canReportBug = canReportBug && showReportBug, loginMode = loginMode, + version = buildMeta.versionName, eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt index 98484a1fc2..1e55c3af2d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt @@ -17,6 +17,7 @@ data class OnBoardingState( val canLoginWithQrCode: Boolean, val canCreateAccount: Boolean, val canReportBug: Boolean, + val version: String, val loginMode: AsyncData, val eventSink: (OnBoardingEvents) -> Unit, ) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt index 9be36719e2..cdf77da523 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt @@ -30,6 +30,7 @@ fun anOnBoardingState( canLoginWithQrCode: Boolean = false, canCreateAccount: Boolean = false, canReportBug: Boolean = false, + version: String = "1.0.0", loginMode: AsyncData = AsyncData.Uninitialized, eventSink: (OnBoardingEvents) -> Unit = {}, ) = OnBoardingState( @@ -39,6 +40,7 @@ fun anOnBoardingState( canLoginWithQrCode = canLoginWithQrCode, canCreateAccount = canCreateAccount, canReportBug = canReportBug, + version = version, loginMode = loginMode, eventSink = eventSink, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index 99ab348b06..6c67e75cd1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -202,12 +202,23 @@ private fun OnBoardingButtons( // Add a report problem text button. Use a Text since we need a special theme here. Text( modifier = Modifier - .padding(16.dp) - .clickable(onClick = onReportProblem), + .clickable(onClick = onReportProblem) + .padding(16.dp), text = stringResource(id = CommonStrings.common_report_a_problem), style = ElementTheme.typography.fontBodySmRegular, color = ElementTheme.colors.textSecondary, ) + } else { + Text( + modifier = Modifier + .clickable { + state.eventSink(OnBoardingEvents.OnVersionClick) + } + .padding(16.dp), + text = stringResource(id = R.string.screen_onboarding_app_version, state.version), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) } } } diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml index 4f4d98baaf..d0840f1983 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -34,6 +34,7 @@ "Matrix is an open network for secure, decentralised communication." "Welcome back!" "Sign in to %1$s" + "Version %1$s" "Sign in manually" "Sign in to %1$s" "Sign in with QR code" diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 7749d1502b..3e59528427 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -83,19 +83,40 @@ class OnBoardingPresenterTest { assertThat(initialState.canLoginWithQrCode).isFalse() assertThat(initialState.productionApplicationName).isEqualTo("B") assertThat(initialState.canCreateAccount).isEqualTo(OnBoardingConfig.CAN_CREATE_ACCOUNT) - assertThat(initialState.canReportBug).isTrue() + assertThat(initialState.canReportBug).isFalse() assertThat(awaitItem().canLoginWithQrCode).isTrue() } } @Test - fun `present - rageshake not available`() = runTest { + fun `present - clicking on version 7 times has no effect if rageshake not available`() = runTest { val presenter = createPresenter( rageshakeFeatureAvailability = { false }, ) presenter.test { skipItems(1) - assertThat(awaitItem().canReportBug).isFalse() + awaitItem().also { state -> + assertThat(state.canReportBug).isFalse() + repeat(7) { + state.eventSink(OnBoardingEvents.OnVersionClick) + } + } + expectNoEvents() + } + } + + @Test + fun `present - clicking on version 7 times will reveal the report a problem button`() = runTest { + val presenter = createPresenter() + presenter.test { + skipItems(1) + awaitItem().also { state -> + assertThat(state.canReportBug).isFalse() + repeat(7) { + state.eventSink(OnBoardingEvents.OnVersionClick) + } + } + assertThat(awaitItem().canReportBug).isTrue() } }