Add a way to customize the on boarding logo.
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
12
features/login/impl/src/main/res/raw/keep.xml
Normal file
12
features/login/impl/src/main/res/raw/keep.xml
Normal 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" />
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user