@ -20,6 +20,12 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
@ -95,8 +101,28 @@ class FloatingUIActivity : ComponentActivity() {
}
private fun setupTransparentOverlay ( ) {
// Make the activity transparent
// Make the activity transparent and fullscreen
window . apply {
// Hide system UI (navigation bar, status bar)
if ( android . os . Build . VERSION . SDK_INT >= android . os . Build . VERSION_CODES . R ) {
setDecorFitsSystemWindows ( false )
insetsController ?. let { controller ->
controller . hide ( android . view . WindowInsets . Type . systemBars ( ) )
controller . systemBarsBehavior = android . view . WindowInsetsController . BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
@Suppress ( " DEPRECATION " )
decorView . systemUiVisibility = (
android . view . View . SYSTEM_UI_FLAG_LAYOUT_STABLE
or android . view . View . SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or android . view . View . SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or android . view . View . SYSTEM_UI_FLAG_HIDE_NAVIGATION
or android . view . View . SYSTEM_UI_FLAG_FULLSCREEN
or android . view . View . SYSTEM_UI_FLAG_IMMERSIVE_STICKY
)
}
// Make it show over other apps
setFlags (
WindowManager . LayoutParams . FLAG_NOT_TOUCH_MODAL ,
WindowManager . LayoutParams . FLAG_NOT_TOUCH_MODAL
@ -106,7 +132,6 @@ class FloatingUIActivity : ComponentActivity() {
WindowManager . LayoutParams . FLAG_WATCH_OUTSIDE_TOUCH
)
// Make it show over other apps
if ( android . os . Build . VERSION . SDK_INT >= android . os . Build . VERSION_CODES . O ) {
setType ( WindowManager . LayoutParams . TYPE_APPLICATION_OVERLAY )
} else {
@ -150,20 +175,36 @@ fun FloatingFABInterface(
var isProcessing by remember { mutableStateOf ( false ) }
val hapticFeedback = LocalHapticFeedback . current
// Draggable position state
var fabOffset by remember { mutableStateOf ( Offset . Zero ) }
val configuration = LocalConfiguration . current
val density = LocalDensity . current
// Calculate screen bounds for FAB positioning
val screenWidth = with ( density ) { configuration . screenWidthDp . dp . toPx ( ) }
val screenHeight = with ( density ) { configuration . screenHeightDp . dp . toPx ( ) }
val fabSize = with ( density ) { 56. dp . toPx ( ) }
Box (
modifier = Modifier
. fillMaxSize ( )
. background ( Color . Transparent ) ,
contentAlignment = Alignment . BottomEnd
. background ( Color . Transparent )
) {
// Main content area - transparent to allow touches through
Spacer ( modifier = Modifier . fillMaxSize ( ) )
// FAB and menu area
// FAB and menu area - positioned with offset
Column (
horizontalAlignment = Alignment . End ,
verticalArrangement = Arrangement . Bottom ,
modifier = Modifier . padding ( 16. dp )
modifier = Modifier
. offset {
IntOffset (
fabOffset . x . toInt ( ) ,
fabOffset . y . toInt ( )
)
}
. padding ( 16. dp )
) {
// Expanded Menu Items
@ -213,7 +254,7 @@ fun FloatingFABInterface(
)
MenuFABItem (
icon = Icons . Default . RadioButtonUnchecked ,
icon = Icons . Default . AccountCircle ,
label = " POKEBALL " ,
containerColor = MaterialTheme . colorScheme . error ,
onClick = {
@ -263,6 +304,19 @@ fun FloatingFABInterface(
onLongClick = {
hapticFeedback . performHapticFeedback ( HapticFeedbackType . LongPress )
onClose ( )
} ,
onDrag = { dragAmount ->
val newOffset = Offset (
( fabOffset . x + dragAmount . x ) . coerceIn (
- screenWidth + fabSize ,
0f
) ,
( fabOffset . y + dragAmount . y ) . coerceIn (
- screenHeight + fabSize ,
0f
)
)
fabOffset = newOffset
}
)
}
@ -274,7 +328,8 @@ fun MainFloatingActionButton(
isProcessing : Boolean ,
isMenuExpanded : Boolean ,
onClick : ( ) -> Unit ,
onLongClick : ( ) -> Unit
onLongClick : ( ) -> Unit ,
onDrag : ( Offset ) -> Unit
) {
val rotation by animateFloatAsState (
targetValue = if ( isMenuExpanded ) 45f else 0f ,
@ -295,7 +350,20 @@ fun MainFloatingActionButton(
onClick = onClick ,
modifier = Modifier
. size ( 56. dp )
. rotate ( if ( isProcessing ) processingRotation else rotation ) ,
. rotate ( if ( isProcessing ) processingRotation else rotation )
. pointerInput ( Unit ) {
detectDragGestures (
onDragStart = {
// Provide haptic feedback when drag starts
} ,
onDragEnd = {
// Optional: snap to screen edges
} ,
onDrag = { _ , dragAmount ->
onDrag ( dragAmount )
}
)
} ,
containerColor = when {
isProcessing -> MaterialTheme . colorScheme . tertiary
isMenuExpanded -> MaterialTheme . colorScheme . error
@ -310,7 +378,7 @@ fun MainFloatingActionButton(
imageVector = when {
isProcessing -> Icons . Default . Refresh
isMenuExpanded -> Icons . Default . Close
else -> Icons . Default . Camera
else -> Icons . Default . Home
} ,
contentDescription = when {
isProcessing -> " Processing... "