From 79ccd40ae1c4ec65e6626e4c3e2da6cdcae38cd8 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 10 Oct 2022 17:47:21 -0300 Subject: [PATCH 1/5] use locationManager method to determine gps exists or is disabled --- .../mesh/android/ContextServices.kt | 12 ++++++-- .../geeksville/mesh/ui/SettingsFragment.kt | 30 +++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt index 424229999..a187ccf9f 100644 --- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt +++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt @@ -3,6 +3,7 @@ package com.geeksville.mesh.android import android.Manifest import android.app.NotificationManager import android.bluetooth.BluetoothManager +import android.location.LocationManager import android.companion.CompanionDeviceManager import android.content.Context import android.content.pm.PackageManager @@ -29,6 +30,8 @@ val Context.usbManager: UsbManager get() = requireNotNull(getSystemService(Conte val Context.notificationManager: NotificationManager get() = requireNotNull(getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager?) +val Context.locationManager: LocationManager get() = requireNotNull(getSystemService(Context.LOCATION_SERVICE) as? LocationManager?) + /** * @return true if CompanionDeviceManager API is present */ @@ -40,8 +43,13 @@ fun Context.hasCompanionDeviceApi(): Boolean = /** * @return true if the device has a GPS receiver */ -fun Context.hasGps(): Boolean = - packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) +fun Context.hasGps(): Boolean = locationManager.allProviders.contains(LocationManager.GPS_PROVIDER) + +/** + * @return true if the device has a GPS receiver and it is disabled (location turned off) + */ +fun Context.gpsDisabled(): Boolean = + if (hasGps()) !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) else false /** * return a list of the permissions we don't have diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index eb5e8cdec..3c9bae206 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -2,14 +2,22 @@ package com.geeksville.mesh.ui import android.bluetooth.BluetoothDevice import android.companion.CompanionDeviceManager -import android.content.* -import android.location.LocationManager -import android.os.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo -import android.widget.* +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.RadioButton +import android.widget.TextView +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -161,7 +169,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { if (connected == MeshService.ConnectionState.DISCONNECTED) model.setOwner("") - if (model.config.position.gpsEnabled) { + if (requireContext().hasGps() && model.config.position.gpsEnabled) { binding.provideLocationCheckbox.isEnabled = true } else { binding.provideLocationCheckbox.isChecked = false @@ -537,17 +545,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { // Default warning valid only for classic bluetooth scan warningReason: String = getString(R.string.location_disabled_warning) ) { - val locationManager = - myActivity.getSystemService(Context.LOCATION_SERVICE) as LocationManager - var gpsEnabled = false - - try { - gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) - } catch (ex: Throwable) { - debug("LocationManager GPS_PROVIDER error: ${ex.message}") - } - - if (myActivity.hasGps() && !gpsEnabled) { + if (requireContext().gpsDisabled()) { warn("Telling user we need need location access") showSnackbar(warningReason) } From 213164e95b9023410ea6c2d3c0b33c3d6a261e08 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 10 Oct 2022 18:06:19 -0300 Subject: [PATCH 2/5] requestConfig refactor --- .../com/geeksville/mesh/service/MeshService.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index dd5e2ce1f..a82c5e60b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1363,12 +1363,16 @@ class MeshService : Service(), Logging { warn("Ignoring stale config complete") } - private fun requestDeviceConfig() { - AdminProtos.AdminMessage.ConfigType.values().forEach { - sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) { - if (it != AdminProtos.AdminMessage.ConfigType.UNRECOGNIZED) getConfigRequest = it - }) - } + private fun requestConfig(config: AdminProtos.AdminMessage.ConfigType) { + sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) { + getConfigRequest = config + }) + } + + private fun requestAllConfig() { + AdminProtos.AdminMessage.ConfigType.values().filter { + it != AdminProtos.AdminMessage.ConfigType.UNRECOGNIZED + }.forEach(::requestConfig) } private fun requestChannel(channelIndex: Int) { From d9eb113b12687d7197ef0f442a742ffa6757e174 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 10 Oct 2022 18:09:20 -0300 Subject: [PATCH 3/5] add remote node admin --- .../main/java/com/geeksville/mesh/model/UIState.kt | 3 +++ .../com/geeksville/mesh/service/MeshService.kt | 14 ++++++++++---- .../com/geeksville/mesh/ui/ContactsFragment.kt | 4 ++-- .../java/com/geeksville/mesh/ui/UsersFragment.kt | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index d79ce445c..2404ff116 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -94,6 +94,7 @@ class UIViewModel @Inject constructor( private val _channels = MutableStateFlow(ChannelSet()) val channels: StateFlow = _channels + val channelSet get() = channels.value.protobuf private val _quickChatActions = MutableStateFlow>(emptyList()) val quickChatActions: StateFlow> = _quickChatActions @@ -355,6 +356,8 @@ class UIViewModel @Inject constructor( } } + val adminChannelIndex: Int get() = channelSet.settingsList.map { it.name }.indexOf("admin") + fun requestShutdown(idNum: Int) { try { meshService?.requestShutdown(idNum) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index a82c5e60b..d91466143 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -463,6 +463,9 @@ class MeshService : Service(), Logging { /// My node ID string private val myNodeID get() = toNodeID(myNodeNum) + /// Admin channel index + private var adminChannelIndex: Int = 0 + /// Convert the channels array into a ChannelSet private var channelSet: AppOnlyProtos.ChannelSet get() { @@ -543,6 +546,7 @@ class MeshService : Service(), Logging { initFn: AdminProtos.AdminMessage.Builder.() -> Unit ): MeshPacket = buildMeshPacket( wantAck = true, + channel = adminChannelIndex, priority = MeshPacket.Priority.RELIABLE ) { @@ -958,9 +962,10 @@ class MeshService : Service(), Logging { } } - private fun addChannelSettings(channel: ChannelProtos.Channel) { + private fun addChannelSettings(ch: ChannelProtos.Channel) { + if (ch.index == 0 || ch.settings.name == "admin") adminChannelIndex = ch.index serviceScope.handledLaunch { - channelSetRepository.addSettings(channel) + channelSetRepository.addSettings(ch) } } @@ -1381,9 +1386,10 @@ class MeshService : Service(), Logging { }) } - private fun setChannel(channel: ChannelProtos.Channel) { + private fun setChannel(ch: ChannelProtos.Channel) { + if (ch.index == 0 || ch.settings.name == "admin") adminChannelIndex = ch.index sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) { - setChannel = channel + setChannel = ch }) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/ContactsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ContactsFragment.kt index 199f7a81c..35bdd7b3c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ContactsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ContactsFragment.kt @@ -91,7 +91,7 @@ class ContactsFragment : ScreenFragment("Messages"), Logging { val node = nodes[if (fromLocal) contact.to else contact.from] //grab channel names from DeviceConfig - val channels = model.channels.value.protobuf + val channels = model.channelSet val channelName = if (channels.settingsCount > contact.channel) Channel(channels.settingsList[contact.channel], channels.loraConfig).name else null @@ -173,7 +173,7 @@ class ContactsFragment : ScreenFragment("Messages"), Logging { fun onContactsChanged(contacts: Map) { // Add empty channel placeholders (always show Broadcast contacts, even when empty) val mutableMap = contacts.toMutableMap() - for (ch in 0 until model.channels.value.protobuf.settingsCount) { + for (ch in 0 until model.channelSet.settingsCount) { val contactKey = "$ch${DataPacket.ID_BROADCAST}" if (mutableMap[contactKey] == null) mutableMap[contactKey] = Packet( 0L, 1, contactKey, 0L, diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt index edbccdfb5..ff6ff5114 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -56,7 +56,7 @@ class UsersFragment : ScreenFragment("Users"), Logging { 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 showAdmin = position == 0 || model.adminChannelIndex > 0 val popup = PopupMenu(requireContext(), view) popup.inflate(R.menu.menu_nodes) popup.menu.findItem(R.id.direct_message).isVisible = position > 0 From adb0b13f3fd694a491c63f8961c66b014e77fb99 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 10 Oct 2022 18:16:44 -0300 Subject: [PATCH 4/5] updating proto submodule to latest --- app/src/main/proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/proto b/app/src/main/proto index d3375fe59..d3dfaa63a 160000 --- a/app/src/main/proto +++ b/app/src/main/proto @@ -1 +1 @@ -Subproject commit d3375fe599da1dd48572354325d3186eccf6f449 +Subproject commit d3dfaa63a5108c1da7571cd780efaf561b99cc74 From 478b0be2f9b5092295c5add4eb3ac940ed7bdcfc Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 10 Oct 2022 18:37:54 -0300 Subject: [PATCH 5/5] increase QR code size --- app/src/main/java/com/geeksville/mesh/model/ChannelSet.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/ChannelSet.kt b/app/src/main/java/com/geeksville/mesh/model/ChannelSet.kt index 298f28cd6..9789bdb13 100644 --- a/app/src/main/java/com/geeksville/mesh/model/ChannelSet.kt +++ b/app/src/main/java/com/geeksville/mesh/model/ChannelSet.kt @@ -67,8 +67,8 @@ data class ChannelSet( multiFormatWriter.encode( getChannelUrl(true).toString(), BarcodeFormat.QR_CODE, - 192, - 192 + 960, + 960 ) val barcodeEncoder = BarcodeEncoder() barcodeEncoder.createBitmap(bitMatrix)