Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-19 21:11:19 +05:00
parent 5566331bdb
commit 76c5fd4032
16 changed files with 338 additions and 92 deletions

View file

@ -5,8 +5,4 @@ import androidx.fragment.app.Fragment
interface DisclaimerRouter {
fun entryFragment(): Fragment
companion object {
const val IS_TOS_ACCEPTED_KEY = "is_tos_accepted"
}
}

View file

@ -5,10 +5,10 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import com.tangem.common.routing.AppRoute.Disclaimer.Companion.IS_TOS_ACCEPTED_KEY
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.features.disclaimer.api.DisclaimerRouter
import com.tangem.features.disclaimer.impl.presentation.ui.DisclaimerScreen
import com.tangem.features.disclaimer.impl.presentation.viewmodel.DisclaimerViewModel
import dagger.hilt.android.AndroidEntryPoint
@ -26,7 +26,7 @@ internal class DisclaimerFragment : ComposeFragment() {
private val viewModel by viewModels<DisclaimerViewModel>()
private val isTosAccepted: Boolean
get() = arguments?.getBoolean(DisclaimerRouter.IS_TOS_ACCEPTED_KEY) ?: false
get() = arguments?.getBoolean(IS_TOS_ACCEPTED_KEY) ?: false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -4,15 +4,16 @@ import androidx.fragment.app.Fragment
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.features.disclaimer.impl.DisclaimerFragment
import javax.inject.Inject
internal class DefaultDisclaimerRouter(
internal class DefaultDisclaimerRouter @Inject constructor(
private val appRouter: AppRouter,
) : InnerDisclaimerRouter {
override fun entryFragment(): Fragment = DisclaimerFragment.create()
override fun openPushNotificationPermission() {
// todo [REDACTED_JIRA]
appRouter.push(AppRoute.PushNotification)
}
override fun openHome() {

View file

@ -1,6 +1,7 @@
package com.tangem.features.disclaimer.impl.presentation.ui
import android.Manifest
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.os.Build
import android.view.View
@ -9,12 +10,12 @@ import android.webkit.WebView
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
@ -28,6 +29,7 @@ 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
import com.tangem.core.ui.components.buttons.common.TangemButtonColors
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
@ -39,36 +41,37 @@ import com.tangem.features.disclaimer.impl.presentation.state.DummyDisclaimer
@Composable
internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) {
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
val bottomPadding = if (state.isTosAccepted) {
bottomBarHeight + TangemTheme.dimens.size16
} else {
bottomBarHeight + TangemTheme.dimens.size64
}
val backgroundColor = if (state.isTosAccepted) TangemTheme.colors.background.primary else TangemColorPalette.Dark6
val (textColor, iconColor) = if (state.isTosAccepted) {
TangemTheme.colors.text.primary1 to TangemTheme.colors.icon.primary1
} else {
TangemColorPalette.Light4 to TangemColorPalette.Light4
}
Box(
modifier = Modifier
.background(TangemColorPalette.Dark6)
.background(backgroundColor)
.statusBarsPadding(),
) {
Column {
Column(modifier = Modifier.padding(bottom = bottomPadding)) {
AppBarWithAdditionalButtons(
text = resourceReference(R.string.disclaimer_title),
startButton = AdditionalButton(
iconRes = R.drawable.ic_back_24,
onIconClicked = onBackClick,
).takeIf { state.isTosAccepted },
textColor = textColor,
iconColor = iconColor,
)
DisclaimerContent(state.url)
Spacer(
modifier = Modifier
.background(TangemColorPalette.Dark6)
.height(
height = if (!state.isTosAccepted) {
bottomBarHeight
} else {
bottomBarHeight + TangemTheme.dimens.size64
},
),
)
DisclaimerContent(state.url, state.isTosAccepted)
}
if (!state.isTosAccepted) {
BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = TangemColorPalette.Dark6)
BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = backgroundColor)
DisclaimerButton(state.onAccept)
} else {
NavigationBar3ButtonsScrim()
@ -76,10 +79,13 @@ internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) {
}
}
@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun ColumnScope.DisclaimerContent(url: String) {
private fun DisclaimerContent(url: String, isTosAccepted: Boolean) {
val progressState = remember { mutableStateOf(ProgressState.Loading) }
val webClient = remember { DisclaimerWebViewClient(progressState) }
val transparent = Color.Transparent
val backgroundColor = if (isTosAccepted) TangemTheme.colors.background.primary else TangemColorPalette.Dark6
Box(
modifier = Modifier,
) {
@ -90,12 +96,17 @@ private fun ColumnScope.DisclaimerContent(url: String) {
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
setBackgroundColor(TangemColorPalette.Dark6.toArgb())
setBackgroundColor(transparent.toArgb())
settings.allowFileAccess = false
settings.javaScriptEnabled = false
// to inject css style to display only in dark theme
settings.javaScriptEnabled = !isTosAccepted
overScrollMode = View.OVER_SCROLL_NEVER
webViewClient = webClient
clearHistory()
clearFormData()
clearCache(true)
loadUrl(url)
}
},
@ -103,7 +114,11 @@ private fun ColumnScope.DisclaimerContent(url: String) {
when (progressState.value) {
ProgressState.Loading -> {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxSize()
.background(backgroundColor),
) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
modifier = Modifier
@ -112,25 +127,6 @@ private fun ColumnScope.DisclaimerContent(url: String) {
)
}
}
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
}
}
@ -147,6 +143,12 @@ private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) {
PrimaryButton(
text = stringResource(id = R.string.common_accept),
onClick = { onAccept(shouldAskPushPermission) },
colors = TangemButtonColors(
backgroundColor = TangemColorPalette.Light4,
contentColor = TangemColorPalette.Dark6,
disabledBackgroundColor = TangemColorPalette.Light4,
disabledContentColor = TangemColorPalette.Dark6,
),
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()

View file

@ -10,6 +10,22 @@ internal enum class ProgressState {
Error,
}
/**
* Workaround to display web view with ToS only in dark theme
*/
private fun WebView.injectCSS() {
val code = "javascript:(function() {" +
"var node = document.createElement('style');" +
"node.type = 'text/css';" +
" node.innerHTML = 'body, label,th,p,a, td, tr,li,ul,span,table,h1,h2,h3,h4,h5,h6,h7,div,small {" +
" color: #C9C9C9;" +
"background-color: #1E1E1E;" +
" } ';" +
" document.head.appendChild(node);})();"
evaluateJavascript(code, null)
}
internal class DisclaimerWebViewClient(private val progressState: MutableState<ProgressState>) : WebViewClient() {
private var loadingUrl: String? = null
@ -22,20 +38,23 @@ internal class DisclaimerWebViewClient(private val progressState: MutableState<P
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
view?.injectCSS()
super.onPageStarted(view, url, favicon)
if (loadingUrl != url) progressState.value = ProgressState.Loading
if (loadingUrl != url && progressState.value != ProgressState.Error) progressState.value = ProgressState.Loading
loadingUrl = url
}
override fun onPageFinished(view: WebView?, url: String?) {
view?.injectCSS()
super.onPageFinished(view, url)
if (loadedUrl != url) progressState.value = ProgressState.Done
if (loadedUrl != url && progressState.value != ProgressState.Error) progressState.value = ProgressState.Done
loadedUrl = url
}
override fun onReceivedError(view: WebView?, resourceRequest: WebResourceRequest?, error: WebResourceError?) {
view?.injectCSS()
super.onReceivedError(view, resourceRequest, error)
error?.let { progressState.value = ProgressState.Error }
}
@ -45,6 +64,7 @@ internal class DisclaimerWebViewClient(private val progressState: MutableState<P
resourceRequest: WebResourceRequest?,
errorResponse: WebResourceResponse?,
) {
view?.injectCSS()
super.onReceivedHttpError(view, resourceRequest, errorResponse)
if (resourceRequest != null && errorResponse != null) {

View file

@ -4,8 +4,8 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.common.routing.AppRoute.Disclaimer.Companion.IS_TOS_ACCEPTED_KEY
import com.tangem.domain.card.repository.CardRepository
import com.tangem.features.disclaimer.api.DisclaimerRouter.Companion.IS_TOS_ACCEPTED_KEY
import com.tangem.features.disclaimer.impl.navigation.DefaultDisclaimerRouter
import com.tangem.features.disclaimer.impl.presentation.state.DisclaimerState
import dagger.hilt.android.lifecycle.HiltViewModel
@ -31,8 +31,7 @@ internal class DisclaimerViewModel @Inject constructor(
override fun onAccept(shouldAskPushPermission: Boolean) {
viewModelScope.launch {
cardRepository.acceptTangemTOS()
// todo [REDACTED_JIRA]
disclaimerRouter.openHome()
disclaimerRouter.openPushNotificationPermission()
}
}