Ref(DeviceInfo-PaymentResult-WebView):

- Refactor `PaymentResultEntity` to include a nested `PaymentResultDataEntity` and companion factory methods for success and failure.
- Update `PaymentResultStatus` enum members from `OK`/`ERROR` to `SUCCESS`/`FAILURE`.
- Modify `PspService` and its implementations (`P3`, `PS4`, `Stage`) to return a non-nullable `PaymentResultEntity`.
- Enhance `PspWebView` with improved instance lifecycle management, `rememberUpdatedState` for callbacks, and new `onWebViewReady` and `onPageFinished` hooks.
- Optimize `PspWebView` to prevent redundant `loadUrl` calls during recomposition.
- Add `README.md` and `AGENT.md` to provide project architecture overviews and hardware-specific development guidelines.
- Update `TisWebViewScreen` and `StageWebViewScreen` to handle the refactored result models and utilize new WebView callbacks.
This commit is contained in:
Amir Mousavi
2026-05-17 13:40:32 +03:30
parent 2b03af6e52
commit 08060001e1
14 changed files with 254 additions and 72 deletions
@@ -46,13 +46,13 @@ fun PspWebView(
val scope = rememberCoroutineScope()
val context = LocalContext.current
var lastBackTime by remember { mutableLongStateOf(0L) }
var webView by remember { mutableStateOf<WebView?>(null) }
var webViewInstance by remember { mutableStateOf<WebView?>(null) }
// Use rememberUpdatedState to ensure the bridge always calls the latest callbacks
val currentOnPayClick by rememberUpdatedState(onPayClick)
val currentOnPrintClick by rememberUpdatedState(onPrintClick)
val currentOnDeviceInfo by rememberUpdatedState(onDeviceInfo)
val currentOnWebViewReady by rememberUpdatedState(onWebViewReady)
val currentOnPageFinished by rememberUpdatedState(onPageFinished)
val bridge = remember {
PspJavaScriptInterface(
@@ -63,8 +63,8 @@ fun PspWebView(
}
BackHandler {
if (canGoBack.value) {
webView?.goBack()
if (webViewInstance?.canGoBack() == true) {
webViewInstance?.goBack()
} else {
val now = System.currentTimeMillis()
if (now - lastBackTime < 2000) {
@@ -72,38 +72,34 @@ fun PspWebView(
} else {
lastBackTime = now
scope.launch {
SnackbarController.sendEvent(
SnackbarEvent(
message = "برای خروج، دوباره بر روی بازگشت کلیک کنید."
)
)
SnackbarController.sendEvent(SnackbarEvent("برای خروج، دوباره کلیک کنید."))
}
}
}
}
// Manage pooling and lifecycle
DisposableEffect(Unit) {
webView = WebViewManager.getWebView(context)
webView?.let(onWebViewReady)
onDispose {
webView?.let { WebViewManager.releaseWebView(it) }
webViewInstance?.let { WebViewManager.releaseWebView(it) }
}
}
AndroidView(
modifier = modifier,
factory = { ctx ->
(webView ?: WebViewManager.getWebView(ctx)).apply {
val wv = WebViewManager.getWebView(ctx).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
overScrollMode = View.OVER_SCROLL_NEVER
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
canGoBack.value = view?.canGoBack() ?: false
view?.let(onPageFinished)
view?.let(currentOnPageFinished)
}
}
@@ -112,16 +108,21 @@ fun PspWebView(
javaScriptEnabled = true
domStorageEnabled = true
cacheMode = WebSettings.LOAD_NO_CACHE
allowFileAccess = true // Needed for Service Worker
allowFileAccess = true
}
clearCache(true)
removeJavascriptInterface("NativeBridge")
addJavascriptInterface(bridge, "NativeBridge")
}
webViewInstance = wv
currentOnWebViewReady(wv) // CRITICAL: Send the ACTUAL instance being shown
wv
},
update = {
it.loadUrl(url)
if (it.url != url) {
it.loadUrl(url)
}
}
)
}