Updated on 2026-08-14
This commit is contained in:
parent
5566331bdb
commit
76c5fd4032
16 changed files with 338 additions and 92 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue