Reputation: 146
My goal is to have a small UI element (50dp bubble/square) that is draggable and is shown over the system screens and other apps. Yes, like Messenger chat bubbles.
I have successfully set up a ComposeView and used it with WindowManager to display it over other apps. The small view is also draggable.
The problem is I want to be able to drag it anywhere on the screen, but without blocking touch input outside of the square's area. The blocking happens because I use a Box with the modifier fillMaxSize(). If I don't use fillMaxSize(), the square is clipped when dragged outside of the 50dp x 50dp area of its initial position.
This is the Composable
@Composable
fun OverlayScreen(
) {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
modifier = Modifier
.fillMaxSize()
.offset {
IntOffset(offsetX.roundToInt(), offsetY.roundToInt())
}) {
Box(
modifier = Modifier
.height(50.dp)
.width(50.dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
offsetX += dragAmount.x
offsetY += dragAmount.y
}
}
.background(color = Color(100, 9, 100))
) {
Text("Hello Compose!")
}
}
}
And this is the ComposeView:
val bubbleOverlay = ComposeView(context).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
OverlayScreen()
}
}
}
I also launch a foreground service and ask for ACTION_MANAGE_OVERLAY_PERMISSION ( Draw over other apps).
Upvotes: 3
Views: 1027
Reputation: 146
Found the answer.
All you need to do is let Compose handle the dragging and send the drag amount for X and Y out to a function that updates the View's position in the Window.
Set your layout params:
mParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
)
Create a function to update the View's position in the Window:
private fun moveOverlay(x: Int, y: Int) {
mParams.x += x
mParams.y += y
mWindowManager.updateViewLayout(bubbleOverlay, mParams)
}
Pass that function in to Compose
bubbleOverlay = ComposeView(context).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
OverlayScreen(
::moveOverlay
)
}
}
}
And make sure the Composable doesn't fill the whole screen, so don't use fillMaxSize().
Upvotes: 4