I want to use Jetpack Compose to implement the floting window UI. But I got this error:java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView
Here are my floating window service code:
class FloatingService : Service() {
private lateinit var windowManager: WindowManager
private lateinit var contentView: View
private lateinit var layoutParams: WindowManager.LayoutParams
// init windowManager, contentView, layoutParams
override fun onCreate() {
super.onCreate()
windowManager = getSystemService<WindowManager>()!!
contentView = ComposeView(this).apply {
setContent {
Text(text = "Hello World")
}
}
layoutParams = WindowManager.LayoutParams().apply {
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
WindowManager.LayoutParams.TYPE_PHONE
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
x = 0
y = 200
format = PixelFormat.RGBA_8888 // give window transparent background
gravity = Gravity.TOP or Gravity.END // layout right
}
}
// add contentView to windowManager
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
windowManager.addView(
contentView,
layoutParams
)
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
windowManager.removeView(contentView)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
I am confirm it can work good when you init contentView
by using Button(this)
. What's more, I have tried using LifecycleService
which in implementation("androidx.lifecycle:lifecycle-service:2.5.1")
and init ViewTreeLifecycleOwner
by using ViewTreeLifecycleOwner.set(contentView, this)
, the result is that I got another error: java.lang.IllegalStateException: Composed into the View which doesn't propagateViewTreeSavedStateRegistryOwner!
.
OK. Here is my own solution
implementation("androidx.lifecycle:lifecycle-service:2.5.1")
import android.content.Intent
import android.view.View
import androidx.compose.material3.Text
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.LifecycleService
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
// you need extends LifecycleService and implement SavedStateRegistryOwner.
class YourService() : LifecycleService(), SavedStateRegistryOwner {
// create a SavedStateRegistryController to get SavedStateRegistry object.
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private lateinit var contentView: View
override fun onCreate() {
super.onCreate()
// init your SavedStateRegistryController
savedStateRegistryController.performAttach() // you can ignore this line, becase performRestore method will auto call performAttach() first.
savedStateRegistryController.performRestore(null)
// configure your ComposeView
contentView = ComposeView(this).apply {
setViewTreeSavedStateRegistryOwner(this@YourService)
setContent {
Text(text = "Hello World")
}
}
ViewTreeLifecycleOwner.set(contentView, this)
// init your WindowManager and LayoutParams
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
// add your contentView to windowManager
}
override fun onDestroy() {
super.onDestroy()
// remove your view from your windowManager
}
// override savedStateRegistry property from SavedStateRegistryOwner interface.
override val savedStateRegistry: SavedStateRegistry
get() = savedStateRegistryController.savedStateRegistry
}
android.permission.SYSTEM_ALERT_WINDOW
permission.