From fc86c696cd6dbb2cf3e5c43e38ec43c501bbbb51 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Fri, 3 Apr 2026 08:47:15 -0500 Subject: [PATCH] feat(wifi-provision): add mPWRD-OS branding and disclaimer banner (#4978) --- .../ProcessRadioResponseUseCaseTest.kt | 8 ++-- .../drawable/img_mpwrd_logo.png | Bin 0 -> 8721 bytes .../composeResources/values/strings.xml | 7 +-- .../meshtastic/core/takserver/CoTXmlTest.kt | 4 +- .../core/takserver/TAKPacketConversionTest.kt | 4 +- feature/wifi-provision/README.md | 4 +- .../wifiprovision/ui/WifiProvisionPreviews.kt | 10 ++++ .../wifiprovision/ui/WifiProvisionScreen.kt | 43 ++++++++++++++++++ 8 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png diff --git a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt index 4bc54ac08..b8fcf6a20 100644 --- a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt @@ -77,7 +77,7 @@ class ProcessRadioResponseUseCaseTest { // Assert assertTrue(result is RadioResponseResult.Metadata) - assertEquals("2.5.0", (result as RadioResponseResult.Metadata).metadata.firmware_version) + assertEquals("2.5.0", result.metadata.firmware_version) } @Test @@ -99,7 +99,7 @@ class ProcessRadioResponseUseCaseTest { // Assert assertTrue(result is RadioResponseResult.CannedMessages) - assertEquals("Hello World", (result as RadioResponseResult.CannedMessages).messages) + assertEquals("Hello World", result.messages) } @Test @@ -133,7 +133,7 @@ class ProcessRadioResponseUseCaseTest { ) val result = useCase(packet, 123, setOf(42)) assertTrue(result is RadioResponseResult.Owner) - assertEquals("Owner", (result as RadioResponseResult.Owner).user.long_name) + assertEquals("Owner", result.user.long_name) } @Test @@ -186,7 +186,7 @@ class ProcessRadioResponseUseCaseTest { ) val result = useCase(packet, 123, setOf(42)) assertTrue(result is RadioResponseResult.ChannelResponse) - assertEquals("Main", (result as RadioResponseResult.ChannelResponse).channel.settings?.name) + assertEquals("Main", result.channel.settings?.name) } private fun ByteArray.toByteString() = okio.ByteString.of(*this) diff --git a/core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png b/core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..224c5add369faf2902c50d6a47ce1f291482bc1d GIT binary patch literal 8721 zcmb_iRa+cgv&CJ5`{2RdLU4D7Ai>=UZbNXFV8Pwpo!~A(gTvtNKG>P}?)-r7qN}g^ z+0|WZ)mpWCN2)5zpra6@KtVyF%gIWrL++#hT}TL!SpBUg2MUUESx!<+(<|#N8`(`$ z>fys_PFIi;b>*?R@soJ}A8INXDlV|=z!oTu8reIyma(Yc+{AzM4nfuX2Jia-Gpd|I8wM5#LMY)#)cV+a6 zR&J{beyFIFpj37PxqarzPC&CO;G&}E^vg@$u_|qEFH{@-i`*QaM)^lRF3N`wTGJuh zQFom5=)XH+ZcDu>t3(*bU;`MU{bE67)YS4Q?|_ z#(c%(CcRxK>fR?>Cv!2Q%=gxF;|>&kO5c64GBC|wsy9wN2N^b8rz!SwEbMuj}pIgU_KhH`xR zJyq%0YzZ}UyrTgOu@#PMV|t(8f7>|OxvJNnuH&JimCWR9Af-_3M?jLX8bmEJf3Zp0 zY70g-BxbV>us&wA39i^^S9Ac7R4TAIy^PPrSO+egvxGgIA)J7Pm%;<}3*yWC3rviS zp`u!I%$;P`Fx&r9>>I)b6V}5Vvx;CAJDIC3=}7zhw7oZ1dy!PQt|XdC-=dP&??&HH zZA)LH{!R1?X7=8i@-kHOs3)(m!s*l;k>j*9s9z53|F48PWu|T`1ReoKFHTEhpifrW@Aa?_5kz)SRq-{7Z zEWrY~;2{#9Tk4E!p3}8Q8udhHzfqhXk6Iqk%|iUyCmjfM;2VeD;hVy5R%vRo{3rG% z+^l;!`)du2$A}=~p8SWEByy3L9fCpGWl0T1H;~xlCW*MqGbz#bmv2f>5<1!Q6n^_} zQX&o*o7d*oL|1aM&84vZBb&hgmNoaRJKIPT|9~gk6Y?}4WPOW63bgJ7=iRx3X}^b7 z!iI<>)7a)(mp_5{tVJYxJS8NI*5E(;pTIh_~2;gUiKsn@YzNBrjDZo|(PchV! z01M7$4%;d|jkg^Zda<>YiG{Fcjw<*$t>J^*5b0QuNGvww^Mul6dSd2FdvHL;ES6wP{C4}GB zV5R9>-Fwv+cWe!#sz?(iEc;1Lek$;X3b&Us3RbfU_g#y19;kLCZ5645i~MWp>lTW^ z-MmKcKQ*xp!{<&b7pjO~y>+-ql^mzSV-%Y4D!7omAVm222fqUzA!LN+qf#4WpqZ1W zCvj=LQadYt;VP3?8~exTUw<7=gmPhMV!%8}BZ`%vs*teQg7ECz6YAP#Y-~1qnYPlL z@02Hy-|H`Gv14ax2hRAGpu+bpJwgu{1|SbCEB6dZ<2KVsuxmsn1k{jkgj!tNC_sg< zl>((4Sc6a-E|QFfLICzNx_^<+JJ%$0_qQ|thFk=0^s@#RXLSI3r)EJt-)JGl?Dgfd z15(OUnIQcw55)3${MN@A_eVYgXvuglgeCRyH4;Y(O(!f?bf!$L&)9RhT~K`a#svbF z)DxlSnc@;pp4P`7L1|W{*eUlJcxZp3smd-F*JjYY+4P3l6j2}#F065;za|yr=1@i7 z6`c3*h~Ja9B#T$j(*idQSkqaKArUhb23u6FTFQwV8IW zi-l2GiWs>{PwckguX@$GtLV!IRM7O&WQV^qRTV1VN~15{>2{q_IG(1qsxky@>>uWz zDf!4q9J>-^!q?tQvLv65IoMBqYZkTr z!wjbc?{Dwjt27HDruDaPZ)qf0K=b88`p7I0Bf6Mg24{00mG}zmpk6skyIvXA;6vCHtl!yEQxi%o?%PcOCnimxYExQ zEtEZRGrqkZ+1wVHZBxuh{ll?-?D~`WuO;vZFSE-0hEkJ%KlvRJcEyWFmvoW+FyjBh z+y$&w2Us}en)vKWSCL|3Y|f3OJ}$V4@3Xc*m0igs_RZ?N8rP&*t?XQ`{pChOtu?B| zksVO6n7OlqclWPt&j+=BR)?UnU=tW+Be!IhDDqIwiRe*-f%Ocxc4GKEZ9l`lN09`p zO`DLjs_Z98%mRTP)==9f8~$Q00qE?iY5B8#8ilc$*AzcXl{^5V zk0yDwxgEk8acK<$_N*NX)vDLRne_Gx8XCO<5x~dH@)r5epRM_Zs$x+$EUhnb!Qx z4Cw0gyTan+Z=s?yUt7+y?N7iDh1q~tG4G;thRuw?SXVvLw`BYe=E9vWz*Wn)5?=Y1 z+%Raew<9*Z27-th8C3YpI5k14)h~?B4KM;{seEMR=6c3$UHYs;R><9KzsBG)R766>p8(^)6naIqhNDpGG>PGJ)*0aBxE!kG&b1oC zpylG>jD%*?}9$Ce|bE(;Wngm52O{xJuQRdDC=a)Z+dGu)OS)r=#-xI=M4mo z%D)%;N{17wMZquN{3D9H^koM!+&mw3a(dClbDc(|YRC$fu)0vm?a1Ap%x&?Hg*COO zCuZbwERpu`>fm9U8O@o}ROIi%7zRW)B9Wmb4P>=Dby#5~k=S!8IBWwQA9ERouv#A$ zg3b%-qw0Vu8!gpG2M%}t__24a!L}1B1Xx&De#=bwzcztbUX^#b7VP7ye5%zJB-u^#h0>T@ebexYN#;fm&mksk< zg~0_=$6hJw)1%4&soE^#@dNB08{TDK{{KkdvETp!MkTdrA~VJrhvZ(=_0s~29(M|v zVk9qfU=>_Y*v5lv?81HUMa*v*x(9NWJtxJ1!?^h!9zHm~+SW-&0Tz^Ep++I~7k5gx zy)n-UT9QDwa{c@p2{6>>;&E8KW&w(gN9)(6dfe1vx7A9^iZp8g56^1f2QN; zA{D*9xz6a$h<_Kk3v>YqPwPO4lvi#$vfaoGVSNPOUR5llX>u((Db@BJ%8sLt!Ox{? z{l(S4_%e(to;tRhKyu%+lDO1P3ueF6R0y@3v_7+#6d$uIHTMikDf+K#jkRbj+&hrr zVGX3|YcpqH==44_gKuSLtW1

3Fs=>9)-Yo2&(WC@(3HViq6it>^t`# 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 BkD~wp literal 0 HcmV?d00001 diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml index 692ddcb37..9b8c6d7aa 100644 --- a/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/core/resources/src/commonMain/composeResources/values/strings.xml @@ -940,7 +940,7 @@ PAX Metrics PAX No PAX metrics available. - WiFi Devices + Wi-Fi Provisioning for mPWRD-OS Bluetooth Devices Paired devices Connected Device @@ -1327,8 +1327,9 @@ Connect Done - WiFi Provisioning - Provision WiFi credentials to your Meshtastic device via Bluetooth. + Wi-Fi Provisioning for mPWRD-OS + Provision Wi-Fi credentials to your mPWRD-OS device via Bluetooth. + Learn more about the mPWRD-OS project\nhttps://github.com/mPWRD-OS Searching for device… Device found Ready to scan for WiFi networks. diff --git a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt index 2c7669319..7b6aa0ecd 100644 --- a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt +++ b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt @@ -81,8 +81,8 @@ class CoTXmlTest { assertEquals("b-t-f", roundTripped.type) assertNotNull(roundTripped.chat) - assertEquals("Hello World", roundTripped.chat?.message) - assertEquals("Alice", roundTripped.chat?.senderCallsign) + assertEquals("Hello World", roundTripped.chat.message) + assertEquals("Alice", roundTripped.chat.senderCallsign) } // ── XML escaping ───────────────────────────────────────────────────────── diff --git a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt index 9bab59c03..771f10cfe 100644 --- a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt +++ b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt @@ -92,8 +92,8 @@ class TAKPacketConversionTest { assertEquals(85, cot.status?.battery) assertNotNull(cot.track) - assertEquals(5.0, cot.track?.speed) - assertEquals(90.0, cot.track?.course) + assertEquals(5.0, cot.track.speed) + assertEquals(90.0, cot.track.course) } @Test diff --git a/feature/wifi-provision/README.md b/feature/wifi-provision/README.md index 4e61464a0..1403c6d79 100644 --- a/feature/wifi-provision/README.md +++ b/feature/wifi-provision/README.md @@ -23,9 +23,9 @@ classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000; ``` -## WiFi Provisioning System +## WiFi Provisioning System — for mPWRD-OS -The `:feature:wifi-provision` module provides BLE-based WiFi provisioning for Meshtastic devices using the Nymea network manager protocol. It scans for provisioning-capable devices, retrieves available WiFi networks, and applies credentials — all over BLE via the Kable multiplatform library. +The `:feature:wifi-provision` module provides BLE-based WiFi provisioning for [mPWRD-OS](https://github.com/mPWRD-OS/mPWRD-OS) devices using the Nymea network manager protocol. mPWRD-OS is a community project that combines Armbian and Meshtastic for Linux-native mesh networking hardware. This module scans for provisioning-capable devices, retrieves available WiFi networks, and applies credentials — all over BLE via the Kable multiplatform library. ### Architecture diff --git a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt index 0bb2100aa..dc9f62f8d 100644 --- a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt +++ b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt @@ -346,3 +346,13 @@ private fun NetworkRowLongSsidPreview() { } } } + +// --------------------------------------------------------------------------- +// mPWRD-OS disclaimer banner +// --------------------------------------------------------------------------- + +@PreviewLightDark +@Composable +private fun MpwrdDisclaimerBannerPreview() { + AppTheme { Surface { Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) { MpwrdDisclaimerBanner() } } } +} diff --git a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt index 6f9c9dc68..ced6d212c 100644 --- a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt +++ b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -38,6 +39,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -77,6 +79,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.text.input.ImeAction @@ -86,6 +89,7 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.meshtastic.core.resources.Res @@ -93,6 +97,7 @@ import org.meshtastic.core.resources.apply import org.meshtastic.core.resources.back import org.meshtastic.core.resources.cancel import org.meshtastic.core.resources.hide_password +import org.meshtastic.core.resources.img_mpwrd_logo import org.meshtastic.core.resources.password import org.meshtastic.core.resources.show_password import org.meshtastic.core.resources.wifi_provision_available_networks @@ -100,6 +105,7 @@ import org.meshtastic.core.resources.wifi_provision_connect_failed import org.meshtastic.core.resources.wifi_provision_description import org.meshtastic.core.resources.wifi_provision_device_found import org.meshtastic.core.resources.wifi_provision_device_found_detail +import org.meshtastic.core.resources.wifi_provision_mpwrd_disclaimer import org.meshtastic.core.resources.wifi_provision_no_networks import org.meshtastic.core.resources.wifi_provision_scan_failed import org.meshtastic.core.resources.wifi_provision_scan_networks @@ -110,6 +116,7 @@ import org.meshtastic.core.resources.wifi_provision_signal_strength import org.meshtastic.core.resources.wifi_provision_ssid_label import org.meshtastic.core.resources.wifi_provision_ssid_placeholder import org.meshtastic.core.resources.wifi_provisioning +import org.meshtastic.core.ui.component.AutoLinkText import org.meshtastic.feature.wifiprovision.WifiProvisionError import org.meshtastic.feature.wifiprovision.WifiProvisionUiState import org.meshtastic.feature.wifiprovision.WifiProvisionUiState.Phase @@ -164,6 +171,8 @@ fun WifiProvisionScreen( Spacer(Modifier.height(4.dp)) } + MpwrdDisclaimerBanner() + Crossfade(targetState = screenKey(uiState), label = "wifi_provision") { key -> when (key) { ScreenKey.ConnectingBle -> ScanningBleContent() @@ -481,6 +490,40 @@ internal fun NetworkRow(network: WifiNetwork, isSelected: Boolean, onClick: () - ) } +// --------------------------------------------------------------------------- +// mPWRD-OS disclaimer banner +// --------------------------------------------------------------------------- + +private const val MPWRD_LOGO_SIZE_DP = 40 + +/** Branded disclaimer banner shown at the top of the provisioning screen. */ +@Composable +internal fun MpwrdDisclaimerBanner() { + Card( + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium, + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh), + ) { + Row( + modifier = Modifier.padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.Top, + ) { + Image( + painter = painterResource(Res.drawable.img_mpwrd_logo), + contentDescription = "mPWRD-OS", + modifier = Modifier.size(MPWRD_LOGO_SIZE_DP.dp).clip(RoundedCornerShape(8.dp)), + ) + AutoLinkText( + text = stringResource(Res.string.wifi_provision_mpwrd_disclaimer), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } + } +} + // --------------------------------------------------------------------------- // Shared layout wrapper for centered status screens // ---------------------------------------------------------------------------