kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: show lock icon for nodes with public keys 🔒
rodzic
092ed32c23
commit
24886994a6
|
@ -21,6 +21,7 @@ import com.geeksville.mesh.android.Logging
|
|||
import com.geeksville.mesh.database.MeshLogRepository
|
||||
import com.geeksville.mesh.database.PacketRepository
|
||||
import com.geeksville.mesh.database.QuickChatActionRepository
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
import com.geeksville.mesh.database.entity.toNodeInfo
|
||||
|
@ -229,9 +230,9 @@ class UIViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val nodeList: StateFlow<List<NodeInfo>> = nodesUiState.flatMapLatest { state ->
|
||||
val nodeList: StateFlow<List<NodeEntity>> = nodesUiState.flatMapLatest { state ->
|
||||
nodeDB.getNodes(state.sort, state.filter, state.includeUnknown)
|
||||
}.mapLatest { list -> list.map { it.toNodeInfo() } }.stateIn(
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = Eagerly,
|
||||
initialValue = emptyList(),
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.geeksville.mesh.DataPacket
|
|||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
import com.geeksville.mesh.database.entity.toNodeInfo
|
||||
import com.geeksville.mesh.databinding.MessagesFragmentBinding
|
||||
import com.geeksville.mesh.model.Message
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
|
@ -294,9 +295,9 @@ class MessagesFragment : Fragment(), Logging {
|
|||
}
|
||||
|
||||
private fun openNodeInfo(msg: Message) = lifecycleScope.launch {
|
||||
model.nodeList.firstOrNull()?.find { it.user?.id == msg.user.id }?.let { node ->
|
||||
model.nodeList.firstOrNull()?.find { it.user.id == msg.user.id }?.let { node ->
|
||||
parentFragmentManager.popBackStack()
|
||||
model.focusUserNode(node)
|
||||
model.focusUserNode(node.toNodeInfo())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,11 +75,13 @@ fun NodeInfo(
|
|||
blinking: Boolean = false,
|
||||
expanded: Boolean = false,
|
||||
currentTimeMillis: Long,
|
||||
hasPublicKey: Boolean = false,
|
||||
) {
|
||||
val isUnknownUser = thatNodeInfo.user?.hwModel == MeshProtos.HardwareModel.UNSET
|
||||
val unknownShortName = stringResource(id = R.string.unknown_node_short_name)
|
||||
val unknownLongName = stringResource(id = R.string.unknown_username)
|
||||
val longName = thatNodeInfo.user?.longName ?: stringResource(id = R.string.unknown_username)
|
||||
|
||||
val nodeName = thatNodeInfo.user?.longName ?: unknownLongName
|
||||
val nodeName = if (hasPublicKey) "🔒 $longName" else longName
|
||||
val isThisNode = thisNodeInfo?.num == thatNodeInfo.num
|
||||
val distance = thisNodeInfo?.distanceStr(thatNodeInfo, distanceUnits)
|
||||
val (textColor, nodeColor) = thatNodeInfo.colors
|
||||
|
@ -101,7 +103,7 @@ fun NodeInfo(
|
|||
label = "blinking node"
|
||||
)
|
||||
|
||||
val style = if (thatNodeInfo.user?.hwModel == MeshProtos.HardwareModel.UNSET) {
|
||||
val style = if (isUnknownUser) {
|
||||
LocalTextStyle.current.copy(fontStyle = FontStyle.Italic)
|
||||
} else {
|
||||
LocalTextStyle.current
|
||||
|
|
|
@ -4,12 +4,12 @@ import android.view.Gravity
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
internal fun View.nodeMenu(
|
||||
node: NodeInfo,
|
||||
node: NodeEntity,
|
||||
ignoreIncomingList: List<Int>,
|
||||
isOurNode: Boolean = false,
|
||||
isManaged: Boolean = false,
|
||||
|
@ -43,7 +43,7 @@ internal fun View.nodeMenu(
|
|||
val message = if (isIgnored) R.string.ignore_remove else R.string.ignore_add
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.ignore)
|
||||
.setMessage(context.getString(message, node.user?.longName))
|
||||
.setMessage(context.getString(message, node.user.longName))
|
||||
.setNeutralButton(R.string.cancel) { _, _ -> }
|
||||
.setPositiveButton(R.string.send) { _, _ ->
|
||||
item.onMenuItemAction()
|
||||
|
|
|
@ -22,9 +22,11 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.geeksville.mesh.database.entity.toNodeInfo
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.components.NodeFilterTextField
|
||||
import com.geeksville.mesh.ui.components.rememberTimeTickWithLifecycle
|
||||
|
@ -36,7 +38,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
|
||||
private val model: UIViewModel by activityViewModels()
|
||||
|
||||
private fun popup(node: NodeInfo) {
|
||||
private fun popup(node: NodeEntity) {
|
||||
if (!model.isConnected()) return
|
||||
val isOurNode = node.num == model.myNodeNum
|
||||
val ignoreIncomingList = model.ignoreIncomingList
|
||||
|
@ -77,30 +79,31 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
}
|
||||
|
||||
R.id.remote_admin -> {
|
||||
navigateToRadioConfig(node)
|
||||
navigateToRadioConfig(node.num)
|
||||
}
|
||||
|
||||
R.id.metrics -> {
|
||||
navigateToMetrics(node)
|
||||
navigateToMetrics(node.num)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToMessages(node: NodeInfo) = node.user?.let { user ->
|
||||
val contactKey = "${node.channel}${user.id}"
|
||||
private fun navigateToMessages(node: NodeEntity) = node.user.let { user ->
|
||||
val channel = if (user.publicKey.isEmpty) node.channel else DataPacket.PKC_CHANNEL_INDEX
|
||||
val contactKey = "$channel${user.id}"
|
||||
info("calling MessagesFragment filter: $contactKey")
|
||||
parentFragmentManager.navigateToMessages(contactKey, user.longName)
|
||||
}
|
||||
|
||||
private fun navigateToRadioConfig(node: NodeInfo) {
|
||||
info("calling RadioConfig --> destNum: ${node.num}")
|
||||
parentFragmentManager.navigateToRadioConfig(node.num)
|
||||
private fun navigateToRadioConfig(nodeNum: Int) {
|
||||
info("calling RadioConfig --> destNum: $nodeNum")
|
||||
parentFragmentManager.navigateToRadioConfig(nodeNum)
|
||||
}
|
||||
|
||||
private fun navigateToMetrics(node: NodeInfo) {
|
||||
info("calling Metrics --> destNum: ${node.num}")
|
||||
parentFragmentManager.navigateToMetrics(node.num)
|
||||
private fun navigateToMetrics(nodeNum: Int) {
|
||||
info("calling Metrics --> destNum: $nodeNum")
|
||||
parentFragmentManager.navigateToMetrics(nodeNum)
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,7 +127,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
@Composable
|
||||
fun NodesScreen(
|
||||
model: UIViewModel = hiltViewModel(),
|
||||
chipClicked: (NodeInfo) -> Unit,
|
||||
chipClicked: (NodeEntity) -> Unit,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val state by model.nodesUiState.collectAsStateWithLifecycle()
|
||||
|
@ -136,7 +139,7 @@ fun NodesScreen(
|
|||
val focusedNode by model.focusedNode.collectAsStateWithLifecycle()
|
||||
LaunchedEffect(focusedNode) {
|
||||
focusedNode?.let { node ->
|
||||
val index = nodes.indexOfFirst { it == node }
|
||||
val index = nodes.indexOfFirst { it.num == node.num }
|
||||
if (index != -1) {
|
||||
listState.animateScrollToItem(index)
|
||||
}
|
||||
|
@ -167,9 +170,10 @@ fun NodesScreen(
|
|||
}
|
||||
|
||||
items(nodes, key = { it.num }) { node ->
|
||||
val nodeInfo = node.toNodeInfo()
|
||||
NodeInfo(
|
||||
thisNodeInfo = ourNodeInfo,
|
||||
thatNodeInfo = node,
|
||||
thatNodeInfo = nodeInfo,
|
||||
gpsFormat = state.gpsFormat,
|
||||
distanceUnits = state.distanceUnits,
|
||||
tempInFahrenheit = state.tempInFahrenheit,
|
||||
|
@ -178,9 +182,10 @@ fun NodesScreen(
|
|||
focusManager.clearFocus()
|
||||
chipClicked(node)
|
||||
},
|
||||
blinking = node == focusedNode,
|
||||
blinking = nodeInfo == focusedNode,
|
||||
expanded = state.showDetails,
|
||||
currentTimeMillis = currentTimeMillis
|
||||
currentTimeMillis = currentTimeMillis,
|
||||
hasPublicKey = !node.user.publicKey.isEmpty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import com.geeksville.mesh.android.hasGps
|
|||
import com.geeksville.mesh.android.hasLocationPermission
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.database.entity.toNodeInfo
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.model.map.CustomTileSource
|
||||
import com.geeksville.mesh.model.map.MarkerWithLabel
|
||||
|
@ -59,6 +60,8 @@ import com.geeksville.mesh.util.zoomIn
|
|||
import com.geeksville.mesh.waypoint
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.events.MapEventsReceiver
|
||||
|
@ -299,7 +302,10 @@ fun MapView(
|
|||
if (permissions.entries.all { it.value }) map.toggleMyLocation()
|
||||
}
|
||||
|
||||
val nodes by model.nodeList.collectAsStateWithLifecycle()
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val nodes by model.nodeList
|
||||
.mapLatest { list -> list.map { it.toNodeInfo() } }
|
||||
.collectAsStateWithLifecycle(emptyList())
|
||||
val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap())
|
||||
|
||||
var showDownloadButton: Boolean by remember { mutableStateOf(false) }
|
||||
|
|
Ładowanie…
Reference in New Issue