Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-17 11:24:16 +03:00
commit f5b2046666
14 changed files with 160 additions and 24 deletions

View file

@ -1,6 +1,10 @@
package com.tangem.tap.features.onboarding.products.wallet.ui
import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.collectAsState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.windowsize.rememberWindowSize
import com.tangem.feature.onboarding.api.OnboardingSeedPhraseScreen
import com.tangem.feature.onboarding.api.OnboardingSeedPhraseApi
import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseScreen
@ -16,6 +20,7 @@ import com.tangem.wallet.R
*/
internal class OnboardingSeedPhraseStateHandler(
private val onboardingSeedPhraseApi: OnboardingSeedPhraseApi = OnboardingSeedPhraseScreen(),
private val activity: Activity,
) {
fun newState(
@ -49,11 +54,16 @@ internal class OnboardingSeedPhraseStateHandler(
val subScreen = viewModel.currentScreen.collectAsState().value
setMainScreenToolbarTitle(walletFragment, subScreen)
onboardingSeedPhraseApi.ScreenContent(
uiState = viewModel.uiState,
subScreen = subScreen,
progress = viewModel.progress.collectAsState(0).value.toFloat() / onboardingWalletMaxProgress,
)
TangemTheme(
isDark = isSystemInDarkTheme(),
windowSize = rememberWindowSize(activity = activity),
) {
onboardingSeedPhraseApi.ScreenContent(
uiState = viewModel.uiState,
subScreen = subScreen,
progress = viewModel.progress.collectAsState(0).value.toFloat() / onboardingWalletMaxProgress,
)
}
}
}

View file

@ -68,7 +68,8 @@ class OnboardingWalletFragment :
private val canSkipBackup by lazy { arguments?.getBoolean(OnboardingRouter.CAN_SKIP_BACKUP) ?: true }
private val seedPhraseStateHandler: OnboardingSeedPhraseStateHandler = OnboardingSeedPhraseStateHandler()
private lateinit var seedPhraseStateHandler: OnboardingSeedPhraseStateHandler
private val seedPhraseViewModel by viewModels<SeedPhraseViewModel>()
private lateinit var cardsWidget: WalletCardsWidget
@ -79,6 +80,7 @@ class OnboardingWalletFragment :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
seedPhraseStateHandler = OnboardingSeedPhraseStateHandler(activity = requireActivity())
val newSeedPhraseRouter = makeSeedPhraseRouter()
seedPhraseRouter = newSeedPhraseRouter

View file

@ -22,6 +22,12 @@ dependencies {
implementation(deps.androidx.fragment.ktx)
implementation(deps.androidx.paging.runtime)
implementation(deps.androidx.palette)
implementation(deps.androidx.windowManager) {
exclude(
deps.kotlin.coroutines.android.get().module.group,
deps.kotlin.coroutines.android.get().module.name
)
}
/** Compose */
implementation(deps.compose.constraintLayout)

View file

@ -36,6 +36,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemColorPalette.White
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
/**
* Travala notification with image background
@ -192,7 +193,7 @@ private fun formatSubtitle(subtitle: String): AnnotatedString {
@Preview
@Composable
private fun TravalaNotificationWithBackgroundPreview() {
TangemTheme {
TangemThemePreview {
TravalaNotificationWithBackground(
config = NotificationConfig(
title = resourceReference(

View file

@ -19,6 +19,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
/**
* Snackbar to inform the user about copying text to the clipboard
@ -100,7 +101,7 @@ private fun MessageText(text: TextReference, modifier: Modifier = Modifier) {
private fun Preview_CopiedTextSnackbar(
@PreviewParameter(CopiedTextSnackbarDataProvider::class) message: TextReference,
) {
TangemTheme(isDark = false) {
TangemThemePreview {
CopiedTextSnackbar(message = message)
}
}

View file

@ -8,9 +8,12 @@ import androidx.compose.runtime.*
import com.tangem.core.ui.haptic.HapticManager
import com.tangem.core.ui.haptic.MockHapticManager
import com.tangem.core.ui.windowsize.WindowSize
@Composable
fun TangemTheme(
isDark: Boolean = false,
windowSize: WindowSize,
typography: TangemTypography = TangemTheme.typography,
dimens: TangemDimens = TangemTheme.dimens,
hapticManager: HapticManager = MockHapticManager,
@ -33,6 +36,7 @@ fun TangemTheme(
LocalIsInDarkTheme provides isDark,
LocalHapticManager provides hapticManager,
LocalSnackbarHostState provides snackbarHostState,
LocalWindowSize provides windowSize,
) {
ProvideTextStyle(
value = TangemTheme.typography.body1,
@ -208,4 +212,8 @@ val LocalHapticManager = staticCompositionLocalOf<HapticManager> {
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
error("No SnackbarHostState provided")
}
val LocalWindowSize = staticCompositionLocalOf<WindowSize> {
error("No WindowSize provided")
}

View file

@ -1,7 +1,9 @@
package com.tangem.core.ui.res
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.runtime.Composable
import com.tangem.core.ui.windowsize.rememberWindowSizePreview
@Composable
fun TangemThemePreview(
@ -12,10 +14,13 @@ fun TangemThemePreview(
) {
val isDarkTheme = isDark ?: isSystemInDarkTheme()
TangemTheme(
isDark = isDarkTheme,
typography = typography,
dimens = dimens,
content = content,
)
BoxWithConstraints {
TangemTheme(
isDark = isDarkTheme,
typography = typography,
dimens = dimens,
windowSize = rememberWindowSizePreview(maxWidth, maxHeight),
content = content,
)
}
}

View file

@ -12,6 +12,6 @@ abstract class ComposeActivity : ComponentActivity(), ComposeScreen {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(createComposeView(context = this))
setContentView(createComposeView(context = this, activity = this))
}
}

View file

@ -52,7 +52,7 @@ abstract class ComposeBottomSheetFragment : BottomSheetDialogFragment(), Compose
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return createComposeView(inflater.context)
return createComposeView(inflater.context, requireActivity())
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

View file

@ -17,7 +17,7 @@ abstract class ComposeFragment : Fragment(), ComposeScreen {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val isTransitionsInflated = TransitionInflater.from(requireContext()).inflateTransitions()
return createComposeView(inflater.context).also {
return createComposeView(inflater.context, requireActivity()).also {
it.isTransitionGroup = isTransitionsInflated
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.core.ui.screen
import android.app.Activity
import android.content.Context
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
@ -10,6 +11,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.windowsize.rememberWindowSize
import com.tangem.domain.apptheme.model.AppThemeMode
/**
@ -50,13 +52,15 @@ internal interface ComposeScreen {
* @param context The context.
* @return A [ComposeView] instance with the defined screen content.
*/
internal fun ComposeScreen.createComposeView(context: Context): ComposeView {
internal fun ComposeScreen.createComposeView(context: Context, activity: Activity): ComposeView {
return ComposeView(context).apply {
setContent {
val appThemeMode by uiDependencies.appThemeModeHolder.appThemeMode
val windowSize = rememberWindowSize(activity = activity)
TangemTheme(
isDark = shouldUseDarkTheme(appThemeMode),
windowSize = windowSize,
hapticManager = uiDependencies.hapticManager,
snackbarHostState = uiDependencies.globalSnackbarHostState,
) {

View file

@ -0,0 +1,99 @@
package com.tangem.core.ui.windowsize
import android.app.Activity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.toComposeRect
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.window.layout.WindowMetricsCalculator
/**
* The preferred way to determine the window size is because it provides the actual size of the window as opposed to
* [LocalConfiguration.current.screenHeightDp]
*
* @property width actual window width
* @property height actual window height
* @property widthSizeType window size type based on width
* @property heightSizeType window size type based on height
* @property smallestSize smallest size of width and height
*/
data class WindowSize(
val width: Dp,
val height: Dp,
) {
val widthSizeType: WindowSizeType = getWidthWindowSizeType(width)
val heightSizeType: WindowSizeType = getHeightWindowSizeType(height)
val smallestSize: Dp = minOf(width, height)
fun widthAtLeast(type: WindowSizeType): Boolean = this.widthSizeType.ordinal >= type.ordinal
fun widthAtLeast(value: Dp): Boolean = this.width >= value
fun heightAtLeast(type: WindowSizeType): Boolean = this.heightSizeType.ordinal >= type.ordinal
fun heightAtLeast(value: Dp): Boolean = this.height >= value
fun widthAtMost(type: WindowSizeType): Boolean = this.widthSizeType.ordinal <= type.ordinal
fun widthAtMost(value: Dp): Boolean = this.width <= value
fun heightAtMost(type: WindowSizeType): Boolean = this.heightSizeType.ordinal <= type.ordinal
fun heightAtMost(value: Dp): Boolean = this.height <= value
private fun getWidthWindowSizeType(windowDp: Dp): WindowSizeType = when {
windowDp < 300.dp -> WindowSizeType.ExtraSmall
windowDp < 380.dp -> WindowSizeType.Small
windowDp < 540.dp -> WindowSizeType.Normal
windowDp < 700.dp -> WindowSizeType.Large
else -> WindowSizeType.ExtraLarge
}
private fun getHeightWindowSizeType(heightDp: Dp): WindowSizeType = when {
heightDp < 480.dp -> WindowSizeType.ExtraSmall
heightDp < 640.dp -> WindowSizeType.Small
heightDp < 860.dp -> WindowSizeType.Normal
heightDp < 1100.dp -> WindowSizeType.Large
else -> WindowSizeType.ExtraLarge
}
}
enum class WindowSizeType {
ExtraSmall, Small, Normal, Large, ExtraLarge
}
@Composable
fun rememberWindowSize(activity: Activity): WindowSize {
val windowBoundsSize = rememberWindowBoundsSize(activity)
val windowDpSize = with(LocalDensity.current) {
windowBoundsSize.toDpSize()
}
return WindowSize(
width = windowDpSize.width,
height = windowDpSize.height,
)
}
@Composable
internal fun rememberWindowSizePreview(width: Dp, height: Dp): WindowSize {
return remember(width, height) {
WindowSize(
width = width,
height = height,
)
}
}
@Composable
private fun rememberWindowBoundsSize(activity: Activity): Size {
val configuration = LocalConfiguration.current
val windowMetrics = remember(configuration) {
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
}
return windowMetrics.bounds.toComposeRect().size
}

View file

@ -1,7 +1,6 @@
package com.tangem.feature.onboarding.api
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.runtime.Composable
@ -19,11 +18,9 @@ class OnboardingSeedPhraseScreen : OnboardingSeedPhraseApi {
@Composable
override fun ScreenContent(uiState: OnboardingSeedPhraseState, subScreen: SeedPhraseScreen, progress: Float) {
BackHandler(onBack = uiState.onBackClick)
TangemTheme(isDark = isSystemInDarkTheme()) {
Column {
ProgressIndicator(progress)
Content(subScreen, uiState)
}
Column {
ProgressIndicator(progress)
Content(subScreen, uiState)
}
}
}

View file

@ -21,6 +21,7 @@ androidxLifecycle = "2.5.1"
androidx-paging = "3.1.1"
androidx-palette = "1.0.0"
androidx-datastore = "1.0.0"
androidxWindowManager = "1.3.0"
# endregion AndroidX
# region Compose
@ -145,6 +146,7 @@ androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "androidx-paging" }
androidx-swipeRefreshLayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swipeRefreshLayout" }
androidx-palette = { module = "androidx.palette:palette", version.ref = "androidx-palette" }
androidx-windowManager = { group = "androidx.window", name = "window", version.ref = "androidxWindowManager" }
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidxLifecycle" }
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
lifecycle-viewModel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
@ -217,6 +219,7 @@ armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" }
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" }
kotlin-coroutines-rx2 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2", version.ref = "coroutine" }
kotlin-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutine" }
kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" }
desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" }
googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" }