feat: Implement iOS support and unify Compose Multiplatform infrastructure (#4876)

This commit is contained in:
James Rich 2026-03-21 18:19:13 -05:00 committed by GitHub
parent f04924ded5
commit d136b162a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
170 changed files with 2208 additions and 2432 deletions

View file

@ -43,6 +43,7 @@ import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.jvm.JvmName
@Composable
fun <T : Enum<T>> DropDownPreference(

View file

@ -217,7 +217,7 @@ fun EditTextPreference(
isError = isError,
onValueChange = {
if (maxSize > 0) {
if (it.toByteArray().size <= maxSize) {
if (it.encodeToByteArray().size <= maxSize) {
onValueChanged(it)
}
} else {
@ -255,7 +255,7 @@ fun EditTextPreference(
if (maxSize > 0 && isFocused) {
Box(contentAlignment = Alignment.BottomEnd, modifier = Modifier.fillMaxWidth()) {
Text(
text = "${value.toByteArray().size}/$maxSize",
text = "${value.encodeToByteArray().size}/$maxSize",
style = MaterialTheme.typography.bodySmall,
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(end = 8.dp, bottom = 4.dp),

View file

@ -45,6 +45,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.common.util.formatString
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.bad
import org.meshtastic.core.resources.fair
@ -153,7 +154,7 @@ fun Snr(snr: Float, modifier: Modifier = Modifier) {
Text(
modifier = modifier,
text = "%s %.2fdB".format(stringResource(Res.string.snr), snr),
text = formatString("%s %.2fdB", stringResource(Res.string.snr), snr),
color = color,
style = MaterialTheme.typography.labelSmall,
)
@ -171,7 +172,7 @@ fun Rssi(rssi: Int, modifier: Modifier = Modifier) {
}
Text(
modifier = modifier,
text = "%s %ddBm".format(stringResource(Res.string.rssi), rssi),
text = formatString("%s %ddBm", stringResource(Res.string.rssi), rssi),
color = color,
style = MaterialTheme.typography.labelSmall,
)

View file

@ -39,6 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.common.util.formatString
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.unknown
import org.meshtastic.core.ui.icon.BatteryEmpty
@ -60,7 +61,7 @@ fun MaterialBatteryInfo(
voltage: Float? = null,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
) {
val levelString = FORMAT.format(level)
val levelString = formatString(FORMAT, level)
Row(
modifier = modifier,
@ -130,7 +131,7 @@ fun MaterialBatteryInfo(
?.takeIf { it > 0 }
?.let {
Text(
text = "%.2fV".format(it),
text = formatString("%.2fV", it),
color = contentColor.copy(alpha = 0.8f),
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp),
)

View file

@ -16,6 +16,10 @@
*/
package org.meshtastic.core.ui.component
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import kotlinx.coroutines.flow.MutableSharedFlow
/**
* Event emitted when a user re-presses a bottom navigation destination that should trigger a scroll-to-top behaviour on
* the corresponding screen.
@ -25,3 +29,5 @@ sealed class ScrollToTopEvent {
data object ConversationsTabPressed : ScrollToTopEvent()
}
@Composable fun rememberScrollToTopEvents(): MutableSharedFlow<ScrollToTopEvent> = remember { MutableSharedFlow() }

View file

@ -33,6 +33,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.common.util.formatString
import org.meshtastic.core.model.Node
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.signal_quality
@ -63,7 +64,7 @@ fun SignalInfo(
tint = signalColor,
)
Text(
text = "%.1fdB · %ddBm · %s".format(node.snr, node.rssi, stringResource(quality.nameRes)),
text = formatString("%.1fdB · %ddBm · %s", node.snr, node.rssi, stringResource(quality.nameRes)),
style =
MaterialTheme.typography.labelSmall.copy(
fontWeight = FontWeight.Bold,

View file

@ -43,7 +43,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@ -297,7 +297,7 @@ fun ScannedQrCodeDialog(
}
}
@PreviewScreenSizes
@PreviewLightDark
@Composable
private fun ScannedQrCodeDialogPreview() {
ScannedQrCodeDialog(

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.ui.component
import androidx.compose.runtime.Composable
@Composable actual fun rememberTimeTickWithLifecycle(): Long = 0L
internal actual fun <T : Enum<T>> enumEntriesOf(selectedItem: T): List<T> = emptyList()
internal actual fun Enum<*>.isDeprecatedEnumEntry(): Boolean = false

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.ui.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
@Composable actual fun dynamicColorScheme(darkTheme: Boolean): ColorScheme? = null

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.ui.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLinkStyles
import org.jetbrains.compose.resources.StringResource
actual fun createClipEntry(text: String, label: String): ClipEntry =
throw UnsupportedOperationException("ClipEntry instantiation not supported on iOS stub")
actual fun annotatedStringFromHtml(html: String, linkStyles: TextLinkStyles?): AnnotatedString = AnnotatedString(html)
@Composable actual fun rememberOpenNfcSettings(): () -> Unit = {}
@Composable actual fun rememberShowToast(): suspend (String) -> Unit = { _ -> }
@Composable actual fun rememberShowToastResource(): suspend (StringResource) -> Unit = { _ -> }
@Composable actual fun rememberOpenMap(): (latitude: Double, longitude: Double, label: String) -> Unit = { _, _, _ -> }
@Composable actual fun rememberOpenUrl(): (url: String) -> Unit = { _ -> }
@Composable actual fun SetScreenBrightness(brightness: Float) {}