Updated on 2026-08-14
This commit is contained in:
parent
1524ae4ce4
commit
ff0dffd2dc
5 changed files with 55 additions and 84 deletions
|
|
@ -223,7 +223,6 @@ dependencies {
|
||||||
implementation(deps.lottie)
|
implementation(deps.lottie)
|
||||||
implementation(deps.compose.accompanist.appCompatTheme)
|
implementation(deps.compose.accompanist.appCompatTheme)
|
||||||
implementation(deps.compose.accompanist.systemUiController)
|
implementation(deps.compose.accompanist.systemUiController)
|
||||||
implementation(deps.compose.accompanist.webView)
|
|
||||||
implementation(deps.xmlShimmer)
|
implementation(deps.xmlShimmer)
|
||||||
implementation(deps.viewBindingDelegate)
|
implementation(deps.viewBindingDelegate)
|
||||||
implementation(deps.armadillo)
|
implementation(deps.armadillo)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.tangem.core.ui.webview
|
||||||
|
|
||||||
|
import android.webkit.WebSettings
|
||||||
|
import android.webkit.WebView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies set of settings to prevent base security issues
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
fun WebView.applySafeSettings() {
|
||||||
|
settings.apply {
|
||||||
|
// disable to use scripts
|
||||||
|
javaScriptEnabled = false
|
||||||
|
|
||||||
|
// disable access to files
|
||||||
|
allowFileAccess = false
|
||||||
|
|
||||||
|
// disable access to content by system content provider
|
||||||
|
allowContentAccess = false
|
||||||
|
|
||||||
|
// disable to use cached data (scripts, files, etc)
|
||||||
|
cacheMode = WebSettings.LOAD_NO_CACHE
|
||||||
|
|
||||||
|
// disable to use local storage
|
||||||
|
domStorageEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
setDownloadListener(null)
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.features.disclaimer.impl.ui
|
package com.tangem.features.disclaimer.impl.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.webkit.WebView
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -37,29 +36,22 @@ import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.core.ui.test.TestTags.DISCLAIMER_SCREEN_ACCEPT_BUTTON
|
import com.tangem.core.ui.test.TestTags.DISCLAIMER_SCREEN_ACCEPT_BUTTON
|
||||||
import com.tangem.core.ui.test.TestTags.DISCLAIMER_SCREEN_CONTAINER
|
import com.tangem.core.ui.test.TestTags.DISCLAIMER_SCREEN_CONTAINER
|
||||||
|
import com.tangem.core.ui.webview.applySafeSettings
|
||||||
import com.tangem.features.disclaimer.impl.R
|
import com.tangem.features.disclaimer.impl.R
|
||||||
import com.tangem.features.disclaimer.impl.entity.DisclaimerUM
|
import com.tangem.features.disclaimer.impl.entity.DisclaimerUM
|
||||||
import com.tangem.features.disclaimer.impl.entity.DummyDisclaimer
|
import com.tangem.features.disclaimer.impl.entity.DummyDisclaimer
|
||||||
import com.tangem.features.disclaimer.impl.local.localTermsOfServices
|
import com.tangem.features.disclaimer.impl.local.localTermsOfServices
|
||||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DisclaimerScreen(state: DisclaimerUM) {
|
internal fun DisclaimerScreen(state: DisclaimerUM) {
|
||||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||||
val bottomPadding = if (state.isTosAccepted) {
|
val bottomPadding = bottomBarHeight + TangemTheme.dimens.size16
|
||||||
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(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(backgroundColor)
|
.background(TangemTheme.colors.background.primary)
|
||||||
.statusBarsPadding()
|
.statusBarsPadding()
|
||||||
.testTag(DISCLAIMER_SCREEN_CONTAINER),
|
.testTag(DISCLAIMER_SCREEN_CONTAINER),
|
||||||
) {
|
) {
|
||||||
|
|
@ -75,14 +67,15 @@ internal fun DisclaimerScreen(state: DisclaimerUM) {
|
||||||
onIconClicked = state.popBack,
|
onIconClicked = state.popBack,
|
||||||
).takeIf { state.isTosAccepted },
|
).takeIf { state.isTosAccepted },
|
||||||
titleAlignment = Alignment.CenterHorizontally,
|
titleAlignment = Alignment.CenterHorizontally,
|
||||||
textColor = textColor,
|
|
||||||
iconTint = iconColor,
|
|
||||||
)
|
)
|
||||||
DisclaimerContent(state.url, state.isTosAccepted)
|
DisclaimerContent(state.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.isTosAccepted) {
|
if (!state.isTosAccepted) {
|
||||||
BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = backgroundColor)
|
BottomFade(
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
backgroundColor = TangemTheme.colors.background.primary,
|
||||||
|
)
|
||||||
DisclaimerButton(state.onAccept)
|
DisclaimerButton(state.onAccept)
|
||||||
} else {
|
} else {
|
||||||
NavigationBar3ButtonsScrim()
|
NavigationBar3ButtonsScrim()
|
||||||
|
|
@ -90,14 +83,14 @@ internal fun DisclaimerScreen(state: DisclaimerUM) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisclaimerContent(url: String, isTosAccepted: Boolean) {
|
private fun DisclaimerContent(url: String) {
|
||||||
val backgroundColor = if (isTosAccepted) TangemTheme.colors.background.primary else TangemColorPalette.Dark6
|
|
||||||
|
|
||||||
val webViewStateUrl = rememberWebViewState(url)
|
val webViewStateUrl = rememberWebViewState(url)
|
||||||
val webViewStateData =
|
val webViewStateData = rememberWebViewStateWithHTMLData(
|
||||||
rememberWebViewStateWithHTMLData(data = localTermsOfServices, mimeType = "text/html", encoding = "UTF-8")
|
data = localTermsOfServices,
|
||||||
|
mimeType = "text/html",
|
||||||
|
encoding = StandardCharsets.UTF_8.name(),
|
||||||
|
)
|
||||||
|
|
||||||
val webViewState by remember {
|
val webViewState by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
@ -113,12 +106,10 @@ private fun DisclaimerContent(url: String, isTosAccepted: Boolean) {
|
||||||
WebView(
|
WebView(
|
||||||
state = webViewState,
|
state = webViewState,
|
||||||
captureBackPresses = false,
|
captureBackPresses = false,
|
||||||
onCreated = {
|
onCreated = WebView::applySafeSettings,
|
||||||
it.settings.javaScriptEnabled = !isTosAccepted
|
modifier = Modifier
|
||||||
it.setBackgroundColor(backgroundColor.toArgb())
|
.fillMaxSize()
|
||||||
},
|
.background(TangemTheme.colors.background.primary),
|
||||||
client = remember { DisclaimerWebViewClient() },
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
|
|
@ -128,12 +119,12 @@ private fun DisclaimerContent(url: String, isTosAccepted: Boolean) {
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(backgroundColor),
|
.background(TangemTheme.colors.background.primary),
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(backgroundColor),
|
.background(TangemTheme.colors.background.primary),
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
color = TangemTheme.colors.icon.informative,
|
color = TangemTheme.colors.icon.informative,
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package com.tangem.features.disclaimer.impl.ui
|
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.webkit.WebView
|
|
||||||
import com.google.accompanist.web.AccompanistWebViewClient
|
|
||||||
|
|
||||||
internal enum class ProgressState {
|
|
||||||
Loading,
|
|
||||||
Done,
|
|
||||||
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 : AccompanistWebViewClient() {
|
|
||||||
|
|
||||||
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
|
||||||
view?.injectCSS()
|
|
||||||
super.onPageStarted(view, url, favicon)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageCommitVisible(view: WebView?, url: String?) {
|
|
||||||
view?.injectCSS()
|
|
||||||
super.onPageCommitVisible(view, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
|
||||||
view?.injectCSS()
|
|
||||||
super.onPageFinished(view, url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.google.accompanist.web.rememberWebViewState
|
||||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
import com.tangem.core.ui.webview.applySafeSettings
|
||||||
import com.tangem.feature.referral.presentation.R
|
import com.tangem.feature.referral.presentation.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,12 +51,7 @@ private fun AgreementHtmlView(url: String) {
|
||||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||||
captureBackPresses = false,
|
captureBackPresses = false,
|
||||||
onCreated = {
|
onCreated = {
|
||||||
if (!isInPreviewMode) {
|
if (!isInPreviewMode) it.applySafeSettings()
|
||||||
it.settings.apply {
|
|
||||||
javaScriptEnabled = false
|
|
||||||
allowFileAccess = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue