我正在使用 Kotlin 和 Jetpack Compose 创建一个 Android 应用程序,我有一个显示 Web 视图的可组合屏幕。
屏幕说明
需要什么?
这是Redirect Url的模式
https://www.mollie.com/checkout/ideal/return/SOMETOKEN?trxid=SOMEVALUE=SOMEVALUE
下面是AndroidManifest.xml的代码
<activity
android:name=".presentation.feature.dashboard.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="ideal"
android:scheme="intent" />
<data
android:host="x-bunq-app"
android:scheme="bunq" />
<data
android:host="payment"
android:scheme="nl-asnbank-ideal" />
<data
android:host="ideal.ing.nl"
android:scheme="intent" />
<data
android:host="authenticate"
android:scheme="intent" />
<data
android:host="ideal2.knab.nl"
android:scheme="knabapp" />
<data
android:host="payment"
android:scheme="nl-regiobank-ideal" />
<data
android:host="payment"
android:scheme="nl-snsbank-ideal" />
</intent-filter>
</activity>
可组合屏幕代码
val USER_AGENT_MOZILLA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) AppleWebKit/605.1.15 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/605.1.15"
@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun BookingPaymentScreen(
navController: NavController,
addBookingViewModel: AddBookingViewModel,
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val result = navController.previousBackStackEntry?.savedStateHandle?.get<Boolean>("isFromRetry")
val resultUrl = navController.previousBackStackEntry?.savedStateHandle?.get<String>("url")
val errorToastState = remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
activityResult ->
}
)
BackHandler {
goBackBookingPaymentScreen(addBookingViewModel, navController)
}
//First time
if (result == true) {
if (!addBookingViewModel.retryCallMade) {
addBookingViewModel.onEvent(AddBookingUiEvent.onRetryPayment)
addBookingViewModel.retryCallMade = true
}
}
Surface(
modifier = Modifier
.background(
color = PallaAppTheme.colors.primary
)
.fillMaxHeight()
.statusBarsPadding()
) {
if (errorToastState.value) {
Toast.makeText(context, "App not installed", Toast.LENGTH_LONG)
.show()
errorToastState.value = false
}
if (addBookingViewModel.checkoutLoading) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(AppVariables.AppPading)
.background(PallaAppTheme.AppBackground),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = PallaAppTheme.colors.secondary,
strokeWidth = 2.dp
)
}
} else {
AndroidView(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding(),
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
settings.userAgentString = USER_AGENT_MOZILLA
settings.domStorageEnabled = true
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
settings.allowContentAccess = true
settings.loadWithOverviewMode = true
webViewClient = object : WebViewClient() {
override fun onPageCommitVisible(view: WebView?, url: String?) {
super.onPageCommitVisible(view, url)
}
override fun onPageStarted(
view: WebView?,
url: String?,
favicon: Bitmap?
) {
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
}
@Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(
view: WebView?,
url: String?
): Boolean {
if (url != null && url.startsWith(AppDeepLinks.COURT_BOOKING)) {
addBookingViewModel.retryCallMade = false
val bookingDetail = BookingDetail(
bookingId = addBookingViewModel.bookingFinalUiState.bookingId,
ownerToken = addBookingViewModel.bookingFinalUiState.ownerToken,
url = addBookingViewModel.bookingFinalUiState.paymentUrl
)
navController.currentBackStackEntry?.savedStateHandle?.set(
key = "bookingDetail",
value = bookingDetail
)
navController.navigate(BOOKING_DETAIL_ROUTE)
return true
}
if (isDeepLink(url)
) {
try {
val intent = Intent(ACTION_VIEW, Uri.parse(url))
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Only browser apps are available, or a browser is the default.
// So you can open the URL directly in your app, for example in a
errorToastState.value = true
}
return true
}
// return super.shouldOverrideUrlLoading(view, url)
return false
}
}
var url = addBookingViewModel.bookingFinalUiState.paymentUrl
if (url.isEmpty()) {
url = resultUrl ?: ""
}
loadUrl(url)
}
},
update = { webView ->
// Update the WebView if needed
CookieManager.getInstance()?.setAcceptThirdPartyCookies(webView, true)
}
)
}
}
}
fun goBackBookingPaymentScreen(
addBookingViewModel: AddBookingViewModel,
navController: NavController
) {
addBookingViewModel.retryCallMade = false
val bookingDetail = BookingDetail(
bookingId = addBookingViewModel.bookingFinalUiState.bookingId,
ownerToken = addBookingViewModel.bookingFinalUiState.ownerToken
)
navController.currentBackStackEntry?.savedStateHandle?.set(
key = "bookingDetail",
value = bookingDetail
)
navController.navigate(BOOKING_DETAIL_ROUTE)
}
fun isDeepLink(url: String?): Boolean {
if ((url?.startsWith("nl") == true && url.contains("payloadUri"))
) {
return true
}
if (url?.startsWith("http") == false && !url.startsWith("https") && url.contains("://")) {
return true
}
return false
}
我在这里缺少的任何帮助,即不允许我的应用程序接收重定向网址,而是在用户的网络浏览器中打开重定向网址。
您还没有在您的应用程序和网站中正确设置深层链接。
如果您不希望链接在外部网络浏览器中打开,您需要将 https://yourdomain/.well-known/assetlinks.json 与以下内容放在一起。
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "your_com.example",
"sha256_cert_fingerprints":
["YOUR_CERTIFICATE"]
}
}]
注意:您可以从此 URL 创建上述文件: https://developers.google.com/digital-asset-links/tools/generator
您可以使用以下命令生成应用程序的 SHA256 指纹:
keytool -list -v -keystore <keystore path> -alias <key alias> -storepass <store password> -keypass <key password>