Skip to content

System Host

petterp edited this page Aug 30, 2026 · 2 revisions

System window (SystemHost)

SystemHost, from floatingx-system: a real WindowManager window that can be shown outside your app. This page covers the three permission strategies and downgrading, LayoutParams customisation, keyboard and back key, and installing from a Service.

What it is

SystemHost attaches the container to the WindowManager as a standalone system window, so it can sit on top of other apps. It only needs an application context and does not depend on an Activity, which means it can be installed from a Service (#192).

The module's manifest already declares the SYSTEM_ALERT_WINDOW permission and the transparent permission-request Activity — you don't have to configure anything.

Full example

FloatingX.install("sys") {
    layout(R.layout.fx_input)
    anchor(FxGravity.TOP_START, dx = 24f, dy = 200f)
    adsorb(FxAdsorb.Edges(setOf(FxEdge.START, FxEdge.END)))
    systemHost(app) {
        permission(FxPermissionStrategy.auto())          // default: request automatically
        // fall back to an app-level window when permission is denied (2.x's SYSTEM_AUTO)
        fallback(AppHost.builder(app).build())
        layoutParams { it.alpha = 0.9f }                 // runs after the defaults, may override any field
        keyboard(R.id.etInput)                           // touching these EditTexts makes the window focusable
        onBackPressed { true }                           // only delivered while the keyboard is up
    }
}.show()

The three permission strategies

Strategy Behaviour
FxPermissionStrategy.auto() Default. Requests via a transparent Activity; on denial falls back to fallback (stays INSTALLED if none)
FxPermissionStrategy.manual { request -> … } You decide: request.proceed() to request / useFallback() to downgrade / deny() to give up
FxPermissionStrategy.skip() Attach the window without checking (you requested it yourself, or the type needs no permission)

Of the three methods on the FxPermissionRequest handed to manual, call exactly one:

systemHost(app) {
    permission(
        FxPermissionStrategy.manual { request ->
            when {
                userAgreed -> request.proceed()       // open the system settings page
                canDowngrade -> request.useFallback() // downgrade straight to the fallback host
                else -> request.deny()                // give up: no downgrade, stays INSTALLED
            }
        },
    )
    fallback(AppHost.builder(app).build())
}

Once permission is granted after a denial, call retryPermission() to recover:

(control.host as? SystemHost)?.retryPermission()

Downgrading after a denial

With fallback(...) configured, a denial triggers a requestSwap to that host (usually an AppHost): the container is swapped, but the config, listeners, features and current position are all kept — this is 2.x's SYSTEM_AUTO. Without a fallback it stays INSTALLED; call retryPermission() once you have the permission.

LayoutParams customisation

systemHost(app) {
    layoutParams { lp ->
        lp.alpha = 0.9f
        lp.flags = lp.flags or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
    }
}

The customizer runs after the default LayoutParams, so it may override any field (type / flags / alpha …). SystemLayoutParamsCustomizer is a fun interface, so Java can pass a lambda.

Keyboard and back key

System windows carry FLAG_NOT_FOCUSABLE by default (they must not steal focus, or they would block input in the app below). Register the EditText ids inside your content with keyboard(...), and the window becomes temporarily focusable when one of them is touched, bringing up the soft keyboard:

systemHost(app) {
    keyboard(R.id.etInput)          // vararg: register as many EditText ids as you need
    onBackPressed {                 // only delivered while the window is focusable (keyboard up)
        true                        // return true to consume it
    }
}

The back press that dismisses the keyboard is swallowed by the system and never reaches onBackPressed.

Theme

Just like the app-level window, the content is created with an application context, and Material components need a Material theme:

systemHost(app) { theme(R.style.Theme_App) }

Installing from a Service

SystemHost only needs an application context, so installing from a foreground Service is a fully supported use case (#192):

class DemoService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startAsForeground()   // on API 34+ a foreground Service must declare a foregroundServiceType matching the manifest
        FloatingX.install("sys") { /**/ systemHost(application) { } }.show()
        return START_NOT_STICKY
    }
}

Two system restrictions to keep in mind:

  • Since Android 10 (Q) the background cannot start Activities: while the app is in the background, the request page of the auto() strategy may show nothing at all. For genuine background scenarios use manual {} / skip() to defer the request and call retryPermission() once you are back in the foreground.
  • Since Android 13 (T) notifications need user consent: without it startForeground still succeeds, the notification just isn't displayed.

The window's lifetime belongs to the FloatingX registry (by tag) and does not follow the Service — it stays alive when the Service is destroyed. Call control.cancel() or FloatingX.uninstall(tag) yourself when you want it gone.

Java builder

SystemHost host = SystemHost.builder(app)
        .layoutParams(lp -> lp.alpha = 0.9f)
        .permission(FxPermissionStrategy.auto())
        .fallback(AppHost.builder(app).build())
        .build();
FxControl control = FloatingX.install("java-system", config, host);
control.show();

Known limitation: no safe area below Android 11

WindowManager.getCurrentWindowMetrics(), the only public entry point for screen-level insets, was added in API 30 (R). Below R, SystemHost cannot obtain a safe area, so safeArea has no effect (the usable area is the whole screen). Reserve the space yourself with margin(top = …) if you need to avoid the status bar or cutout on older versions. More on FAQ.


Back to Home · Related: App Host · Configuration · FAQ

Clone this wiki locally