Back to Home

DreamService and Compose in Android 4.2+

DreamService allows creating UI in Android sleep mode while charging. Integrating Jetpack Compose requires implementing LifecycleOwner and SavedStateRegistryOwner. Step-by-step guide with code for middle/senior developers.

Compose in DreamService: UI for sleeping Android
Advertisement 728x90

DreamService in Android: Integrating Jetpack Compose for Dream Screens

DreamService, introduced in Android 4.2, allows displaying custom UI on the device while idle during charging. The component inherits from Service and supports interactive elements, including buttons. Activation requires manual user configuration in display settings. This solution is suitable for creating dynamic screens without affecting the main app lifecycle.

Declaring the service in AndroidManifest.xml is standard:

<service
    android:name=".MyDreamService"
    android:exported="true"
    android:description="@string/dream_service_description"
    android:icon="@drawable/dream_service_icon"
    android:label="@string/dream_service_label"
    android:permission="android.permission.BIND_DREAM_SERVICE">

    <intent-filter>
        <action android:name="android.service.dreams.DreamService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

The basic implementation uses onAttachedToWindow() to configure the view:

Google AdInline article slot
class MyDreamService : DreamService() {

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        isInteractive = false
        isFullscreen = true
        setContentView(R.layout.dream_service_layout)
    }
}

Integrating Jetpack Compose into DreamService

The setContentView method works only with Views, so a ComposeView is created for Compose. However, DreamService does not provide ViewTreeLifecycleOwner and SavedStateRegistryOwner, which are critical for Compose to function correctly.

The solution is to extend DreamService with an implementation of SavedStateRegistryOwner:

class MyDreamService : DreamService(), SavedStateRegistryOwner {

    private val lifecycleRegistry = LifecycleRegistry(this)
    private val savedStateRegistryController = SavedStateRegistryController.create(this)

    override val lifecycle: Lifecycle
        get() = lifecycleRegistry

    override val savedStateRegistry: SavedStateRegistry
        get() = savedStateRegistryController.savedStateRegistry
}

In onAttachedToWindow(), synchronize the lifecycle and registry:

Google AdInline article slot
val composeView = ComposeView(this).apply {
    setViewTreeLifecycleOwner(this@MyDreamService)
    setViewTreeSavedStateRegistryOwner(this@MyDreamService)
    setContent {
        DreamServiceTheme {
            DreamScreen(
                onOpenSettings = { 
                    openSettingsAndExitDream() 
                }
            )
        }
    }
}

setContentView(composeView)

Don't forget to call lifecycle methods in DreamService callbacks (onAttachedToWindow, onDetachedFromWindow, etc.) for proper state synchronization.

Key Limitations and Best Practices

  • Activation: Users must manually enable Daydream in settings. Automatic launch is impossible.
  • Lifecycle: Ensure full implementation of LifecycleOwner for Compose, otherwise the UI will not update.
  • Performance: Avoid heavy animations — the device is in sleep mode.
  • Interactivity: isInteractive = true allows buttons, but requires caution regarding power consumption.

List of implementation steps:

  • Declare the service in the manifest with BIND_DREAM_SERVICE permission.
  • Inherit from DreamService and add SavedStateRegistryOwner.
  • Initialize LifecycleRegistry and SavedStateRegistryController.
  • In ComposeView, set owners before setContent.
  • Test on a real device in charging mode.

Key Takeaways

  • DreamService is a full-fledged UI hoster besides Activity, supporting Compose after lifecycle adjustments.
  • Manual activation in display settings is mandatory.
  • Resolve ViewTreeLifecycleOwner issues by extending the service.
  • Suitable for idle screens: clocks, notifications, mini-games.
  • Power saving is critical — minimize draw calls.

This feature expands Android development capabilities, allowing customization of device behavior in non-standard scenarios. For senior developers, it is useful in the context of deep understanding of platform services and their integration with modern UI frameworks.

Google AdInline article slot

— Editorial Team

Advertisement 728x90

Read Next