Add a way to customize the on boarding logo.

This commit is contained in:
Benoit Marty
2025-08-25 14:44:54 +02:00
parent 2f2e886e9f
commit ce20a01ac0
8 changed files with 111 additions and 8 deletions

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.onboarding
import android.annotation.SuppressLint
import android.content.Context
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import javax.inject.Inject
fun interface CustomLogoResIdProvider {
fun get(): Int?
}
@ContributesBinding(AppScope::class)
class DefaultCustomLogoResIdProvider @Inject constructor(
@ApplicationContext private val context: Context,
) : CustomLogoResIdProvider {
@SuppressLint("DiscouragedApi")
override fun get(): Int? {
val resId = context.resources
.getIdentifier("custom_logo", "drawable", context.packageName)
.takeIf { it != 0 }
return resId
}
}

View File

@@ -36,6 +36,7 @@ class OnBoardingPresenter @AssistedInject constructor(
private val defaultAccountProviderAccessControl: DefaultAccountProviderAccessControl,
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
private val loginHelper: LoginHelper,
private val customLogoResIdProvider: CustomLogoResIdProvider,
) : Presenter<OnBoardingState> {
@AssistedFactory
interface Factory {
@@ -81,6 +82,9 @@ class OnBoardingPresenter @AssistedInject constructor(
}
val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false)
var showReportBug by rememberSaveable { mutableStateOf(false) }
val customLogoResId = remember {
customLogoResIdProvider.get()
}
val loginMode by loginHelper.collectLoginMode()
@@ -112,6 +116,7 @@ class OnBoardingPresenter @AssistedInject constructor(
canReportBug = canReportBug && showReportBug,
loginMode = loginMode,
version = buildMeta.versionName,
customLogoResId = customLogoResId,
eventSink = ::handleEvent,
)
}

View File

@@ -7,6 +7,7 @@
package io.element.android.features.login.impl.screens.onboarding
import androidx.annotation.DrawableRes
import io.element.android.features.login.impl.login.LoginMode
import io.element.android.libraries.architecture.AsyncData
@@ -18,6 +19,8 @@ data class OnBoardingState(
val canCreateAccount: Boolean,
val canReportBug: Boolean,
val version: String,
@DrawableRes
val customLogoResId: Int?,
val loginMode: AsyncData<LoginMode>,
val eventSink: (OnBoardingEvents) -> Unit,
) {

View File

@@ -7,9 +7,11 @@
package io.element.android.features.login.impl.screens.onboarding
import androidx.annotation.DrawableRes
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.login.impl.login.LoginMode
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.R
open class OnBoardingStateProvider : PreviewParameterProvider<OnBoardingState> {
override val values: Sequence<OnBoardingState>
@@ -20,6 +22,7 @@ open class OnBoardingStateProvider : PreviewParameterProvider<OnBoardingState> {
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true),
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true, canReportBug = true),
anOnBoardingState(defaultAccountProvider = "element.io", canCreateAccount = false, canReportBug = true),
anOnBoardingState(customLogoResId = R.drawable.sample_background),
)
}
@@ -31,6 +34,8 @@ fun anOnBoardingState(
canCreateAccount: Boolean = false,
canReportBug: Boolean = false,
version: String = "1.0.0",
@DrawableRes
customLogoResId: Int? = null,
loginMode: AsyncData<LoginMode> = AsyncData.Uninitialized,
eventSink: (OnBoardingEvents) -> Unit = {},
) = OnBoardingState(
@@ -42,5 +47,6 @@ fun anOnBoardingState(
canReportBug = canReportBug,
version = version,
loginMode = loginMode,
customLogoResId = customLogoResId,
eventSink = eventSink,
)

View File

@@ -7,6 +7,7 @@
package io.element.android.features.login.impl.screens.onboarding
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -19,9 +20,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -67,8 +70,15 @@ fun OnBoardingView(
) {
OnBoardingPage(
modifier = modifier,
renderBackground = state.customLogoResId == null,
content = {
OnBoardingContent(state = state)
if (state.customLogoResId != null) {
OnBoardingSimpleLogo(
customLogoResId = state.customLogoResId,
)
} else {
OnBoardingContent(state = state)
}
LoginModeView(
loginMode = state.loginMode,
onClearError = {
@@ -139,6 +149,24 @@ private fun OnBoardingContent(state: OnBoardingState) {
}
}
@Composable
private fun OnBoardingSimpleLogo(
customLogoResId: Int,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center,
) {
Image(
painter = painterResource(id = customLogoResId),
contentDescription = null
)
}
}
@Composable
private fun OnBoardingButtons(
state: OnBoardingState,

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2025 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ Please see LICENSE files in the repository root for full details.
-->
<!--
Note: one or several custom_logo drawable can be added at build time. Ensure that these
resource are not stripped out by the resource shrinker.
-->
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/custom_logo" />

View File

@@ -84,6 +84,18 @@ class OnBoardingPresenterTest {
}
}
@Test
fun `present - custom logo`() = runTest {
val presenter = createPresenter(
customLogoResIdProvider = CustomLogoResIdProvider { 42 },
)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.customLogoResId).isEqualTo(42)
}
}
@Test
fun `present - clicking on version 7 times has no effect if rageshake not available`() = runTest {
val presenter = createPresenter(
@@ -224,6 +236,7 @@ private fun createPresenter(
wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(),
rageshakeFeatureAvailability: () -> Flow<Boolean> = { flowOf(true) },
loginHelper: LoginHelper = createLoginHelper(),
customLogoResIdProvider: CustomLogoResIdProvider = CustomLogoResIdProvider { null },
) = OnBoardingPresenter(
params = params,
buildMeta = buildMeta,
@@ -234,6 +247,7 @@ private fun createPresenter(
),
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
loginHelper = loginHelper,
customLogoResIdProvider = customLogoResIdProvider,
)
fun createLoginHelper(

View File

@@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun OnBoardingPage(
modifier: Modifier = Modifier,
renderBackground: Boolean = true,
contentAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
footer: @Composable () -> Unit = {},
content: @Composable () -> Unit = {},
@@ -47,13 +48,15 @@ fun OnBoardingPage(
.fillMaxSize()
) {
// BG
Image(
modifier = Modifier
.fillMaxSize(),
painter = painterResource(id = R.drawable.onboarding_bg),
contentScale = ContentScale.Crop,
contentDescription = null,
)
if (renderBackground) {
Image(
modifier = Modifier
.fillMaxSize(),
painter = painterResource(id = R.drawable.onboarding_bg),
contentScale = ContentScale.Crop,
contentDescription = null,
)
}
Column(
modifier = Modifier
.fillMaxSize()