mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
82ebfd0094
48 changed files with 1099 additions and 607 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.ColorMatrix
|
||||
import android.graphics.ColorMatrixColorFilter
|
||||
|
|
@ -18,7 +19,6 @@ import com.geeksville.android.Logging
|
|||
import com.geeksville.android.hideKeyboard
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.ChannelProtos
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.databinding.ChannelFragmentBinding
|
||||
import com.geeksville.mesh.model.Channel
|
||||
|
|
@ -50,6 +50,7 @@ fun ImageView.setOpaque() {
|
|||
class ChannelFragment : ScreenFragment("Channel"), Logging {
|
||||
|
||||
private var _binding: ChannelFragmentBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
|
|
@ -81,6 +82,12 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
val channels = model.channels.value
|
||||
val channel = channels?.primaryChannel
|
||||
|
||||
val connected = model.isConnected.value == MeshService.ConnectionState.CONNECTED
|
||||
|
||||
// Only let buttons work if we are connected to the radio
|
||||
binding.shareButton.isEnabled = connected
|
||||
binding.resetButton.isEnabled = connected && Channel.default != channel
|
||||
|
||||
binding.editableCheckbox.isChecked = false // start locked
|
||||
if (channel != null) {
|
||||
binding.qrView.visibility = View.VISIBLE
|
||||
|
|
@ -89,7 +96,6 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
|
||||
// For now, we only let the user edit/save channels while the radio is awake - because the service
|
||||
// doesn't cache radioconfig writes.
|
||||
val connected = model.isConnected.value == MeshService.ConnectionState.CONNECTED
|
||||
binding.editableCheckbox.isEnabled = connected
|
||||
|
||||
binding.qrView.setImageBitmap(channels.getChannelQR())
|
||||
|
|
@ -138,8 +144,38 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
requireActivity().startActivity(shareIntent)
|
||||
try {
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
requireActivity().startActivity(shareIntent)
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
Snackbar.make(
|
||||
binding.shareButton,
|
||||
R.string.no_app_found,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send new channel settings to the device
|
||||
private fun installSettings(newChannel: ChannelProtos.ChannelSettings) {
|
||||
val newSet =
|
||||
ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
|
||||
// Try to change the radio, if it fails, tell the user why and throw away their redits
|
||||
try {
|
||||
model.setChannels(newSet)
|
||||
// Since we are writing to radioconfig, that will trigger the rest of the GUI update (QR code etc)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("ignoring channel problem", ex)
|
||||
|
||||
setGUIfromModel() // Throw away user edits
|
||||
|
||||
// Tell the user to try again
|
||||
Snackbar.make(
|
||||
binding.editableCheckbox,
|
||||
R.string.radio_sleeping,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +186,34 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
requireActivity().hideKeyboard()
|
||||
}
|
||||
|
||||
binding.resetButton.setOnClickListener { _ ->
|
||||
// User just locked it, we should warn and then apply changes to radio
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.reset_to_defaults)
|
||||
.setMessage(R.string.are_you_shure_change_default)
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
setGUIfromModel() // throw away any edits
|
||||
}
|
||||
.setPositiveButton(R.string.apply) { _, _ ->
|
||||
debug("Switching back to default channel")
|
||||
installSettings(Channel.default.settings)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
// Note: Do not use setOnCheckedChanged here because we don't want to be called when we programmatically disable editing
|
||||
binding.editableCheckbox.setOnClickListener { _ ->
|
||||
|
||||
/// We use this to determine if the user tried to install a custom name
|
||||
var originalName = ""
|
||||
|
||||
val checked = binding.editableCheckbox.isChecked
|
||||
if (checked) {
|
||||
// User just unlocked for editing - remove the # goo around the channel name
|
||||
model.channels.value?.primaryChannel?.let { ch ->
|
||||
binding.channelNameEdit.setText(ch.name)
|
||||
// Note: We are careful to show the emptystring here if the user was on a default channel, so the user knows they should it for any changes
|
||||
originalName = ch.settings.name
|
||||
binding.channelNameEdit.setText(originalName)
|
||||
}
|
||||
} else {
|
||||
// User just locked it, we should warn and then apply changes to radio
|
||||
|
|
@ -170,49 +227,33 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
// Generate a new channel with only the changes the user can change in the GUI
|
||||
model.channels.value?.primaryChannel?.let { oldPrimary ->
|
||||
var newSettings = oldPrimary.settings.toBuilder()
|
||||
newSettings.name = binding.channelNameEdit.text.toString().trim()
|
||||
val newName = binding.channelNameEdit.text.toString().trim()
|
||||
|
||||
// Generate a new AES256 key unleess the user is trying to go back to stock
|
||||
if (!newSettings.name.equals(
|
||||
Channel.defaultChannel.name,
|
||||
ignoreCase = true
|
||||
)
|
||||
) {
|
||||
// Find the new modem config
|
||||
val selectedChannelOptionString =
|
||||
binding.filledExposedDropdown.editableText.toString()
|
||||
var modemConfig = getModemConfig(selectedChannelOptionString)
|
||||
if (modemConfig == ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED) // Huh? didn't find it - keep same
|
||||
modemConfig = oldPrimary.settings.modemConfig
|
||||
|
||||
// Generate a new AES256 key if the user changes channel name or the name is non-default and the settings changed
|
||||
if (newName != originalName || (newName.isNotEmpty() && modemConfig != oldPrimary.settings.modemConfig)) {
|
||||
// Install a new customized channel
|
||||
debug("ASSIGNING NEW AES256 KEY")
|
||||
val random = SecureRandom()
|
||||
val bytes = ByteArray(32)
|
||||
random.nextBytes(bytes)
|
||||
newSettings.name = newName
|
||||
newSettings.psk = ByteString.copyFrom(bytes)
|
||||
} else {
|
||||
debug("Switching back to default channel")
|
||||
newSettings = Channel.defaultChannel.settings.toBuilder()
|
||||
newSettings = Channel.default.settings.toBuilder()
|
||||
}
|
||||
|
||||
val selectedChannelOptionString =
|
||||
binding.filledExposedDropdown.editableText.toString()
|
||||
val modemConfig = getModemConfig(selectedChannelOptionString)
|
||||
// No matter what apply the speed selection from the user
|
||||
newSettings.modemConfig = modemConfig
|
||||
|
||||
if (modemConfig != ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED)
|
||||
newSettings.modemConfig = modemConfig
|
||||
|
||||
val newChannel = newSettings.build()
|
||||
val newSet = ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
|
||||
// Try to change the radio, if it fails, tell the user why and throw away their redits
|
||||
try {
|
||||
model.setChannels(newSet)
|
||||
// Since we are writing to radioconfig, that will trigger the rest of the GUI update (QR code etc)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("ignoring channel problem", ex)
|
||||
|
||||
setGUIfromModel() // Throw away user edits
|
||||
|
||||
// Tell the user to try again
|
||||
Snackbar.make(
|
||||
binding.editableCheckbox,
|
||||
R.string.radio_sleeping,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
installSettings(newSettings.build())
|
||||
}
|
||||
}
|
||||
.show()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.geeksville.mesh.model.UIViewModel
|
|||
class DebugFragment : Fragment() {
|
||||
|
||||
private var _binding: DebugFragmentBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
|
|
@ -44,11 +45,11 @@ class DebugFragment : Fragment() {
|
|||
model.deleteAllPacket()
|
||||
}
|
||||
|
||||
binding.closeButton.setOnClickListener{
|
||||
parentFragmentManager.popBackStack();
|
||||
binding.closeButton.setOnClickListener {
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
model.allPackets.observe(viewLifecycleOwner, Observer {
|
||||
packets -> packets?.let { adapter.setPackets(it) }
|
||||
model.allPackets.observe(viewLifecycleOwner, Observer { packets ->
|
||||
packets?.let { adapter.setPackets(it) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -131,8 +131,10 @@ fun positionToMeter(a: MeshProtos.Position, b: MeshProtos.Position): Double {
|
|||
a.latitudeI * 1e-7,
|
||||
a.longitudeI * 1e-7,
|
||||
b.latitudeI * 1e-7,
|
||||
b.longitudeI * 1e-7)
|
||||
b.longitudeI * 1e-7
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert degrees/mins/secs to a single double
|
||||
*
|
||||
|
|
@ -186,7 +188,7 @@ fun bearing(
|
|||
val y = sin(deltaLonRad) * cos(lat2Rad)
|
||||
val x =
|
||||
cos(lat1Rad) * sin(lat2Rad) - (sin(lat1Rad) * cos(lat2Rad)
|
||||
* Math.cos(deltaLonRad))
|
||||
* Math.cos(deltaLonRad))
|
||||
return radToBearing(Math.atan2(y, x))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.geeksville.android.Logging
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.databinding.AdapterMessageLayoutBinding
|
||||
import com.geeksville.mesh.databinding.MessagesFragmentBinding
|
||||
|
|
@ -25,7 +24,6 @@ import com.geeksville.mesh.model.UIViewModel
|
|||
import com.geeksville.mesh.service.MeshService
|
||||
import com.google.android.material.chip.Chip
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.util.*
|
||||
|
||||
// Allows usage like email.on(EditorInfo.IME_ACTION_NEXT, { confirm() })
|
||||
|
|
@ -54,9 +52,9 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
|
|||
private val timeFormat: DateFormat =
|
||||
DateFormat.getTimeInstance(DateFormat.SHORT)
|
||||
|
||||
private fun getShortDateTime(time : Date): String {
|
||||
private fun getShortDateTime(time: Date): String {
|
||||
// return time if within 24 hours, otherwise date/time
|
||||
val one_day = 60*60*24*100L
|
||||
val one_day = 60 * 60 * 24 * 100L
|
||||
if (System.currentTimeMillis() - time.time > one_day) {
|
||||
return dateTimeFormat.format(time)
|
||||
} else return timeFormat.format(time)
|
||||
|
|
@ -152,11 +150,25 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
|
|||
if (isMe) {
|
||||
marginParams.leftMargin = messageOffset
|
||||
marginParams.rightMargin = 0
|
||||
context?.let{ holder.card.setCardBackgroundColor(ContextCompat.getColor(it, R.color.colorMyMsg)) }
|
||||
context?.let {
|
||||
holder.card.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
it,
|
||||
R.color.colorMyMsg
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
marginParams.rightMargin = messageOffset
|
||||
marginParams.leftMargin = 0
|
||||
context?.let{ holder.card.setCardBackgroundColor(ContextCompat.getColor(it, R.color.colorMsg)) }
|
||||
context?.let {
|
||||
holder.card.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
it,
|
||||
R.color.colorMsg
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// Hide the username chip for my messages
|
||||
if (isMe) {
|
||||
|
|
@ -240,20 +252,26 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
|
|||
// If connection state _OR_ myID changes we have to fix our ability to edit outgoing messages
|
||||
fun updateTextEnabled() {
|
||||
binding.textInputLayout.isEnabled =
|
||||
model.isConnected.value != MeshService.ConnectionState.DISCONNECTED && model.nodeDB.myId.value != null && model.radioConfig.value != null
|
||||
model.isConnected.value != MeshService.ConnectionState.DISCONNECTED
|
||||
|
||||
// Just being connected is enough to allow sending texts I think
|
||||
// && model.nodeDB.myId.value != null && model.radioConfig.value != null
|
||||
}
|
||||
|
||||
model.isConnected.observe(viewLifecycleOwner, Observer { _ ->
|
||||
// If we don't know our node ID and we are offline don't let user try to send
|
||||
updateTextEnabled() })
|
||||
updateTextEnabled()
|
||||
})
|
||||
|
||||
model.nodeDB.myId.observe(viewLifecycleOwner, Observer { _ ->
|
||||
/* model.nodeDB.myId.observe(viewLifecycleOwner, Observer { _ ->
|
||||
// If we don't know our node ID and we are offline don't let user try to send
|
||||
updateTextEnabled() })
|
||||
updateTextEnabled()
|
||||
})
|
||||
|
||||
model.radioConfig.observe(viewLifecycleOwner, Observer { _ ->
|
||||
// If we don't know our node ID and we are offline don't let user try to send
|
||||
updateTextEnabled() })
|
||||
updateTextEnabled()
|
||||
}) */
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,50 @@
|
|||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.content.Context
|
||||
import java.text.DateFormat
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import java.util.*
|
||||
|
||||
class PacketListAdapter internal constructor(
|
||||
context: Context
|
||||
) : RecyclerView.Adapter<PacketListAdapter.PacketViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
private var packets = emptyList<Packet>()
|
||||
|
||||
private val timeFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
|
||||
inner class PacketViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val packetTypeView: TextView = itemView.findViewById(R.id.type)
|
||||
val packetDateReceivedView: TextView = itemView.findViewById(R.id.dateReceived)
|
||||
val packetRawMessage : TextView = itemView.findViewById(R.id.rawMessage)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PacketViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.adapter_packet_layout, parent, false)
|
||||
return PacketViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PacketViewHolder, position: Int) {
|
||||
val current = packets[position]
|
||||
holder.packetTypeView.text = current.message_type
|
||||
holder.packetRawMessage.text = current.raw_message
|
||||
val date = Date(current.received_date)
|
||||
holder.packetDateReceivedView.text = timeFormat.format(date)
|
||||
}
|
||||
|
||||
internal fun setPackets(packets: List<Packet>) {
|
||||
this.packets = packets
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = packets.size
|
||||
|
||||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
class PacketListAdapter internal constructor(
|
||||
context: Context
|
||||
) : RecyclerView.Adapter<PacketListAdapter.PacketViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
private var packets = emptyList<Packet>()
|
||||
|
||||
private val timeFormat: DateFormat =
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
|
||||
inner class PacketViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val packetTypeView: TextView = itemView.findViewById(R.id.type)
|
||||
val packetDateReceivedView: TextView = itemView.findViewById(R.id.dateReceived)
|
||||
val packetRawMessage: TextView = itemView.findViewById(R.id.rawMessage)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PacketViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.adapter_packet_layout, parent, false)
|
||||
return PacketViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PacketViewHolder, position: Int) {
|
||||
val current = packets[position]
|
||||
holder.packetTypeView.text = current.message_type
|
||||
holder.packetRawMessage.text = current.raw_message
|
||||
val date = Date(current.received_date)
|
||||
holder.packetDateReceivedView.text = timeFormat.format(date)
|
||||
}
|
||||
|
||||
internal fun setPackets(packets: List<Packet>) {
|
||||
this.packets = packets
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = packets.size
|
||||
|
||||
}
|
||||
|
|
@ -38,10 +38,7 @@ import com.geeksville.mesh.android.bluetoothManager
|
|||
import com.geeksville.mesh.android.usbManager
|
||||
import com.geeksville.mesh.databinding.SettingsFragmentBinding
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.service.BluetoothInterface
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.service.RadioInterfaceService
|
||||
import com.geeksville.mesh.service.SerialInterface
|
||||
import com.geeksville.mesh.service.*
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ACTION_UPDATE_PROGRESS
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ProgressNotStarted
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ProgressSuccess
|
||||
|
|
@ -59,7 +56,7 @@ import kotlinx.coroutines.Job
|
|||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
object SLogging : Logging {}
|
||||
object SLogging : Logging
|
||||
|
||||
/// Change to a new macaddr selection, updating GUI and radio
|
||||
fun changeDeviceSelection(context: MainActivity, newAddr: String?) {
|
||||
|
|
@ -186,7 +183,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
|||
// if that device later disconnects remove it as a candidate
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
|
||||
if ((result.device.name?.startsWith("Mesh") ?: false)) {
|
||||
if ((result.device.name?.startsWith("Mesh") == true)) {
|
||||
val addr = result.device.address
|
||||
val fullAddr = "x$addr" // full address with the bluetooh prefix
|
||||
// prevent logspam because weill get get lots of redundant scan results
|
||||
|
|
@ -245,14 +242,12 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
|||
debug("BTScan component active")
|
||||
selectedAddress = RadioInterfaceService.getDeviceAddress(context)
|
||||
|
||||
return if (bluetoothAdapter == null || RadioInterfaceService.isMockInterfaceAvailable(
|
||||
context
|
||||
)
|
||||
) {
|
||||
return if (bluetoothAdapter == null || MockInterface.addressValid(context, "")) {
|
||||
warn("No bluetooth adapter. Running under emulation?")
|
||||
|
||||
val testnodes = listOf(
|
||||
DeviceListEntry("Simulated interface", "m", true),
|
||||
DeviceListEntry("Included simulator", "m", true),
|
||||
DeviceListEntry("Complete simulator", "t10.0.2.2", true),
|
||||
DeviceListEntry(context.getString(R.string.none), "n", true)
|
||||
/* Don't populate fake bluetooth devices, because we don't want testlab inside of google
|
||||
to try and use them.
|
||||
|
|
@ -494,7 +489,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
|
||||
/// Set the correct update button configuration based on current progress
|
||||
private fun refreshUpdateButton() {
|
||||
private fun refreshUpdateButton(enable: Boolean) {
|
||||
debug("Reiniting the udpate button")
|
||||
val info = model.myNodeInfo.value
|
||||
val service = model.meshService
|
||||
|
|
@ -505,7 +500,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
|
||||
val progress = service.updateStatus
|
||||
|
||||
binding.updateFirmwareButton.isEnabled =
|
||||
binding.updateFirmwareButton.isEnabled = enable &&
|
||||
(progress < 0) // if currently doing an upgrade disable button
|
||||
|
||||
if (progress >= 0) {
|
||||
|
|
@ -542,7 +537,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
val connected = model.isConnected.value
|
||||
|
||||
val isConnected = connected == MeshService.ConnectionState.CONNECTED
|
||||
binding.nodeSettings.visibility = if(isConnected) View.VISIBLE else View.GONE
|
||||
binding.nodeSettings.visibility = if (isConnected) View.VISIBLE else View.GONE
|
||||
|
||||
if (connected == MeshService.ConnectionState.DISCONNECTED)
|
||||
model.ownerName.value = ""
|
||||
|
|
@ -552,25 +547,19 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
val spinner = binding.regionSpinner
|
||||
val unsetIndex = regions.indexOf(RadioConfigProtos.RegionCode.Unset.name)
|
||||
spinner.onItemSelectedListener = null
|
||||
if(region != null) {
|
||||
debug("current region is $region")
|
||||
var regionIndex = regions.indexOf(region.name)
|
||||
if(regionIndex == -1) // Not found, probably because the device has a region our app doesn't yet understand. Punt and say Unset
|
||||
regionIndex = unsetIndex
|
||||
|
||||
// We don't want to be notified of our own changes, so turn off listener while making them
|
||||
spinner.setSelection(regionIndex, false)
|
||||
spinner.onItemSelectedListener = regionSpinnerListener
|
||||
spinner.isEnabled = true
|
||||
}
|
||||
else {
|
||||
warn("region is unset!")
|
||||
spinner.setSelection(unsetIndex, false)
|
||||
spinner.isEnabled = false // leave disabled, because we can't get our region
|
||||
}
|
||||
debug("current region is $region")
|
||||
var regionIndex = regions.indexOf(region.name)
|
||||
if (regionIndex == -1) // Not found, probably because the device has a region our app doesn't yet understand. Punt and say Unset
|
||||
regionIndex = unsetIndex
|
||||
|
||||
// We don't want to be notified of our own changes, so turn off listener while making them
|
||||
spinner.setSelection(regionIndex, false)
|
||||
spinner.onItemSelectedListener = regionSpinnerListener
|
||||
spinner.isEnabled = true
|
||||
|
||||
// If actively connected possibly let the user update firmware
|
||||
refreshUpdateButton()
|
||||
refreshUpdateButton(region != RadioConfigProtos.RegionCode.Unset)
|
||||
|
||||
// Update the status string (highest priority messages first)
|
||||
val info = model.myNodeInfo.value
|
||||
|
|
@ -590,14 +579,14 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private val regionSpinnerListener = object : AdapterView.OnItemSelectedListener{
|
||||
private val regionSpinnerListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>,
|
||||
view: View,
|
||||
position: Int,
|
||||
id: Long
|
||||
) {
|
||||
val item = parent.getItemAtPosition(position) as String
|
||||
val item = parent.getItemAtPosition(position) as String?
|
||||
val asProto = item!!.let { RadioConfigProtos.RegionCode.valueOf(it) }
|
||||
exceptionToSnackbar(requireView()) {
|
||||
model.region = asProto
|
||||
|
|
@ -622,7 +611,8 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
|
||||
// init our region spinner
|
||||
val spinner = binding.regionSpinner
|
||||
val regionAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, regions)
|
||||
val regionAdapter =
|
||||
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, regions)
|
||||
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
spinner.adapter = regionAdapter
|
||||
|
||||
|
|
@ -632,7 +622,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
|
||||
|
||||
// Only let user edit their name or set software update while connected to a radio
|
||||
model.isConnected.observe(viewLifecycleOwner, Observer { connectionState ->
|
||||
model.isConnected.observe(viewLifecycleOwner, Observer { _ ->
|
||||
updateNodeInfo()
|
||||
})
|
||||
|
||||
|
|
@ -702,7 +692,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
scanModel.onSelected(requireActivity() as MainActivity, device)
|
||||
|
||||
if (!b.isSelected)
|
||||
binding.scanStatusText.setText(getString(R.string.please_pair))
|
||||
binding.scanStatusText.text = getString(R.string.please_pair)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -765,7 +755,10 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
// get rid of the warning text once at least one device is paired.
|
||||
// If we are running on an emulator, always leave this message showing so we can test the worst case layout
|
||||
binding.warningNotPaired.visibility =
|
||||
if (hasBonded && !RadioInterfaceService.isMockInterfaceAvailable(requireContext())) View.GONE else View.VISIBLE
|
||||
if (hasBonded && !MockInterface.addressValid(requireContext(), ""))
|
||||
View.GONE
|
||||
else
|
||||
View.VISIBLE
|
||||
}
|
||||
|
||||
/// Setup the GUI to do a classic (pre SDK 26 BLE scan)
|
||||
|
|
@ -935,7 +928,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
|
||||
private val updateProgressReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
refreshUpdateButton()
|
||||
refreshUpdateButton(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
val name = n.user?.longName ?: n.user?.id ?: "Unknown node"
|
||||
holder.nodeNameView.text = name
|
||||
|
||||
val pos = n.validPosition;
|
||||
val pos = n.validPosition
|
||||
if (pos != null) {
|
||||
val coords =
|
||||
String.format("%.5f %.5f", pos.latitude, pos.longitude).replace(",", ".")
|
||||
|
|
@ -141,7 +141,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
}
|
||||
renderBattery(n.batteryPctLevel, holder)
|
||||
|
||||
holder.lastTime.text = formatAgo(n.lastSeen);
|
||||
holder.lastTime.text = formatAgo(n.lastHeard)
|
||||
|
||||
if ((n.num == ourNodeInfo?.num) || (n.snr > 100f)) {
|
||||
holder.signalView.visibility = View.INVISIBLE
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue