Reputation: 11
I have a composable that is not recomposing in a Floating ComposeView but in an Activity, it works. In the Floating, only LazyColumn recomposes its items but outside of that. I've tried with mutableStateOf and MutableLiveData, MutableStateFlow in a ViewModel.
I think something is missing in the FloatingService's LifeCycle
Here is simple testable code: Composable
@Composable
fun Test() {
var count by remember { mutableStateOf(0) }
Log.d("TestComposable", "Recomposing: Count = $count")
Column(
modifier = Modifier.padding(16.dp)
) {
Text("Count: $count")
Button(onClick = {
count++
Log.d("TestComposable", "Button clicked: Count = $count")
}) {
Text("Increment")
}
}
}
Floating Service
class FloatingExecutionService : Service(), LifecycleOwner, ViewModelStoreOwner,
SavedStateRegistryOwner {
private val _lifecycleRegistry: LifecycleRegistry by lazy { LifecycleRegistry(this) }
private val _store by lazy { ViewModelStore() }
private lateinit var windowManager: WindowManager
private lateinit var floatingView: ComposeView
override val lifecycle: Lifecycle
get() = _lifecycleRegistry
override val viewModelStore: ViewModelStore
get() = _store
private val savedStateRegistryController = SavedStateRegistryController.create(this)
override val savedStateRegistry: SavedStateRegistry =
savedStateRegistryController.savedStateRegistry
private fun handleLifecycleEvent(event: Lifecycle.Event) {
Log.d("FloatingExecutionService", "Lifecycle Event: $event")
_lifecycleRegistry.handleLifecycleEvent(event)
}
@CallSuper
override fun onCreate() {
super.onCreate()
savedStateRegistryController.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
floatingView = ComposeView(this).apply {
setViewTreeSavedStateRegistryOwner(this@FloatingExecutionService)
setViewTreeLifecycleOwner(this@FloatingExecutionService)
setViewTreeViewModelStoreOwner(this@FloatingExecutionService)
setContent {
Test()
}
}
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
params.gravity = Gravity.TOP or Gravity.START
params.x = 0
params.y = 0
windowManager.addView(floatingView, params)
floatingView.setOnTouchListener(object : View.OnTouchListener {
private var initialX = 0
private var initialY = 0
private var initialTouchX = 0f
private var initialTouchY = 0f
override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialX = params.x
initialY = params.y
initialTouchX = event.rawX
initialTouchY = event.rawY
return true
}
MotionEvent.ACTION_MOVE -> {
params.x = initialX + (event.rawX - initialTouchX).toInt()
params.y = initialY + (event.rawY - initialTouchY).toInt()
windowManager.updateViewLayout(floatingView, params)
return true
}
}
return false
}
})
}
override fun onDestroy() {
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
super.onDestroy()
if (::floatingView.isInitialized) {
windowManager.removeView(floatingView)
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
@CallSuper
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
}
AndroidManifest.xml
...
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
...
<service
android:name=".service.FloatingExecutionService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.SYSTEM_ALERT_WINDOW" />
Libs
agp = "8.8.0"
kotlin = "2.0.0"
coreKtx = "1.15.0"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.12.01"
gradle-8.11.1-bin.zip
Upvotes: 1
Views: 17