kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: add menu item to add/remove nodes from `ignoreIncomingList`
rodzic
c3ec67a9ba
commit
ac3190e944
|
@ -394,6 +394,15 @@ class UIViewModel @Inject constructor(
|
||||||
updateLoraConfig { it.copy { region = value } }
|
updateLoraConfig { it.copy { region = value } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ignoreIncomingList: MutableList<Int>
|
||||||
|
get() = config.lora.ignoreIncomingList
|
||||||
|
set(value) = updateLoraConfig {
|
||||||
|
it.copy {
|
||||||
|
ignoreIncoming.clear()
|
||||||
|
ignoreIncoming.addAll(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun gpsString(p: Position): String {
|
fun gpsString(p: Position): String {
|
||||||
return when (config.display.gpsFormat) {
|
return when (config.display.gpsFormat) {
|
||||||
Config.DisplayConfig.GpsCoordinateFormat.DEC -> GPSFormat.DEC(p)
|
Config.DisplayConfig.GpsCoordinateFormat.DEC -> GPSFormat.DEC(p)
|
||||||
|
|
|
@ -2,7 +2,9 @@ package com.geeksville.mesh.ui
|
||||||
|
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.SpannableString
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.text.style.StrikethroughSpan
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -54,17 +56,29 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
private val nodesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
private val nodesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||||
|
|
||||||
private var nodes = arrayOf<NodeInfo>()
|
private var nodes = arrayOf<NodeInfo>()
|
||||||
|
val ignoreIncomingList: MutableList<Int> = mutableListOf()
|
||||||
|
|
||||||
|
private fun CharSequence.strike() = SpannableString(this).apply {
|
||||||
|
setSpan(StrikethroughSpan(), 0, this.length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CharSequence.strikeIf(isIgnored: Boolean) = if (isIgnored) strike() else this
|
||||||
|
|
||||||
private fun popup(view: View, position: Int) {
|
private fun popup(view: View, position: Int) {
|
||||||
if (!model.isConnected()) return
|
if (!model.isConnected()) return
|
||||||
val node = nodes[position]
|
val node = nodes[position]
|
||||||
val user = node.user ?: return
|
val user = node.user ?: return
|
||||||
val showAdmin = position == 0 || model.adminChannelIndex > 0
|
val showAdmin = position == 0 || model.adminChannelIndex > 0
|
||||||
|
val isIgnored = ignoreIncomingList.contains(node.num)
|
||||||
val popup = PopupMenu(requireContext(), view)
|
val popup = PopupMenu(requireContext(), view)
|
||||||
popup.inflate(R.menu.menu_nodes)
|
popup.inflate(R.menu.menu_nodes)
|
||||||
popup.menu.setGroupVisible(R.id.group_remote, position > 0)
|
popup.menu.setGroupVisible(R.id.group_remote, position > 0)
|
||||||
popup.menu.setGroupVisible(R.id.group_admin, showAdmin)
|
popup.menu.setGroupVisible(R.id.group_admin, showAdmin)
|
||||||
popup.menu.setGroupEnabled(R.id.group_admin, !model.isManaged)
|
popup.menu.setGroupEnabled(R.id.group_admin, !model.isManaged)
|
||||||
|
popup.menu.findItem(R.id.ignore).apply {
|
||||||
|
isEnabled = ignoreIncomingList.size < 3
|
||||||
|
isChecked = isIgnored
|
||||||
|
}
|
||||||
popup.setOnMenuItemClickListener { item: MenuItem ->
|
popup.setOnMenuItemClickListener { item: MenuItem ->
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.direct_message -> {
|
R.id.direct_message -> {
|
||||||
|
@ -89,6 +103,27 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
debug("requesting traceroute for '${user.longName}'")
|
debug("requesting traceroute for '${user.longName}'")
|
||||||
model.requestTraceroute(node.num)
|
model.requestTraceroute(node.num)
|
||||||
}
|
}
|
||||||
|
R.id.ignore -> {
|
||||||
|
val message = if (isIgnored) R.string.ignore_remove else R.string.ignore_add
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.ignore)
|
||||||
|
.setMessage(getString(message, user.longName))
|
||||||
|
.setNeutralButton(R.string.cancel) { _, _ -> }
|
||||||
|
.setPositiveButton(R.string.send) { _, _ ->
|
||||||
|
model.ignoreIncomingList = ignoreIncomingList.apply {
|
||||||
|
if (isIgnored) {
|
||||||
|
debug("removed '${user.longName}' from ignore list")
|
||||||
|
remove(node.num)
|
||||||
|
} else {
|
||||||
|
debug("added '${user.longName}' to ignore list")
|
||||||
|
add(node.num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
R.id.remote_admin -> {
|
R.id.remote_admin -> {
|
||||||
debug("calling remote admin --> destNum: ${node.num.toUInt()}")
|
debug("calling remote admin --> destNum: ${node.num.toUInt()}")
|
||||||
parentFragmentManager.beginTransaction()
|
parentFragmentManager.beginTransaction()
|
||||||
|
@ -167,8 +202,9 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
val n = nodes[position]
|
val n = nodes[position]
|
||||||
val user = n.user
|
val user = n.user
|
||||||
val (textColor, nodeColor) = n.colors
|
val (textColor, nodeColor) = n.colors
|
||||||
|
val isIgnored: Boolean = ignoreIncomingList.contains(n.num)
|
||||||
with(holder.chipNode) {
|
with(holder.chipNode) {
|
||||||
text = user?.shortName ?: "UNK"
|
text = (user?.shortName ?: "UNK").strikeIf(isIgnored)
|
||||||
chipBackgroundColor = ColorStateList.valueOf(nodeColor)
|
chipBackgroundColor = ColorStateList.valueOf(nodeColor)
|
||||||
setTextColor(textColor)
|
setTextColor(textColor)
|
||||||
}
|
}
|
||||||
|
@ -285,6 +321,13 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
nodesAdapter.onNodesChanged(it.perhapsReindexBy(model.myNodeNum))
|
nodesAdapter.onNodesChanged(it.perhapsReindexBy(model.myNodeNum))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.localConfig.asLiveData().observe(viewLifecycleOwner) { config ->
|
||||||
|
nodesAdapter.ignoreIncomingList.apply {
|
||||||
|
clear()
|
||||||
|
addAll(config.lora.ignoreIncomingList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
model.packetResponse.asLiveData().observe(viewLifecycleOwner) { meshLog ->
|
model.packetResponse.asLiveData().observe(viewLifecycleOwner) { meshLog ->
|
||||||
meshLog?.meshPacket?.let { meshPacket ->
|
meshLog?.meshPacket?.let { meshPacket ->
|
||||||
val routeList = meshLog.routeDiscovery?.routeList ?: return@let
|
val routeList = meshLog.routeDiscovery?.routeList ?: return@let
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
android:id="@+id/traceroute"
|
android:id="@+id/traceroute"
|
||||||
android:title="@string/traceroute"
|
android:title="@string/traceroute"
|
||||||
app:showAsAction="withText" />
|
app:showAsAction="withText" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/ignore"
|
||||||
|
android:checkable="true"
|
||||||
|
android:checked="false"
|
||||||
|
android:title="@string/ignore" />
|
||||||
</group>
|
</group>
|
||||||
<group android:id="@+id/group_admin">
|
<group android:id="@+id/group_admin">
|
||||||
<item
|
<item
|
||||||
|
|
|
@ -150,6 +150,9 @@
|
||||||
<string name="direct_message">Direct Message</string>
|
<string name="direct_message">Direct Message</string>
|
||||||
<string name="nodedb_reset">NodeDB reset</string>
|
<string name="nodedb_reset">NodeDB reset</string>
|
||||||
<string name="nodedb_reset_description">This will clear all nodes from this list.</string>
|
<string name="nodedb_reset_description">This will clear all nodes from this list.</string>
|
||||||
|
<string name="ignore">Ignore</string>
|
||||||
|
<string name="ignore_add">Add \'%s\' to ignore list? Your radio will reboot after making this change.</string>
|
||||||
|
<string name="ignore_remove">Remove \'%s\' from ignore list? Your radio will reboot after making this change.</string>
|
||||||
<string name="map_select_download_region">Select download region</string>
|
<string name="map_select_download_region">Select download region</string>
|
||||||
<string name="map_5_miles">5 Miles</string>
|
<string name="map_5_miles">5 Miles</string>
|
||||||
<string name="map_10_miles">10 miles</string>
|
<string name="map_10_miles">10 miles</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue