Maverlck
Maverlck

Reputation: 11

No Recomposing in Floating ComposeView

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

Answers (0)

Related Questions