Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-18 14:18:02 +03:00
commit e5066f63fc
41 changed files with 414 additions and 320 deletions

View file

@ -12,15 +12,18 @@ import android.nfc.NfcAdapter
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
@ -195,6 +198,13 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
installAppTheme() // We need to call it before onCreate to prevent unnecessary activity recreation
enableEdgeToEdge(
navigationBarStyle = SystemBarStyle.auto(
Color.Transparent.toArgb(),
Color.Transparent.toArgb(),
),
)
super.onCreate(savedInstanceState)
splashScreen.setKeepOnScreenCondition { viewModel.isSplashScreenShown }
@ -307,8 +317,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@SuppressLint("SourceLockedOrientationActivity")
private fun initContent() {
WindowCompat.setDecorFitsSystemWindows(window, false)
supportFragmentManager.registerFragmentLifecycleCallbacks(
NavBarInsetsFragmentLifecycleCallback(),
true,

View file

@ -1,12 +1,12 @@
package com.tangem.tap.features.customtoken.impl.presentation
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.hilt.navigation.compose.hiltViewModel
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.tap.features.customtoken.impl.presentation.ui.AddCustomTokenScreen
@ -30,12 +30,10 @@ internal class AddCustomTokenFragment : ComposeFragment() {
val viewModel = hiltViewModel<AddCustomTokenViewModel>().apply {
LocalLifecycleOwner.current.lifecycle.addObserver(this)
}
val statusBarColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(color = statusBarColor)
}
AddCustomTokenScreen(
modifier = Modifier.systemBarsPadding(),
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.systemBarsPadding(),
stateHolder = viewModel.uiState,
)
}

View file

@ -6,8 +6,7 @@ import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.screen.ComposeFragment
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -22,12 +21,8 @@ internal class AppCurrencySelectorFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
NavigationBar3ButtonsScrim()
AppCurrencySelectorScreen(
modifier = modifier,
state = uiState,

View file

@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -40,7 +41,9 @@ internal fun AppCurrencySelectorScreen(state: AppCurrencySelectorState, modifier
val listState = rememberLazyListState()
Scaffold(
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
modifier = modifier
.imePadding()
.nestedScroll(scrollBehavior.nestedScrollConnection),
containerColor = TangemTheme.colors.background.secondary,
topBar = {
TopBar(
@ -49,6 +52,7 @@ internal fun AppCurrencySelectorScreen(state: AppCurrencySelectorState, modifier
state = state,
)
},
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
content = { paddingValues ->
val contentModifier = Modifier
.padding(paddingValues)
@ -62,7 +66,7 @@ internal fun AppCurrencySelectorScreen(state: AppCurrencySelectorState, modifier
modifier = contentModifier,
listState = listState,
currencies = state.items,
selectedId = state.selectedId.orEmpty(),
selectedId = state.selectedId,
onCurrencyClick = state.onCurrencyClick,
)
}
@ -175,6 +179,7 @@ private fun SearchBar(onInputChange: (String) -> Unit, modifier: Modifier = Modi
@Composable
private fun LoadingList(modifier: Modifier = Modifier) {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Column(modifier = modifier) {
repeat(times = 10) {
Row(
@ -199,6 +204,7 @@ private fun LoadingList(modifier: Modifier = Modifier) {
)
}
}
Spacer(Modifier.height(bottomBarHeight))
}
}
@ -210,9 +216,11 @@ private fun CurrenciesList(
onCurrencyClick: (Currency) -> Unit,
modifier: Modifier = Modifier,
) {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
LazyColumn(
modifier = modifier,
state = listState,
contentPadding = PaddingValues(bottom = bottomBarHeight),
) {
items(
items = currencies,

View file

@ -1,13 +1,17 @@
package com.tangem.tap.features.details.ui.appsettings
import android.content.res.Configuration
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
@ -32,6 +36,7 @@ internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () ->
},
titleRes = R.string.app_settings_title,
onBackClick = onBackClick,
addBottomInsets = false,
)
}
@ -44,7 +49,11 @@ private fun AppSettings(state: AppSettingsScreenState.Content) {
null -> Unit
}
LazyColumn {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
LazyColumn(
contentPadding = PaddingValues(bottom = bottomBarHeight),
) {
items(
items = state.items,
key = Item::id,

View file

@ -13,8 +13,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.wallet.R
@Composable
@ -24,31 +24,42 @@ internal fun SettingsScreensScaffold(
modifier: Modifier = Modifier,
@StringRes titleRes: Int? = null,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
addBottomInsets: Boolean = true,
fab: @Composable () -> Unit = {},
) {
val state = rememberScaffoldState(snackbarHostState = snackbarHostState)
val backgroundColor = TangemTheme.colors.background.secondary
BackHandler(onBack = onBackClick)
SystemBarsEffect {
setSystemBarsColor(backgroundColor)
}
Scaffold(
scaffoldState = state,
topBar = {
EmptyTopBarWithNavigation(
modifier = Modifier.statusBarsPadding(),
onBackClick = onBackClick,
backgroundColor = backgroundColor,
)
},
modifier = modifier.systemBarsPadding(),
modifier = modifier,
contentWindowInsets = WindowInsetsZero,
backgroundColor = backgroundColor,
floatingActionButton = fab,
content = { paddings ->
floatingActionButton = {
Box(modifier = Modifier.navigationBarsPadding()) {
fab()
}
},
content = { paddingValues ->
Column(
modifier = Modifier
.padding(paddings)
.run {
if (addBottomInsets) {
navigationBarsPadding()
} else {
this
}
}
.padding(paddingValues)
.fillMaxSize(),
) {
if (titleRes != null) {
@ -81,9 +92,11 @@ internal fun ScreenTitle(titleRes: Int, modifier: Modifier = Modifier) {
@Composable
internal fun EmptyTopBarWithNavigation(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
backgroundColor: Color = TangemTheme.colors.background.primary,
) {
TopAppBar(
modifier = modifier,
title = { },
navigationIcon =
{

View file

@ -6,14 +6,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.components.SystemBarsIconsDisposable
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.domain.tokens.TokensAction
import com.tangem.tap.common.analytics.events.IntroductionProcess
@ -44,15 +42,12 @@ class HomeFragment : ComposeFragment(), StoreSubscriber<HomeState> {
@Composable
override fun ScreenContent(modifier: Modifier) {
BackHandler(onBack = requireActivity()::finish)
SystemBarsEffect {
setSystemBarsColor(color = Color.Transparent, darkIcons = false)
}
SystemBarsIconsDisposable(darkIcons = false)
ScreenContent()
}
override fun onStart() {
super.onStart()
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, false) }
store.subscribe(subscriber = this) { state ->
state

View file

@ -1,13 +1,10 @@
package com.tangem.tap.features.tokens.impl.presentation
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.hilt.navigation.compose.hiltViewModel
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.tap.features.tokens.impl.presentation.ui.TokensListScreen
import com.tangem.tap.features.tokens.impl.presentation.viewmodels.TokensListViewModel
@ -30,12 +27,7 @@ internal class TokensListFragment : ComposeFragment() {
val viewModel = hiltViewModel<TokensListViewModel>().apply {
LocalLifecycleOwner.current.lifecycle.addObserver(this)
}
val statusBarColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(color = statusBarColor)
}
TokensListScreen(
modifier = Modifier.systemBarsPadding(),
stateHolder = viewModel.uiState,
)
}

View file

@ -15,7 +15,6 @@ import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
@ -34,6 +33,8 @@ import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import com.tangem.core.ui.components.BottomFade
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.res.TangemTheme
@ -61,32 +62,45 @@ internal fun TokensListScreen(stateHolder: TokensListStateHolder, modifier: Modi
Scaffold(
modifier = modifier,
topBar = { TokensListToolbar(state = stateHolder.toolbarState) },
topBar = {
TokensListToolbar(state = stateHolder.toolbarState)
},
floatingActionButton = {
if (stateHolder is TokensListStateHolder.ManageContent) {
val density = LocalDensity.current
val verticalPadding = TangemTheme.dimens.spacing32
SaveChangesButton(
modifier = Modifier.onSizeChanged {
with(density) { floatingButtonHeight = it.height.toDp() + verticalPadding }
},
modifier = Modifier
.navigationBarsPadding()
.onSizeChanged {
with(density) { floatingButtonHeight = it.height.toDp() + verticalPadding }
},
showProgress = stateHolder.isSavingInProgress,
onClick = stateHolder.onSaveButtonClick,
)
}
},
floatingActionButtonPosition = FabPosition.Center,
backgroundColor = TangemTheme.colors.background.primary,
) { scaffoldPadding ->
backgroundColor = TangemTheme.colors.background.secondary,
) { _ ->
val tokens = stateHolder.tokens.collectAsLazyPagingItems()
TokensListContent(
isDifferentAddressesBlockVisible = stateHolder.isDifferentAddressesBlockVisible,
tokens = tokens,
scaffoldPadding = scaffoldPadding,
bottomMarginDp = floatingButtonHeight,
)
if (stateHolder !is TokensListStateHolder.ManageContent) {
NavigationBar3ButtonsScrim()
}
Box {
TokensListContent(
isDifferentAddressesBlockVisible = stateHolder.isDifferentAddressesBlockVisible,
tokens = tokens,
bottomMarginDp = floatingButtonHeight,
)
if (stateHolder is TokensListStateHolder.ManageContent) {
BottomFade(Modifier.align(Alignment.BottomCenter))
}
}
Crossfade(targetState = stateHolder.isLoading, label = "Update progress bar visibility") {
if (it) {
@ -112,23 +126,24 @@ private fun LoadingContent() {
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun TokensListContent(
isDifferentAddressesBlockVisible: Boolean,
tokens: LazyPagingItems<TokenItemState>,
scaffoldPadding: PaddingValues,
bottomMarginDp: Dp,
) {
val state = rememberLazyListState()
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
LazyColumn(
modifier = Modifier
.background(color = TangemTheme.colors.background.primary)
.imePadding()
.fillMaxSize()
.padding(scaffoldPadding),
.fillMaxSize(),
state = state,
contentPadding = PaddingValues(bottom = bottomMarginDp),
contentPadding = PaddingValues(
bottom = bottomMarginDp + bottomBarHeight,
),
) {
item(
key = "DifferentAddressesWarning$isDifferentAddressesBlockVisible",

View file

@ -1,10 +1,7 @@
package com.tangem.tap.features.tokens.impl.presentation.ui
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@ -16,6 +13,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -42,8 +40,15 @@ internal fun TokensListToolbar(state: TokensListToolbarState) {
} else {
AppBarDefaults.TopAppBarElevation
}
val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() }
TopAppBar(
backgroundColor = TangemTheme.colors.background.secondary,
contentPadding = PaddingValues(
start = TangemTheme.dimens.spacing4,
end = TangemTheme.dimens.spacing4,
top = statusBarHeight,
),
elevation = toolbarElevation,
) {
IconButton(onClick = state.onBackButtonClick) {

View file

@ -16,7 +16,6 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.tap.common.analytics.events.SignIn
@ -49,19 +48,14 @@ internal class WelcomeFragment : ComposeFragment() {
val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference())
val warning by rememberUpdatedState(newValue = state.warning)
val backgroundColor = TangemTheme.colors.background.primary
SystemBarsEffect {
setSystemBarsColor(backgroundColor)
}
BackHandler {
requireActivity().finish()
}
Box(
modifier = modifier
.systemBarsPadding()
.background(backgroundColor),
.background(TangemTheme.colors.background.primary)
.systemBarsPadding(),
) {
WelcomeScreenContent(
showUnlockProgress = state.showUnlockWithBiometricsProgress,

View file

@ -0,0 +1,33 @@
package com.tangem.core.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import com.tangem.core.ui.res.TangemTheme
/**
* A composable that draws a fade effect at the bottom of the screen. Used on screens with a list of repeating
* elements and floating button at the bottom of the screen.
*/
@Composable
fun BottomFade(modifier: Modifier = Modifier, backgroundColor: Color = TangemTheme.colors.background.secondary) {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Box(
modifier = modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size100 + bottomBarHeight)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
backgroundColor,
),
),
),
)
}

View file

@ -1,12 +0,0 @@
package com.tangem.core.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import com.google.accompanist.systemuicontroller.SystemUiController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable
fun SystemBarsEffect(block: SystemUiController.() -> Unit) {
val systemUiController = rememberSystemUiController()
SideEffect { block(systemUiController) }
}

View file

@ -0,0 +1,61 @@
package com.tangem.core.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.ui.res.LocalIsInDarkTheme
/**
* Provides the ability to set a scrim for 3-button navigation
*
* Automatically makes navigation bar transparent when the composable is disposed.
*
* Usage example: [com.tangem.feature.tokendetails.presentation.TokenDetailsFragment]
*/
@Composable
fun NavigationBar3ButtonsScrim() {
val systemUiController = rememberSystemUiController()
val isDarkTheme = LocalIsInDarkTheme.current
SideEffect {
systemUiController.isNavigationBarContrastEnforced = true
}
LaunchedEffect(systemUiController.isNavigationBarContrastEnforced) {
if (systemUiController.isNavigationBarContrastEnforced.not()) {
systemUiController.isNavigationBarContrastEnforced = true
}
}
DisposableEffect(isDarkTheme) {
onDispose {
systemUiController.isNavigationBarContrastEnforced = false
}
}
}
/**
* Provides the ability to set dark/light icons in cases where the darkness of the screen differs from that
* provided in [TangemTheme].
*
* Automatically returns the icon colors to their original state when the composable is disposed.
*
* Usage example: [com.tangem.feature.qrscanning.QrScanningFragment]
*/
@Composable
fun SystemBarsIconsDisposable(darkIcons: Boolean, isNavigationBarContrastEnforced: Boolean = false) {
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.systemBarsDarkContentEnabled = darkIcons
systemUiController.isNavigationBarContrastEnforced = isNavigationBarContrastEnforced
}
val isDarkTheme = LocalIsInDarkTheme.current
DisposableEffect(isDarkTheme) {
onDispose {
systemUiController.systemBarsDarkContentEnabled = !isDarkTheme
systemUiController.isNavigationBarContrastEnforced = false
}
}
}

View file

@ -1,13 +1,14 @@
package com.tangem.core.ui.components.bottomsheets
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@ -15,6 +16,7 @@ import kotlinx.coroutines.launch
* Tangem bottom sheet with custom draggable header and config
*
* @param config data model containing logic and ui models
* @param
* @param content custom bottom sheet content
*/
@OptIn(ExperimentalMaterial3Api::class)
@ -22,20 +24,33 @@ import kotlinx.coroutines.launch
inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
config: TangemBottomSheetConfig,
containerColor: Color = TangemTheme.colors.background.primary,
addBottomInsets: Boolean = true,
crossinline content: @Composable ColumnScope.(T) -> Unit,
) {
var isVisible by remember { mutableStateOf(value = config.isShow) }
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
if (isVisible && config.content is T) {
ModalBottomSheet(
// FIXME temporary solution to fix height of the bottom sheet
modifier = Modifier.sizeIn(maxHeight = LocalWindowSize.current.height - statusBarHeight),
onDismissRequest = config.onDismissRequest,
sheetState = sheetState,
containerColor = containerColor,
shape = TangemTheme.shapes.bottomSheetLarge,
windowInsets = WindowInsetsZero,
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
) {
content(config.content)
if (addBottomInsets) {
Column(
// FIXME temporary solution to fix height of the bottom sheet
modifier = Modifier.navigationBarsPadding(),
) {
content(config.content)
}
} else {
content(config.content)
}
}
}

View file

@ -81,6 +81,7 @@ data class TangemDimens internal constructor(
val size90: Dp = 90.dp,
val size93: Dp = 93.dp,
val size96: Dp = 96.dp,
val size100: Dp = 100.dp,
val size102: Dp = 102.dp,
val size108: Dp = 108.dp,
val size110: Dp = 110.dp,

View file

@ -5,9 +5,10 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.ui.haptic.HapticManager
import com.tangem.core.ui.haptic.MockHapticManager
import com.tangem.core.ui.windowsize.WindowSize
@Composable
@ -25,6 +26,16 @@ fun TangemTheme(
.also { it.update(themeColors) }
val shapes = remember { TangemShapes(dimens) }
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = !isDark,
isNavigationBarContrastEnforced = false,
)
}
MaterialTheme(
colors = materialThemeColors(colors = themeColors, isDark = isDark),
) {

View file

@ -0,0 +1,12 @@
package com.tangem.core.ui.utils
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
object WindowInsetsZero : WindowInsets {
override fun getBottom(density: Density): Int = 0
override fun getLeft(density: Density, layoutDirection: LayoutDirection): Int = 0
override fun getRight(density: Density, layoutDirection: LayoutDirection): Int = 0
override fun getTop(density: Density): Int = 0
}

View file

@ -19,7 +19,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.ComposableContentComponent
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
import com.tangem.core.ui.res.LocalSnackbarHostState
import com.tangem.core.ui.res.TangemTheme
@ -42,10 +41,6 @@ internal fun DetailsScreen(
val backgroundColor = TangemTheme.colors.background.secondary
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
SystemBarsEffect {
setSystemBarsColor(backgroundColor)
}
BackHandler(onBack = state.popBack)
Scaffold(

View file

@ -7,8 +7,6 @@ import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.features.disclaimer.api.DisclaimerRouter
import com.tangem.features.disclaimer.impl.presentation.ui.DisclaimerScreen
@ -37,7 +35,6 @@ internal class DisclaimerFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.primary
BackHandler {
if (isTosAccepted) {
appRouter.pop()
@ -45,9 +42,6 @@ internal class DisclaimerFragment : ComposeFragment() {
requireActivity().finish()
}
}
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
DisclaimerScreen(viewModel.state, appRouter::pop)
}

View file

@ -15,17 +15,16 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.tangem.core.ui.components.BottomFade
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.appbar.AppBarWithAdditionalButtons
import com.tangem.core.ui.components.appbar.models.AdditionalButton
@ -39,17 +38,14 @@ import com.tangem.features.disclaimer.impl.presentation.state.DummyDisclaimer
@Composable
internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) {
val bottomPadding = if (state.isTosAccepted) TangemTheme.dimens.spacing16 else TangemTheme.dimens.size64
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Box(
modifier = Modifier
.statusBarsPadding()
.background(TangemColorPalette.Dark6),
.background(TangemColorPalette.Dark6)
.statusBarsPadding(),
) {
Column(
modifier = Modifier
.navigationBarsPadding()
.padding(bottom = bottomPadding),
) {
Column {
AppBarWithAdditionalButtons(
text = resourceReference(R.string.disclaimer_title),
startButton = AdditionalButton(
@ -58,23 +54,34 @@ internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) {
).takeIf { state.isTosAccepted },
)
DisclaimerContent(state.url)
Spacer(
modifier = Modifier
.background(TangemColorPalette.Dark6)
.height(
height = if (!state.isTosAccepted) {
bottomBarHeight
} else {
bottomBarHeight + TangemTheme.dimens.size64
},
),
)
}
if (!state.isTosAccepted) {
BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = TangemColorPalette.Dark6)
DisclaimerButton(state.onAccept)
} else {
NavigationBar3ButtonsScrim()
}
}
}
@Composable
private fun ColumnScope.DisclaimerContent(url: String) {
val transparent = Color.Transparent
val progressState = remember { mutableStateOf(ProgressState.Loading) }
val webClient = remember { DisclaimerWebViewClient(progressState) }
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
modifier = Modifier,
) {
AndroidView(
factory = {
@ -83,9 +90,10 @@ private fun ColumnScope.DisclaimerContent(url: String) {
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
setBackgroundColor(transparent.toArgb())
setBackgroundColor(TangemColorPalette.Dark6.toArgb())
settings.allowFileAccess = false
settings.javaScriptEnabled = false
overScrollMode = View.OVER_SCROLL_NEVER
webViewClient = webClient
loadUrl(url)
@ -94,28 +102,34 @@ private fun ColumnScope.DisclaimerContent(url: String) {
)
when (progressState.value) {
ProgressState.Loading -> CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
modifier = Modifier
.align(Alignment.Center)
.padding(TangemTheme.dimens.spacing8),
)
ProgressState.Error -> Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing16)
.align(Alignment.Center),
) {
Text(
text = stringResource(id = R.string.disclaimer_error_loading),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.constantWhite,
)
PrimaryButton(
text = stringResource(R.string.common_retry),
onClick = { webClient.reset() },
)
ProgressState.Loading -> {
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
modifier = Modifier
.align(Alignment.Center)
.padding(TangemTheme.dimens.spacing8),
)
}
}
ProgressState.Error -> Box(modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing16)
.align(Alignment.Center),
) {
Text(
text = stringResource(id = R.string.disclaimer_error_loading),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.constantWhite,
)
PrimaryButton(
text = stringResource(R.string.common_retry),
onClick = { webClient.reset() },
)
}
}
else -> Unit
}
@ -130,23 +144,6 @@ private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) {
} else {
true
}
val bottomInsetsPx = WindowInsets.navigationBars.getBottom(LocalDensity.current)
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.height(TangemTheme.dimens.size110 + bottomInsetsPx.dp)
.background(
Brush.verticalGradient(
colors = listOf(
TangemTheme.colors.background.primary.copy(alpha = 0f),
TangemTheme.colors.background.primary.copy(alpha = 0.75f),
TangemTheme.colors.background.primary.copy(alpha = 0.95f),
TangemTheme.colors.background.primary,
),
),
),
)
PrimaryButton(
text = stringResource(id = R.string.common_accept),
onClick = { onAccept(shouldAskPushPermission) },

View file

@ -4,12 +4,7 @@ import android.annotation.SuppressLint
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.*
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
@ -36,6 +31,7 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.LocalDensity
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@ -43,7 +39,9 @@ import androidx.navigation.compose.rememberNavController
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetDraggableHeader
import com.tangem.core.ui.components.bottomsheets.collapse
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRoute
import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRouter
import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel
@ -57,6 +55,7 @@ fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig) {
var isVisible by remember { mutableStateOf(value = config.isShow) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
if (isVisible) {
// ViewModel cannot be scoped to ModalBottomSheet's lifecycle,
@ -67,11 +66,13 @@ fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig) {
}
ModalBottomSheetWithBackHandling(
modifier = Modifier
.sizeIn(maxHeight = LocalWindowSize.current.height - statusBarHeight),
onDismissRequest = config.onDismissRequest,
sheetState = sheetState,
containerColor = TangemTheme.colors.background.tertiary,
shape = TangemTheme.shapes.bottomSheetLarge,
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Top),
windowInsets = WindowInsetsZero,
dragHandle = { TangemBottomSheetDraggableHeader(color = TangemTheme.colors.background.tertiary) },
properties = ModalBottomSheetDefaults.properties(shouldDismissOnBackPress = false),
) {

View file

@ -65,7 +65,6 @@ private fun Content(state: ManageTokensState, onHeaderSizeChange: (Dp) -> Unit)
Box(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding()
.imePadding()
.background(color = TangemTheme.colors.background.primary),
) {

View file

@ -7,21 +7,16 @@ import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.mlkit.vision.common.InputImage
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.components.SystemBarsIconsDisposable
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
@ -105,7 +100,8 @@ internal class QrScanningFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
StatusBarTransparencyDisposable()
SystemBarsIconsDisposable(darkIcons = false)
QrScanningContent(
executor = { cameraExecutor },
analyzer = { cameraAnalyzer },
@ -145,29 +141,4 @@ internal class QrScanningFragment : ComposeFragment() {
fun create() = QrScanningFragment()
}
}
@Composable
private fun StatusBarTransparencyDisposable() {
val systemUiController = rememberSystemUiController()
val systemBarsColor = TangemTheme.colors.background.secondary
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(Unit) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = false,
)
}
if (event == Lifecycle.Event.ON_STOP) {
systemUiController.setSystemBarsColor(systemBarsColor)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}

View file

@ -1,14 +1,11 @@
package com.tangem.feature.referral
import android.os.Bundle
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.referral.router.ReferralRouter
import com.tangem.feature.referral.ui.ReferralScreen
@ -35,12 +32,6 @@ class ReferralFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val backgroundColor = TangemTheme.colors.background.secondary
SystemBarsEffect { setSystemBarsColor(backgroundColor) }
ReferralScreen(
modifier = Modifier.systemBarsPadding(),
stateHolder = viewModel.uiState,
)
ReferralScreen(stateHolder = viewModel.uiState)
}
}

View file

@ -2,23 +2,20 @@ package com.tangem.feature.referral.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewState
import com.tangem.core.ui.components.appbar.AppBarWithAdditionalButtons
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.referral.presentation.R
/**
@ -28,15 +25,16 @@ import com.tangem.feature.referral.presentation.R
*/
@Composable
internal fun AgreementBottomSheetContent(url: String) {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Column(
modifier = Modifier
.fillMaxWidth()
.height(LocalConfiguration.current.screenHeightDp.dp - TangemTheme.dimens.spacing16),
.fillMaxSize()
.verticalScroll(rememberScrollState()),
) {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
AppBarWithAdditionalButtons(text = stringResource(id = R.string.details_referral_title))
AgreementHtmlView(url = url)
}
AppBarWithAdditionalButtons(text = stringResource(id = R.string.details_referral_title))
AgreementHtmlView(url = url)
Spacer(modifier = Modifier.height(bottomBarHeight))
}
}
@ -46,7 +44,7 @@ private fun AgreementHtmlView(url: String) {
val isInPreviewMode = LocalInspectionMode.current
WebView(
state = state,
modifier = Modifier.background(TangemTheme.colors.background.secondary),
modifier = Modifier.background(TangemTheme.colors.background.primary),
captureBackPresses = false,
onCreated = {
if (!isInPreviewMode) {

View file

@ -1,11 +1,18 @@
package com.tangem.feature.referral.ui
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.feature.referral.models.ReferralStateHolder
@OptIn(ExperimentalMaterial3Api::class)
@ -16,14 +23,18 @@ internal fun ReferralBottomSheet(
onDismissRequest: () -> Unit,
config: ReferralStateHolder.ReferralInfoState,
) {
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
if (isVisible) {
ModalBottomSheet(
modifier = Modifier.height(LocalWindowSize.current.height - statusBarHeight),
onDismissRequest = onDismissRequest,
sheetState = sheetState,
shape = RoundedCornerShape(
topStart = TangemTheme.dimens.radius16,
topEnd = TangemTheme.dimens.radius16,
),
windowInsets = WindowInsetsZero,
containerColor = TangemTheme.colors.background.primary,
) {
AgreementBottomSheetContent(

View file

@ -46,16 +46,16 @@ import kotlinx.coroutines.launch
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun ReferralScreen(stateHolder: ReferralStateHolder, modifier: Modifier = Modifier) {
internal fun ReferralScreen(stateHolder: ReferralStateHolder) {
var isBottomSheetVisible by remember { mutableStateOf(value = false) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val snackbarHostState = remember(::SnackbarHostState)
Scaffold(
modifier = modifier,
topBar = {
AppBarWithBackButton(
modifier = Modifier.statusBarsPadding(),
text = stringResource(R.string.details_referral_title),
onBackClick = stateHolder.headerState.onBackClicked,
)

View file

@ -9,8 +9,6 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.send.impl.navigation.InnerSendRouter
@ -61,10 +59,6 @@ internal class SendFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.tertiary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
val currentState = viewModel.stateRouter.currentState.collectAsStateWithLifecycle()
SendScreen(viewModel.uiState, currentState.value)
}

View file

@ -46,10 +46,10 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen)
BackHandler(onBack = onBackClick)
Column(
modifier = Modifier
.background(color = TangemTheme.colors.background.tertiary)
.fillMaxSize()
.imePadding()
.systemBarsPadding()
.background(color = TangemTheme.colors.background.tertiary),
.systemBarsPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SendAppBar(

View file

@ -7,8 +7,6 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.features.staking.api.navigation.StakingRouter
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
@ -59,10 +57,6 @@ internal class StakingFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.tertiary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
val currentState = viewModel.uiState.collectAsStateWithLifecycle()
StakingScreen(currentState.value)
}

View file

@ -28,10 +28,10 @@ internal fun StakingScreen(uiState: StakingUiState) {
BackHandler(onBack = uiState.clickIntents::onBackClick)
Column(
modifier = Modifier
.background(color = TangemTheme.colors.background.tertiary)
.fillMaxSize()
.imePadding()
.systemBarsPadding()
.background(color = TangemTheme.colors.background.tertiary),
.systemBarsPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SendAppBar(

View file

@ -2,13 +2,13 @@ package com.tangem.feature.swap.presentation
import android.os.Bundle
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.swap.router.CustomTabsManager
@ -47,17 +47,17 @@ class SwapFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
viewModel.onScreenOpened()
val backgroundColor = TangemTheme.colors.background.secondary
SystemBarsEffect { setSystemBarsColor(backgroundColor) }
ScreenContent(viewModel = viewModel)
}
@Suppress("TopLevelComposableFunctions")
@Composable
private fun ScreenContent(viewModel: SwapViewModel) {
Crossfade(targetState = viewModel.currentScreen, label = "") { screen ->
Crossfade(
modifier = Modifier.background(TangemTheme.colors.background.secondary),
targetState = viewModel.currentScreen,
label = "",
) { screen ->
when (screen) {
SwapNavScreen.Main -> SwapScreen(stateHolder = viewModel.uiState)
SwapNavScreen.Success -> {

View file

@ -8,6 +8,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.feature.swap.models.SwapStateHolder
import com.tangem.feature.swap.models.states.ChooseFeeBottomSheetConfig
import com.tangem.feature.swap.models.states.ChooseProviderBottomSheetConfig
@ -28,7 +29,7 @@ internal fun SwapScreen(stateHolder: SwapStateHolder) {
iconRes = R.drawable.ic_close_24,
)
},
contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0),
contentWindowInsets = WindowInsetsZero,
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->

View file

@ -6,9 +6,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeActivity
import com.tangem.feature.tester.presentation.actions.TesterActionsScreen
@ -45,9 +45,8 @@ internal class TesterActivity : ComposeActivity() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
val systemUiController = rememberSystemUiController()
systemUiController.setSystemBarsColor(systemBarsColor)
TesterNavHost()
}

View file

@ -5,8 +5,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.hilt.navigation.compose.hiltViewModel
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
@ -33,14 +32,8 @@ internal class TokenDetailsFragment : ComposeFragment() {
override fun ScreenContent(modifier: Modifier) {
val viewModel = hiltViewModel<TokenDetailsViewModel>()
viewModel.router = this@TokenDetailsFragment.internalTokenDetailsRouter
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
val systemBarsColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
NavigationBar3ButtonsScrim()
TokenDetailsScreen(state = viewModel.uiState)
}
}

View file

@ -3,10 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
@ -14,6 +11,7 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
@ -21,6 +19,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
@ -60,11 +59,13 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.e
@Composable
internal fun TokenDetailsScreen(state: TokenDetailsState) {
BackHandler(onBack = state.topAppBarConfig.onBackClick)
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
topBar = { TokenDetailsTopAppBar(config = state.topAppBarConfig) },
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->
val pullRefreshState = rememberPullRefreshState(
@ -90,7 +91,9 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing16),
contentPadding = PaddingValues(
bottom = TangemTheme.dimens.spacing16 + bottomBarHeight,
),
) {
item {
TokenInfoBlock(

View file

@ -3,8 +3,6 @@ package com.tangem.feature.wallet.presentation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.features.managetokens.navigation.ManageTokensUi
@ -37,11 +35,6 @@ internal class WalletFragment : ComposeFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
val systemBarsColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
_walletRouter.Initialize(
onFinish = requireActivity()::finish,
manageTokensUi = manageTokensUi,

View file

@ -16,11 +16,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -28,6 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.BottomFade
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
@ -36,6 +36,7 @@ import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem
@ -57,8 +58,12 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
Scaffold(
modifier = modifier,
topBar = {
TopBar(state.header, tokensListState)
TopBar(
config = state.header,
tokensListState = tokensListState,
)
},
contentWindowInsets = WindowInsetsZero,
content = { paddingValues ->
TokenList(
modifier = Modifier
@ -72,7 +77,9 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
},
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
Actions(state.actions)
Box(modifier = Modifier.navigationBarsPadding()) {
Actions(state.actions)
}
},
containerColor = TangemTheme.colors.background.secondary,
)
@ -104,9 +111,11 @@ private fun TokenList(
onDragEnd = onDragEnd,
)
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
val listContentPadding = PaddingValues(
top = TangemTheme.dimens.spacing4,
bottom = TangemTheme.dimens.spacing92,
bottom = TangemTheme.dimens.spacing92 + bottomBarHeight,
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
)
@ -140,7 +149,7 @@ private fun TokenList(
}
}
BottomGradient(modifier = Modifier.align(Alignment.BottomCenter))
BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
}
}
@ -191,23 +200,6 @@ private fun LazyItemScope.DraggableItem(
}
}
@Composable
private fun BottomGradient(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size116)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
TangemTheme.colors.background.secondary,
),
),
),
)
}
@Composable
private fun TopBar(
config: OrganizeTokensState.HeaderConfig,
@ -228,6 +220,7 @@ private fun TopBar(
modifier = modifier
.shadow(elevation)
.background(TangemTheme.colors.background.secondary)
.statusBarsPadding()
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
) {

View file

@ -1,9 +1,7 @@
package com.tangem.feature.wallet.presentation.router
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
@ -86,7 +84,6 @@ internal class DefaultWalletRouter(
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
OrganizeTokensScreen(
modifier = Modifier.statusBarsPadding(),
state = uiState,
)
}

View file

@ -20,6 +20,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@ -33,16 +34,13 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.paging.compose.collectAsLazyPagingItems
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.ui.components.Keyboard
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
import com.tangem.core.ui.components.snackbar.TangemSnackbar
import com.tangem.core.ui.components.transactions.state.TxHistoryState
@ -50,6 +48,7 @@ import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TestTags
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
@ -112,7 +111,7 @@ internal fun WalletScreen(
)
}
@Suppress("LongMethod", "LongParameterList")
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
@Composable
private fun WalletContent(
state: WalletScreenState,
@ -144,12 +143,14 @@ private fun WalletContent(
.padding(top = betweenItemsPadding)
.padding(horizontal = horizontalPadding)
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
LazyColumn(
modifier = Modifier
.fillMaxSize()
.testTag(TestTags.WALLET_SCREEN),
contentPadding = PaddingValues(
bottom = TangemTheme.dimens.spacing92,
bottom = TangemTheme.dimens.spacing92 + bottomBarHeight,
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@ -330,7 +331,7 @@ private fun BaseScaffoldManageTokenRedesign(
coroutineScope.launch { bottomSheetState.partialExpand() }
}
},
content = { paddingValues ->
content = { _ ->
val pullRefreshState = rememberPullRefreshState(
refreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
onRefresh = {
@ -338,9 +339,7 @@ private fun BaseScaffoldManageTokenRedesign(
},
)
Column(
modifier = Modifier.padding(paddingValues),
) {
Column {
WalletTopBar(config = state.topBarConfig)
Box(
modifier = Modifier.pullRefresh(pullRefreshState),
@ -392,7 +391,7 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: (
}
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("CyclomaticComplexMethod")
@Suppress("CyclomaticComplexMethod", "MagicNumber")
@Composable
private fun BottomSheetStateEffects(
bottomSheetState: SheetState,
@ -425,19 +424,29 @@ private fun BottomSheetStateEffects(
val systemUiController = rememberSystemUiController()
val navigationBarColor = TangemTheme.colors.background.primary
val navigationBarColorWithout = TangemTheme.colors.background.secondary
SystemBarsEffect {
if (showManageTokensBottomSheet) {
setNavigationBarColor(navigationBarColor)
LaunchedEffect(key1 = bottomSheetState.targetValue, navigationBarColor) {
when (bottomSheetState.targetValue) {
SheetValue.Hidden,
SheetValue.Expanded,
-> systemUiController.setNavigationBarColor(
color = Color.Transparent,
darkIcons = navigationBarColor.luminance() > 0.5f,
navigationBarContrastEnforced = true,
)
SheetValue.PartiallyExpanded,
-> systemUiController.setNavigationBarColor(navigationBarColor)
}
}
DisposableEffect(
showManageTokensBottomSheet,
) {
DisposableEffect(showManageTokensBottomSheet) {
onDispose {
if (showManageTokensBottomSheet) {
systemUiController.setNavigationBarColor(navigationBarColorWithout)
systemUiController.setNavigationBarColor(
color = Color.Transparent,
darkIcons = navigationBarColor.luminance() > 0.5f,
navigationBarContrastEnforced = false,
)
}
}
}
@ -506,6 +515,7 @@ private fun BaseScaffold(
) {
Scaffold(
topBar = { WalletTopBar(config = state.topBarConfig) },
contentWindowInsets = WindowInsetsZero,
snackbarHost = {
WalletSnackbarHost(
snackbarHostState = snackbarHostState,
@ -520,7 +530,12 @@ private fun BaseScaffold(
)
}
manageTokensButtonConfig?.let { ManageTokensButton(onClick = it.onClick) }
manageTokensButtonConfig?.let {
ManageTokensButton(
modifier = Modifier.navigationBarsPadding(),
onClick = it.onClick,
)
}
},
floatingActionButtonPosition = FabPosition.Center,
containerColor = TangemTheme.colors.background.secondary,
@ -544,6 +559,8 @@ private fun BaseScaffold(
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter),
)
BottomFade(Modifier.align(Alignment.BottomCenter))
}
},
)
@ -565,11 +582,11 @@ private fun WalletSnackbarHost(
}
@Composable
private fun ManageTokensButton(onClick: () -> Unit) {
private fun ManageTokensButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
PrimaryButton(
text = stringResource(id = R.string.main_manage_tokens),
onClick = onClick,
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing16),
)