SetupWizard: hide + lock system UI during setup
Decouple the setup UI hiding from the managed kiosk lockdown: - New WizardFlags.IMMERSIVE (default true): hide the status/nav bars during setup (sticky immersive) independent of KIOSK. KIOSK stays the aggressive managed lockdown (overlay window + non-sticky + future lock-task). - BaseKioskActivity / MainActivity hide the bars when IMMERSIVE || KIOSK. - Add StatusBarManager.setDisabledForSetup(true) on wizard start (EntryPoint + MainActivity) and setDisabledForSetup(false) in finishSetupWizard, so the shade/quick-settings/Home/Recents are actually locked during setup (matches LineageOS). Called via reflection so the module still builds under Gradle (the API is @SystemApi).
This commit is contained in:
@@ -15,6 +15,7 @@ import me.pawlet.setupwizard.lib.BaseKioskActivity
|
||||
import me.pawlet.setupwizard.lib.SetupWizardManager
|
||||
import me.pawlet.setupwizard.lib.WizardFlags
|
||||
import me.pawlet.setupwizard.lib.buildBuiltinPages
|
||||
import me.pawlet.setupwizard.lib.utils.SetupUtils
|
||||
import me.pawlet.setupwizard.ui.screens.AboutDeviceScreen
|
||||
import me.pawlet.setupwizard.ui.screens.AndroidVersionScreen
|
||||
import me.pawlet.setupwizard.ui.theme.MainTheme
|
||||
@@ -23,12 +24,20 @@ class MainActivity : BaseKioskActivity() {
|
||||
private lateinit var wizardManager: SetupWizardManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Kiosk needs the overlay window type set before setContent; the actual
|
||||
// bar-hiding (immersive or kiosk) is applied in onResume via
|
||||
// BaseKioskActivity for both KIOSK and IMMERSIVE.
|
||||
if (WizardFlags.KIOSK) {
|
||||
FullScreenHelper.prepareKioskWindow(this)
|
||||
FullScreenHelper.enableKioskMode(this)
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Lock the status bar (shade/home/recents) for the duration of setup;
|
||||
// restored in SetupUtils.finishSetupWizard().
|
||||
if (WizardFlags.KIOSK || WizardFlags.IMMERSIVE) {
|
||||
SetupUtils().disableStatusBarForSetup(this)
|
||||
}
|
||||
|
||||
wizardManager = SetupWizardManager.getInstance(this)
|
||||
wizardManager.registerPages(
|
||||
buildBuiltinPages(
|
||||
@@ -46,7 +55,10 @@ class MainActivity : BaseKioskActivity() {
|
||||
val deviceInfo by wizardManager.deviceInfo.collectAsState()
|
||||
|
||||
LaunchedEffect(index) {
|
||||
if (WizardFlags.KIOSK) FullScreenHelper.enableKioskMode(this@MainActivity)
|
||||
// Re-assert hidden system bars when the page changes.
|
||||
if (WizardFlags.KIOSK || WizardFlags.IMMERSIVE) {
|
||||
FullScreenHelper.onResume(this@MainActivity, WizardFlags.KIOSK)
|
||||
}
|
||||
}
|
||||
|
||||
when (overlay) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import me.pawlet.setupwizard.lib.SetupWizardManager
|
||||
import me.pawlet.setupwizard.lib.WizardFlags
|
||||
import me.pawlet.setupwizard.lib.internal.PrefManager
|
||||
import me.pawlet.setupwizard.lib.internal.UriHandler
|
||||
import me.pawlet.setupwizard.lib.utils.SetupUtils
|
||||
import me.pawlet.setupwizard.ui.screens.SplashScreen
|
||||
import me.pawlet.setupwizard.ui.theme.MainTheme
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -41,6 +42,11 @@ class EntryPoint : BaseKioskActivity() {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Lock the status bar for setup as early as the splash.
|
||||
if (WizardFlags.KIOSK || WizardFlags.IMMERSIVE) {
|
||||
SetupUtils().disableStatusBarForSetup(this)
|
||||
}
|
||||
|
||||
// Initialize managers
|
||||
prefs = PrefManager(this)
|
||||
uriHandler = UriHandler(this)
|
||||
|
||||
@@ -6,14 +6,18 @@ import me.pawlet.setupwizard.lib.FullScreenHelper
|
||||
open class BaseKioskActivity : ComponentActivity() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// When kiosk is off we intentionally do NOT touch FullScreenHelper:
|
||||
// onResume(_, false) still applies immersive mode (hides the bars), so
|
||||
// the only way to show normal system bars is to skip the call entirely.
|
||||
if (WizardFlags.KIOSK) FullScreenHelper.onResume(this, true)
|
||||
// Hide the system bars during setup (immersive), or full kiosk when
|
||||
// enabled. onResume(_, isKioskMode) hides the bars either way; the bool
|
||||
// only picks sticky-immersive vs non-sticky kiosk style.
|
||||
if (WizardFlags.KIOSK || WizardFlags.IMMERSIVE) {
|
||||
FullScreenHelper.onResume(this, WizardFlags.KIOSK)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (WizardFlags.KIOSK) FullScreenHelper.onWindowFocusChanged(this, hasFocus, true)
|
||||
if (WizardFlags.KIOSK || WizardFlags.IMMERSIVE) {
|
||||
FullScreenHelper.onWindowFocusChanged(this, hasFocus, WizardFlags.KIOSK)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,19 @@ package me.pawlet.setupwizard.lib
|
||||
*/
|
||||
object WizardFlags {
|
||||
/**
|
||||
* Kiosk / immersive lock-down (hides system bars, re-asserts on focus).
|
||||
* Intended for managed/enrolled devices; disabled for the normal first-run
|
||||
* experience so users can use the status bar and back gesture.
|
||||
* Kiosk lock-down: overlay window type + non-sticky immersive + (future)
|
||||
* lock-task, so the user cannot leave or reveal the bars. Intended for
|
||||
* managed/enrolled devices. Off for the normal first-run experience.
|
||||
*/
|
||||
const val KIOSK = false
|
||||
|
||||
/**
|
||||
* Hide the system bars (status + navigation) during setup — standard
|
||||
* first-run behaviour, independent of the managed KIOSK lockdown. Uses
|
||||
* sticky immersive when KIOSK is off. Turn off only for debugging.
|
||||
*/
|
||||
const val IMMERSIVE = true
|
||||
|
||||
/**
|
||||
* Deep-link / URI entry points (oxn:// IPC scheme and http(s) VIEW links).
|
||||
* Off for now — the wizard is launched only as the SETUP_WIZARD HOME app.
|
||||
|
||||
@@ -533,6 +533,29 @@ class SetupUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the status bar for setup: disables the notification shade / quick
|
||||
* settings, Home, Recents, and assist so the user cannot leave setup. Uses
|
||||
* the platform's setup API (StatusBarManager.setDisabledForSetup, a
|
||||
* @SystemApi needing STATUS_BAR — which the wizard holds) via reflection so
|
||||
* the module still builds under Gradle, where that API isn't on the SDK.
|
||||
*/
|
||||
fun disableStatusBarForSetup(context: Context) = setStatusBarDisabledForSetup(context, true)
|
||||
|
||||
/** Restore the status bar locked by [disableStatusBarForSetup]. */
|
||||
fun enableStatusBar(context: Context) = setStatusBarDisabledForSetup(context, false)
|
||||
|
||||
private fun setStatusBarDisabledForSetup(context: Context, disabled: Boolean) {
|
||||
try {
|
||||
val sbm = context.getSystemService("statusbar") ?: return
|
||||
Class.forName("android.app.StatusBarManager")
|
||||
.getMethod("setDisabledForSetup", java.lang.Boolean.TYPE)
|
||||
.invoke(sbm, disabled)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "setDisabledForSetup($disabled) failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun finishSetupWizard(context: Context) {
|
||||
if (LOGV) {
|
||||
Log.v(TAG, "finishSetupWizard")
|
||||
@@ -566,6 +589,9 @@ class SetupUtils {
|
||||
WallpaperManager.getInstance(context).forgetLoadedWallpaper()
|
||||
disableHome(context)
|
||||
|
||||
// Restore the status bar that was locked for setup.
|
||||
enableStatusBar(context)
|
||||
|
||||
// Notify partner/system apps (e.g. ConfigProvisioner) that setup is done
|
||||
// so they can apply their own customizations. Guarded by FINISH_SETUP.
|
||||
runCatching {
|
||||
|
||||
Reference in New Issue
Block a user