From c1ec0ccee32cab77388db23291af1d6320b61095 Mon Sep 17 00:00:00 2001 From: oxmc <67136658+oxmc@users.noreply.github.com> Date: Sat, 18 Jul 2026 07:40:06 -0700 Subject: [PATCH] SetupWizard: OEM-contributed wizard pages from the config APK A config APK can ship res/xml/oem_wizard.xml (declarative Rich schema: section/text/image/toggle/choice/input/link, multi-page). ConfigProvisioner emits Settings.Global pawlet.oem_wizard_pkg when it detects the file; the wizard loads + parses it and appends the pages near the end of the flow. - lib/oem: OemModels, OemWizardParser (pull-parser; text carried in a value attribute since compiled res/xml drops element text), OemWizardRepository (signal + default-package fallback), OemPendingStore (deferred secure/global writes). - OemWizardScreen renders elements to Compose on the shared scaffold; toggle/ choice/input selections are seeded with OEM defaults and recorded, then applied in finishSetupWizard. - MainActivity appends OEM pages; no config-APK code runs (declarative only), link actions limited to explicit component:/intent:. --- .../setupwizard/activities/MainActivity.kt | 27 ++- .../dev/oxmc/setupwizard/lib/oem/OemModels.kt | 60 ++++++ .../setupwizard/lib/oem/OemPendingStore.kt | 81 ++++++++ .../setupwizard/lib/oem/OemWizardParser.kt | 107 ++++++++++ .../lib/oem/OemWizardRepository.kt | 49 +++++ .../oxmc/setupwizard/lib/utils/SetupUtils.kt | 5 +- .../ui/screens/wizard/OemWizardScreen.kt | 196 ++++++++++++++++++ 7 files changed, 518 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemModels.kt create mode 100644 app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemPendingStore.kt create mode 100644 app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemWizardParser.kt create mode 100644 app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemWizardRepository.kt create mode 100644 app/src/main/java/dev/oxmc/setupwizard/ui/screens/wizard/OemWizardScreen.kt diff --git a/app/src/main/java/dev/oxmc/setupwizard/activities/MainActivity.kt b/app/src/main/java/dev/oxmc/setupwizard/activities/MainActivity.kt index 489210d..422e2d1 100644 --- a/app/src/main/java/dev/oxmc/setupwizard/activities/MainActivity.kt +++ b/app/src/main/java/dev/oxmc/setupwizard/activities/MainActivity.kt @@ -14,10 +14,13 @@ import me.pawlet.setupwizard.lib.FullScreenHelper import me.pawlet.setupwizard.lib.BaseKioskActivity import me.pawlet.setupwizard.lib.SetupWizardManager import me.pawlet.setupwizard.lib.WizardFlags +import me.pawlet.setupwizard.lib.WizardPage import me.pawlet.setupwizard.lib.buildBuiltinPages +import me.pawlet.setupwizard.lib.oem.OemWizardRepository 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.screens.wizard.OemWizardScreen import me.pawlet.setupwizard.ui.theme.MainTheme class MainActivity : BaseKioskActivity() { @@ -39,14 +42,26 @@ class MainActivity : BaseKioskActivity() { } wizardManager = SetupWizardManager.getInstance(this) - wizardManager.registerPages( - buildBuiltinPages( - onFinish = { finish() }, - onSecretUnlocked = { - wizardManager.showOverlay(SetupWizardManager.Overlay.AboutDevice) + + val builtinPages = buildBuiltinPages( + onFinish = { finish() }, + onSecretUnlocked = { + wizardManager.showOverlay(SetupWizardManager.Overlay.AboutDevice) + } + ) + // OEM pages contributed by the config APK (ConfigProvisioner emits the + // signal; OemWizardRepository loads + parses the declarative XML). They + // land near the end of the flow, before "complete". + val oemPages = OemWizardRepository.load(this).mapIndexed { i, page -> + WizardPage( + id = "oem_${page.id}", + order = 92 + i, + content = { onNext, onBack -> + OemWizardScreen(page = page, onContinue = onNext, onBack = onBack) } ) - ) + } + wizardManager.registerPages(builtinPages + oemPages) setContent { MainTheme { diff --git a/app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemModels.kt b/app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemModels.kt new file mode 100644 index 0000000..ea658e4 --- /dev/null +++ b/app/src/main/java/dev/oxmc/setupwizard/lib/oem/OemModels.kt @@ -0,0 +1,60 @@ +package me.pawlet.setupwizard.lib.oem + +/** + * Parsed model of an OEM-provided wizard, loaded from a config APK's + * res/xml/oem_wizard.xml. Purely declarative — no code from the config APK is + * executed; the SetupWizard renders these to Compose. + */ +data class OemWizard( + val version: Int, + val pages: List +) + +data class OemPage( + val id: String, + val title: String, + val subtitle: String?, + val icon: String?, // logical icon name mapped by the renderer + val elements: List +) + +/** A setting target: "secure:" or "global:". Null = no persistence. */ +typealias OemTarget = String + +/** Value type stored for deferred application at finishSetupWizard. */ +enum class OemValueType { BOOL, STRING } + +sealed interface OemElement { + data class Section(val title: String) : OemElement + data class Text(val text: String) : OemElement + data class Image(val url: String) : OemElement + + data class Toggle( + val key: String, + val title: String, + val subtitle: String?, + val default: Boolean, + val target: OemTarget? + ) : OemElement + + data class Choice( + val key: String, + val title: String, + val target: OemTarget?, + val default: String?, + val options: List