diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt index e92ebc5ca3..475a6d64b9 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt @@ -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, + ) + } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 5c7b1e8589..b2d51cca3a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -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() 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 diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index d4f811162d..9933f9f8df 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -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) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/TravalaNotificationWithBackground.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/TravalaNotificationWithBackground.kt index 9e6ba3db1d..6fc2f27ebd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/TravalaNotificationWithBackground.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/TravalaNotificationWithBackground.kt @@ -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( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/snackbar/CopiedTextSnackbar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/snackbar/CopiedTextSnackbar.kt index 35cc8f6903..eea8a09bae 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/snackbar/CopiedTextSnackbar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/snackbar/CopiedTextSnackbar.kt @@ -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) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index e5628c9bf5..968b635c5d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -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 { val LocalSnackbarHostState = staticCompositionLocalOf { error("No SnackbarHostState provided") +} + +val LocalWindowSize = staticCompositionLocalOf { + error("No WindowSize provided") } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt index 2a9e0caf65..48e3237b47 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt @@ -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, + ) + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeActivity.kt b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeActivity.kt index fe4f115dcd..9d4acd5fc0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeActivity.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeActivity.kt @@ -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)) } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeBottomSheetFragment.kt b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeBottomSheetFragment.kt index b437a39cd5..75dc4e8a98 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeBottomSheetFragment.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeBottomSheetFragment.kt @@ -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 { diff --git a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeFragment.kt b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeFragment.kt index fb811871af..51d450ed02 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeFragment.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeFragment.kt @@ -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 } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt index 740eac38e1..d6b1869ec5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt @@ -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, ) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/windowsize/WindowSize.kt b/core/ui/src/main/java/com/tangem/core/ui/windowsize/WindowSize.kt new file mode 100644 index 0000000000..a8928b86f8 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/windowsize/WindowSize.kt @@ -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 +} \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/api/OnboardingSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/api/OnboardingSeedPhraseScreen.kt index 978f1ea01e..6db1217109 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/api/OnboardingSeedPhraseScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/api/OnboardingSeedPhraseScreen.kt @@ -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) } } } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index b549e9bf7b..a9ef5f15c0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -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" }