kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
move admin messages to nodes tab
rodzic
a60013e2dd
commit
b401c8bac3
|
@ -88,13 +88,16 @@ interface IMeshService {
|
|||
void setChannels(in byte []payload);
|
||||
|
||||
/// Send Shutdown admin packet to nodeNum
|
||||
void requestShutdown(in String nodeId);
|
||||
void requestShutdown(in int idNum);
|
||||
|
||||
/// Send Reboot admin packet to nodeNum
|
||||
void requestReboot(in String nodeId);
|
||||
void requestReboot(in int idNum);
|
||||
|
||||
/// Send FactoryReset admin packet to nodeNum
|
||||
void requestFactoryReset(in String nodeId);
|
||||
void requestFactoryReset(in int idNum);
|
||||
|
||||
/// Send NodedbReset admin packet to nodeNum
|
||||
void requestNodedbReset(in int idNum);
|
||||
|
||||
/**
|
||||
Is the packet radio currently connected to the phone? Returns a ConnectionState string.
|
||||
|
|
|
@ -234,11 +234,6 @@ class UIViewModel @Inject constructor(
|
|||
// We consider hasWifi = ESP32
|
||||
fun isESP32() = myNodeInfo.value?.hasWifi == true
|
||||
|
||||
fun hasAXP(): Boolean {
|
||||
val hasAXP = listOf(4, 7, 9) // mesh.proto 'HardwareModel' enums with AXP192 chip
|
||||
return hasAXP.contains(nodeDB.ourNodeInfo?.user?.hwModel?.number)
|
||||
}
|
||||
|
||||
/// hardware info about our local device (can be null)
|
||||
private val _myNodeInfo = MutableLiveData<MyNodeInfo?>()
|
||||
val myNodeInfo: LiveData<MyNodeInfo?> get() = _myNodeInfo
|
||||
|
@ -357,16 +352,36 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun requestShutdown() {
|
||||
meshService?.requestShutdown(DataPacket.ID_LOCAL)
|
||||
fun requestShutdown(idNum: Int) {
|
||||
try {
|
||||
meshService?.requestShutdown(idNum)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("RemoteException: ${ex.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun requestReboot() {
|
||||
meshService?.requestReboot(DataPacket.ID_LOCAL)
|
||||
fun requestReboot(idNum: Int) {
|
||||
try {
|
||||
meshService?.requestReboot(idNum)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("RemoteException: ${ex.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun requestFactoryReset() {
|
||||
meshService?.requestFactoryReset(DataPacket.ID_LOCAL)
|
||||
fun requestFactoryReset(idNum: Int) {
|
||||
try {
|
||||
meshService?.requestFactoryReset(idNum)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("RemoteException: ${ex.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun requestNodedbReset(idNum: Int) {
|
||||
try {
|
||||
meshService?.requestNodedbReset(idNum)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("RemoteException: ${ex.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1341,7 +1341,7 @@ class MeshService : Service(), Logging {
|
|||
else {
|
||||
discardNodeDB()
|
||||
debug("Installing new node DB")
|
||||
myNodeInfo = newMyNodeInfo// Install myNodeInfo as current
|
||||
myNodeInfo = newMyNodeInfo // Install myNodeInfo as current
|
||||
|
||||
newNodes.forEach(::installNodeInfo)
|
||||
newNodes.clear() // Just to save RAM ;-)
|
||||
|
@ -1383,24 +1383,30 @@ class MeshService : Service(), Logging {
|
|||
})
|
||||
}
|
||||
|
||||
private fun requestShutdown(nodeId: String) {
|
||||
sendToRadio(newMeshPacketTo(toNodeNum(nodeId)).buildAdminPacket {
|
||||
private fun requestShutdown(idNum: Int) {
|
||||
sendToRadio(newMeshPacketTo(idNum).buildAdminPacket {
|
||||
shutdownSeconds = 5
|
||||
})
|
||||
}
|
||||
|
||||
private fun requestReboot(nodeId: String) {
|
||||
sendToRadio(newMeshPacketTo(toNodeNum(nodeId)).buildAdminPacket {
|
||||
private fun requestReboot(idNum: Int) {
|
||||
sendToRadio(newMeshPacketTo(idNum).buildAdminPacket {
|
||||
rebootSeconds = 5
|
||||
})
|
||||
}
|
||||
|
||||
private fun requestFactoryReset(nodeId: String) {
|
||||
sendToRadio(newMeshPacketTo(toNodeNum(nodeId)).buildAdminPacket {
|
||||
private fun requestFactoryReset(idNum: Int) {
|
||||
sendToRadio(newMeshPacketTo(idNum).buildAdminPacket {
|
||||
factoryReset = 1
|
||||
})
|
||||
}
|
||||
|
||||
private fun requestNodedbReset(idNum: Int) {
|
||||
sendToRadio(newMeshPacketTo(idNum).buildAdminPacket {
|
||||
nodedbReset = 1
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the modern (REV2) API configuration flow
|
||||
*/
|
||||
|
@ -1721,16 +1727,20 @@ class MeshService : Service(), Logging {
|
|||
stopLocationRequests()
|
||||
}
|
||||
|
||||
override fun requestShutdown(nodeId: String) = toRemoteExceptions {
|
||||
this@MeshService.requestShutdown(nodeId)
|
||||
override fun requestShutdown(idNum: Int) = toRemoteExceptions {
|
||||
this@MeshService.requestShutdown(idNum)
|
||||
}
|
||||
|
||||
override fun requestReboot(nodeId: String) = toRemoteExceptions {
|
||||
this@MeshService.requestReboot(nodeId)
|
||||
override fun requestReboot(idNum: Int) = toRemoteExceptions {
|
||||
this@MeshService.requestReboot(idNum)
|
||||
}
|
||||
|
||||
override fun requestFactoryReset(nodeId: String) = toRemoteExceptions {
|
||||
this@MeshService.requestFactoryReset(nodeId)
|
||||
override fun requestFactoryReset(idNum: Int) = toRemoteExceptions {
|
||||
this@MeshService.requestFactoryReset(idNum)
|
||||
}
|
||||
|
||||
override fun requestNodedbReset(idNum: Int) = toRemoteExceptions {
|
||||
this@MeshService.requestNodedbReset(idNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
|||
binding.lsSleepView.isEnabled = connected && model.config.power.isPowerSaving
|
||||
binding.positionBroadcastSwitch.isEnabled = connected
|
||||
binding.lsSleepSwitch.isEnabled = connected && model.isESP32()
|
||||
binding.shutdownButton.isEnabled = connected && model.hasAXP()
|
||||
binding.rebootButton.isEnabled = connected
|
||||
binding.factoryResetButton.isEnabled = connected
|
||||
}
|
||||
|
||||
binding.positionBroadcastPeriodEditText.on(EditorInfo.IME_ACTION_DONE) {
|
||||
|
@ -111,41 +108,5 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
|||
debug("User changed isPowerSaving to $isChecked")
|
||||
}
|
||||
}
|
||||
|
||||
binding.shutdownButton.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage("${getString(R.string.shutdown)}?")
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ ->
|
||||
debug("User clicked requestShutdown")
|
||||
model.requestShutdown()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.rebootButton.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage("${getString(R.string.reboot)}?")
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ ->
|
||||
debug("User clicked requestReboot")
|
||||
model.requestReboot()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.factoryResetButton.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.are_you_sure_factory_reset)
|
||||
.setMessage(R.string.factory_reset_description)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
model.requestFactoryReset()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@ package com.geeksville.mesh.ui
|
|||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.HtmlCompat
|
||||
|
@ -12,13 +14,14 @@ import androidx.fragment.app.activityViewModels
|
|||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding
|
||||
import com.geeksville.mesh.databinding.NodelistFragmentBinding
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.util.formatAgo
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.net.URLEncoder
|
||||
|
||||
|
@ -48,6 +51,90 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
|
||||
private val nodesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||
|
||||
private var nodes = arrayOf<NodeInfo>()
|
||||
|
||||
private fun popup(view: View, position: Int) {
|
||||
val node = nodes[position]
|
||||
val user = node.user
|
||||
val showAdmin = position == 0 // TODO add admin channel check
|
||||
val popup = PopupMenu(requireContext(), view)
|
||||
popup.inflate(R.menu.menu_nodes)
|
||||
popup.menu.findItem(R.id.direct_message).isVisible = position > 0
|
||||
popup.menu.setGroupVisible(R.id.group_admin, showAdmin)
|
||||
popup.setOnMenuItemClickListener { item: MenuItem ->
|
||||
when (item.itemId) {
|
||||
R.id.direct_message -> {
|
||||
if (position > 0 && user != null) {
|
||||
debug("calling MessagesFragment filter: 0${user.id}")
|
||||
setFragmentResult(
|
||||
"requestKey",
|
||||
bundleOf(
|
||||
"contactKey" to "0${user.id}",
|
||||
"contactName" to user.longName
|
||||
)
|
||||
)
|
||||
parentFragmentManager.beginTransaction()
|
||||
.replace(R.id.mainActivityLayout, MessagesFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
R.id.reboot -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("${getString(R.string.reboot)}\n${user?.longName}?")
|
||||
.setIcon(R.drawable.ic_twotone_warning_24)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ ->
|
||||
debug("User clicked requestReboot")
|
||||
model.requestReboot(node.num)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.shutdown -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("${getString(R.string.shutdown)}\n${user?.longName}?")
|
||||
.setIcon(R.drawable.ic_twotone_warning_24)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ ->
|
||||
debug("User clicked requestShutdown")
|
||||
model.requestShutdown(node.num)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.factory_reset -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("${getString(R.string.factory_reset)}\n${user?.longName}?")
|
||||
.setIcon(R.drawable.ic_twotone_warning_24)
|
||||
.setMessage(R.string.factory_reset_description)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
debug("User clicked requestFactoryReset")
|
||||
model.requestFactoryReset(node.num)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.nodedb_reset -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("${getString(R.string.nodedb_reset)}\n${user?.longName}?")
|
||||
.setIcon(R.drawable.ic_twotone_warning_24)
|
||||
.setMessage(R.string.nodedb_reset_description)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ ->
|
||||
debug("User clicked requestNodedbReset")
|
||||
model.requestNodedbReset(node.num)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
popup.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when RecyclerView needs a new [ViewHolder] of the given type to represent
|
||||
* an item.
|
||||
|
@ -169,26 +256,14 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
}
|
||||
}
|
||||
holder.chipNode.setOnClickListener {
|
||||
if (position > 0 && user != null) {
|
||||
debug("calling MessagesFragment filter:${user.id}")
|
||||
setFragmentResult(
|
||||
"requestKey",
|
||||
bundleOf("contactKey" to "0${user.id}", "contactName" to name)
|
||||
)
|
||||
parentFragmentManager.beginTransaction()
|
||||
.replace(R.id.mainActivityLayout, MessagesFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
popup(it, position)
|
||||
}
|
||||
holder.itemView.setOnLongClickListener {
|
||||
// do something else
|
||||
popup(it, position)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private var nodes = arrayOf<NodeInfo>()
|
||||
|
||||
/// Called when our node DB changes
|
||||
fun onNodesChanged(nodesIn: Array<NodeInfo>) {
|
||||
if (nodesIn.size > 1)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillAlpha="0.3"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4.47,19h15.06L12,5.99 4.47,19zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"
|
||||
android:strokeAlpha="0.3" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M1,21h22L12,2 1,21zM4.47,19L12,5.99 19.53,19L4.47,19zM11,16h2v2h-2zM11,10h2v4h-2z" />
|
||||
</vector>
|
|
@ -66,38 +66,4 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/lsSleepView" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/shutdownButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/shutdown"
|
||||
app:layout_constraintEnd_toStartOf="@id/rebootButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/lsSleepView" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/rebootButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/reboot"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/shutdownButton"
|
||||
app:layout_constraintTop_toTopOf="@id/shutdownButton" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/factoryResetButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/factory_reset"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/shutdownButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/direct_message"
|
||||
android:title="@string/direct_message"
|
||||
app:showAsAction="withText" />
|
||||
<group android:id="@+id/group_admin">
|
||||
<item
|
||||
android:id="@+id/reboot"
|
||||
android:title="@string/reboot"
|
||||
app:showAsAction="withText" />
|
||||
<item
|
||||
android:id="@+id/shutdown"
|
||||
android:title="@string/shutdown"
|
||||
app:showAsAction="withText" />
|
||||
<item
|
||||
android:id="@+id/factory_reset"
|
||||
android:title="@string/factory_reset"
|
||||
app:showAsAction="withText" />
|
||||
<item
|
||||
android:id="@+id/nodedb_reset"
|
||||
android:title="@string/nodedb_reset"
|
||||
app:showAsAction="withText" />
|
||||
</group>
|
||||
</menu>
|
|
@ -151,8 +151,10 @@
|
|||
<string name="mode_instant">Instantly send</string>
|
||||
<string name="warning_default_psk">Empty channel names use the default encryption key (any device on %s can read your messages).</string>
|
||||
<string name="factory_reset">Factory reset</string>
|
||||
<string name="are_you_sure_factory_reset">Are you sure you want to factory reset?</string>
|
||||
<string name="factory_reset_description">This will clear all device configuration you have done.</string>
|
||||
<string name="bluetooth_disabled">Bluetooth disabled.</string>
|
||||
<string name="permission_missing_31">Meshtastic needs Nearby devices permission to find and connect to devices via Bluetooth. You can turn it off when not in use.</string>
|
||||
<string name="direct_message">Direct Message</string>
|
||||
<string name="nodedb_reset">NodeDB reset</string>
|
||||
<string name="nodedb_reset_description">This will clear all nodes from this list.</string>
|
||||
</resources>
|
||||
|
|
Ładowanie…
Reference in New Issue