I am creating an Android application with Kotlin and Jetpack Compose, I have a composable screen which shows a Webview.
Screen Instructions
- Webview loads a Mollie Payment URL Which Shows Payment Option from different banks Each option have a deep link which redirects to bank app if the app is installed The payment process from bank app returns a Redirect URL
What is required?
- The Redirect URL from deep link should be opened in my App's webview instead to opening in Web browser (Google Chrome in this case)
This is the pattern of Redirect Url
https://www.mollie.com/checkout/ideal/return/SOMETOKEN?trxid=SOMEVALUE=SOMEVALUE
Below is the code for 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>
Composable Screen Code
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
}
Any help what i am missing here, which is not allowing my app to receive redirect url instead the redirect url is opened in User's web browser.
You have not setup the Deeplink Properly in your app and website as well.
If you don't want your link to open in external web browser you need to put https://yourdomain/.well-known/assetlinks.json with the content below.
Note : You can create above file from this URL : https://developers.google.com/digital-asset-links/tools/generator
You can generate your app's SHA256 fingerprint with this command: