Feat/2061 public ind (#2284)

This commit is contained in:
DaneEvans 2025-06-29 12:45:12 +10:00 committed by GitHub
parent 80723c59cc
commit 3d9b69eda5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 14 deletions

View file

@ -177,6 +177,7 @@ data class Contact(
val isMuted: Boolean,
val isUnmessageable: Boolean,
val nodeColors: Pair<Int, Int>? = null,
val isDefaultPSK: Boolean? = false
)
@Suppress("LongParameterList", "LargeClass")
@ -519,6 +520,15 @@ class UIViewModel @Inject constructor(
} else {
user.longName
}
val isDefaultPSK = if (toBroadcast) {
val _channel = channelSet.getChannel(data.channel)
val isDefaultPSK = (_channel?.settings?.psk?.size() == 1 &&
_channel.settings.psk.byteAt(0) == 1.toByte()) ||
_channel?.settings?.psk?.toByteArray()?.isEmpty() == true
isDefaultPSK
} else {
false
}
Contact(
contactKey = contactKey,
@ -535,6 +545,7 @@ class UIViewModel @Inject constructor(
} else {
null
},
isDefaultPSK = isDefaultPSK
)
}
}.stateIn(

View file

@ -23,6 +23,7 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
@ -41,6 +42,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@ -49,6 +51,7 @@ import androidx.compose.ui.unit.dp
import com.geeksville.mesh.R
import com.geeksville.mesh.model.Contact
import com.geeksville.mesh.ui.common.theme.AppTheme
import androidx.compose.ui.graphics.vector.ImageVector
@Suppress("LongMethod")
@Composable
@ -108,10 +111,23 @@ fun ContactItem(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = longName,
modifier = Modifier.weight(1f),
)
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) {
Text(
text = longName,
)
// Show unlock icon for broadcast with default PSK
val isBroadcast = contact.contactKey.getOrNull(1) == '^' ||
contact.contactKey.endsWith("^all") ||
contact.contactKey.endsWith("^broadcast")
if (isBroadcast && isDefaultPSK == true) {
Spacer(modifier = Modifier.width(10.dp))
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_lock_open_right_24),
contentDescription = "Unlocked"
)
}
}
Text(
text = lastMessageTime.orEmpty(),
color = MaterialTheme.colorScheme.onSurface,
@ -172,7 +188,7 @@ private fun ContactItemPreview() {
unreadCount = 2,
messageCount = 10,
isMuted = true,
isUnmessageable = false,
isUnmessageable = false
),
selected = false,
)

View file

