I have a BottomSheet non-Modal with a WebView in Compose using an AndroidView.
The WebView shows the Google Map Site and I use parameters to show a Location that I obtain using FusedLocationProviderClient and others.
I wish to disable user interactions with the WebView and I read here there were two suggestions: Add an Overlay or override Touch events. But that is a really old thread and I don't know how to do any of the suggestions with Compose inside an AndroidView.
The following is my code:
BottomSheet:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocationScreen(startLocationUpdates: ()->Unit, context: Context, currentLocation: LatLng) {
val permissions = arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
val launchMultiplePermissions = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissionMaps ->
val areGranted = permissionMaps.values.reduce { acc, next -> acc && next }
if (areGranted) {
locationRequired = true
startLocationUpdates()
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
var readable by remember { mutableStateOf("") }
//val scope = rememberCoroutineScope()
//I HAVE THIS COMMENTED BECAUSE THE BUTTON THAT IS USUALLY LOCATED IN THE BOTTOMSHEET TO CLOSE IT IS COVER BY THE WEBVIEW AND CAN'T BE USED
val scaffoldState = rememberBottomSheetScaffoldState()
BottomSheetScaffold(
scaffoldState = scaffoldState,
sheetPeekHeight = 590.dp, //The Bottom Sheet will show the WebView partially but all that is needed
sheetSwipeEnabled = false, //For the moment I don't want the Bottom Sheet to occupy the full screen
sheetContent = {
Column(
Modifier
.fillMaxWidth()
.padding(6.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble() && readable != "") {
WebViewScreen(location = currentLocation) //HERE IS THE CALL TO THE ANDROIDVIEW/WEBVIEW
} else {Text(text = "Please click [Get your location]")}
}
}) { innerPadding ->
Box(Modifier.padding(innerPadding)) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Your location: ${currentLocation.latitude}/${currentLocation.longitude}")
if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble()) {
Text(
text = "Readable location: $currentLocation"
)
location = ""
readable = getReadableLocation(currentLocation.latitude, currentLocation.longitude, context) //HERE I USE A GEOCODER TO GET THE ADDRESS
glocation = currentLocation
}
Button(onClick = {
if (permissions.all {
ContextCompat.checkSelfPermission(
context,
it
) == PackageManager.PERMISSION_GRANTED
}) {
//Get Locations
startLocationUpdates() //THIS IS WHERE I GET THE LOCATION/COORDINATES
} else {
launchMultiplePermissions.launch(permissions)
}
//run = true
}) {
Text(text = "Get your location")
}
}
}
}
}
AndroidView with WebView:
@Composable
fun WebViewScreen(location: LatLng){
//val urladdress = URLDecoder.decode(address,"utf-8")
val url = "http://maps.google.com/maps?z=16&t=h&q=loc:${location.latitude}+${location.longitude}&hl=${Locale.getDefault().language}"
AndroidView(
factory = { context ->
WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
settings.javaScriptEnabled = true
webViewClient = WebViewClient()
settings.loadWithOverviewMode = true
settings.useWideViewPort = true
settings.setSupportZoom(true)
loadUrl(url)
}
},
update = { webView ->
webView.loadUrl(url)
}
)
}
I am not providing the code for the Location nor the Address as is not needed for the purpose of this question.
Found an alternative approach to disable user interaction with the WebView.
Instead of adding an Overlay I follow the Touch event approach.
The following is the code:
AndroidView(
modifier = Modifier
//.focusProperties { canFocus = false }
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {}
MotionEvent.ACTION_MOVE -> {}
MotionEvent.ACTION_UP -> {}
else -> false
}
true
},
factory = { context ->
WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
settings.javaScriptEnabled = true
webViewClient = WebViewClient()
settings.loadWithOverviewMode = true
settings.useWideViewPort = true
settings.setSupportZoom(true)
loadUrl(url)
}
},
update = { webView ->
webView.loadUrl(url)
}
)
The "pointerInteropFilter" capture all movement in the parent AndroidView blocking all interaction in the children WebView. If for instance you want to permit Touch events in a particular are you can detect the coordinates inside AndroidView and permit the ones corresponding to the area that falls over the children you require to responde to events.