@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
@ -76,6 +77,7 @@ import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
@ -97,6 +99,7 @@ import com.geeksville.mesh.ui.node.components.NodeKeyStatusIcon
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.sharing.SharedContactDialog
import kotlinx.coroutines.launch
import androidx.compose.ui.graphics.vector.ImageVector
private const val MESSAGE_CHARACTER_LIMIT = 200
private const val SNIPPET_CHARACTER_LIMIT = 50
@ -122,12 +125,20 @@ internal fun MessageScreen(
val channels by viewModel.channels.collectAsStateWithLifecycle()
val channelName by remember(channelIndex) {
derivedStateOf {
channelIndex?.let { channels.getChannel(it)?.name } ?: "Unknown Channel"
channelIndex?.let {
val channel = channels.getChannel(it)
val name = channel?.name ?: "Unknown Channel"
// Check if PSK is the default (base64 'AQ==', i.e., single byte 0x01)
val isDefaultPSK = (channel?.settings?.psk?.size() == 1 &&
channel.settings.psk.byteAt(0) == 1.toByte()) ||
channel?.psk?.toByteArray()?.isEmpty() == true
Pair(name, isDefaultPSK)
} ?: Pair("Unknown Channel", false)
}
}
val (channelTitle, isDefaultPsk) = channelName
val title = when (nodeId) {
DataPacket.ID_BROADCAST -> channelName
DataPacket.ID_BROADCAST -> channelTitle
else -> viewModel.getUser(nodeId).longName
}
viewModel.setTitle(title)
@ -201,7 +212,8 @@ internal fun MessageScreen(
}
}
} else {
MessageTopBar(title, channelIndex, mismatchKey, onNavigateBack)
MessageTopBar(title, channelIndex, mismatchKey, onNavigateBack, isDefaultPsk = isDefaultPsk
)
}
},
) { padding ->
@ -442,9 +454,22 @@ private fun MessageTopBar(
title: String,
channelIndex: Int?,
mismatchKey: Boolean = false,
onNavigateBack: () -> Unit
onNavigateBack: () -> Unit,
isDefaultPsk: Boolean = false
) = TopAppBar(
title = { Text(text = title) },
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = title)
if (isDefaultPsk
) {
Spacer(modifier = Modifier.width(10.dp))
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_lock_open_right_24),
contentDescription = "Unlocked"
)
}
}
},
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(

View file

@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
@ -61,6 +62,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -78,6 +80,7 @@ import com.geeksville.mesh.ui.common.components.dragContainer
import com.geeksville.mesh.ui.common.components.dragDropItemsIndexed
import com.geeksville.mesh.ui.common.components.rememberDragDropState
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
import androidx.compose.ui.graphics.vector.ImageVector
@Composable
private fun ChannelItem(
@ -122,12 +125,20 @@ fun ChannelCard(
enabled: Boolean,
onEditClick: () -> Unit,
onDeleteClick: () -> Unit,
isDefaultPSK: Boolean = false,
) = ChannelItem(
index = index,
title = title,
enabled = enabled,
onClick = onEditClick,
) {
if (isDefaultPSK) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_lock_open_right_24),
contentDescription = "Unlocked"
)
Spacer(modifier = Modifier.width(10.dp))
}
IconButton(onClick = { onDeleteClick() }) {
Icon(
imageVector = Icons.TwoTone.Close,
@ -143,13 +154,20 @@ fun ChannelSelection(
title: String,
enabled: Boolean,
isSelected: Boolean,
onSelected: (Boolean) -> Unit
onSelected: (Boolean) -> Unit,
isDefaultPSK: Boolean = false,
) = ChannelItem(
index = index,
title = title,
enabled = enabled,
onClick = {},
) {
if (isDefaultPSK) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_lock_open_right_24),
contentDescription = "Unlocked"
)
}
Checkbox(
enabled = enabled,
checked = isSelected,
@ -274,12 +292,15 @@ fun ChannelSettingsItemList(
items = settingsListInput,
dragDropState = dragDropState,
) { index, channel, isDragging ->
val isDefaultPSK = (channel.psk.size() == 1 && channel.psk.byteAt(0) == 1.toByte()) ||
channel.psk.toByteArray().isEmpty()
ChannelCard(
index = index,
title = channel.name.ifEmpty { primaryChannel.name },
enabled = enabled,
onEditClick = { showEditChannelDialog = index },
onDeleteClick = { settingsListInput.removeAt(index) }
onDeleteClick = { settingsListInput.removeAt(index) },
isDefaultPSK = isDefaultPSK
)
if (index == 0 && !isDragging) {
Text(

View file

@ -448,9 +448,13 @@ private fun ChannelListView(
AdaptiveTwoPane(
first = {
channelSet.settingsList.forEachIndexed { index, channel ->
val isDefaultPSK = (channel.psk.size() == 1 && channel.psk.byteAt(0) == 1.toByte()) ||
channel.psk.toByteArray().isEmpty()
val displayTitle = channel.name.ifEmpty { modemPresetName }
ChannelSelection(
index = index,
title = channel.name.ifEmpty { modemPresetName },
title = displayTitle,
enabled = enabled,
isSelected = channelSelections[index],
onSelected = {
@ -458,6 +462,7 @@ private fun ChannelListView(
channelSelections[index] = it
}
},
isDefaultPSK = isDefaultPSK
)
}
OutlinedButton(