refactor: move RadioInterfaceService out of Activity / Fragment

This commit is contained in:
andrekir 2023-11-03 19:01:19 -03:00
parent 94507195a8
commit 3922bfbffb
6 changed files with 28 additions and 64 deletions

View file

@ -5,7 +5,6 @@ import android.bluetooth.BluetoothAdapter
import android.content.*
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.net.Uri
import android.os.Bundle
@ -38,13 +37,10 @@ import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.model.primaryChannel
import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.repository.radio.BluetoothInterface
import com.geeksville.mesh.repository.radio.InterfaceId
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.service.*
import com.geeksville.mesh.ui.*
import com.geeksville.mesh.ui.map.MapFragment
import com.geeksville.mesh.util.Exceptions
import com.geeksville.mesh.util.getParcelableExtraCompat
import com.geeksville.mesh.util.LanguageUtils
import com.geeksville.mesh.util.getPackageInfoCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -122,9 +118,6 @@ class MainActivity : AppCompatActivity(), Logging {
@Inject
internal lateinit var serviceRepository: ServiceRepository
@Inject
internal lateinit var radioInterfaceService: RadioInterfaceService
private val bluetoothPermissionsLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
if (result.entries.all { it.value }) {
@ -252,9 +245,6 @@ class MainActivity : AppCompatActivity(), Logging {
private var requestedChannelUrl: Uri? = null
/** We keep the usb device here, so later we can give it to our service */
private var usbDevice: UsbDevice? = null
/// Handle any itents that were passed into us
private fun handleIntent(intent: Intent) {
val appLinkAction = intent.action
@ -272,11 +262,7 @@ class MainActivity : AppCompatActivity(), Logging {
}
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
val device: UsbDevice? = intent.getParcelableExtraCompat(UsbManager.EXTRA_DEVICE)
if (device != null) {
debug("Handle USB device attached! $device")
usbDevice = device
}
showSettingsPage()
}
Intent.ACTION_MAIN -> {
@ -464,13 +450,6 @@ class MainActivity : AppCompatActivity(), Logging {
serviceRepository.setMeshService(service)
try {
usbDevice?.let { usb ->
debug("Switching to USB radio ${usb.deviceName}")
val address = radioInterfaceService.toInterfaceAddress(InterfaceId.SERIAL, usb.deviceName)
service.setDeviceAddress(address)
usbDevice = null // Only switch once - thereafter it should be stored in settings
}
val connectionState =
MeshService.ConnectionState.valueOf(service.connectionState())
@ -593,8 +572,7 @@ class MainActivity : AppCompatActivity(), Logging {
}
val bonded = model.bondedAddress != null
if (!bonded && usbDevice == null) // we will handle USB later
showSettingsPage()
if (!bonded) showSettingsPage()
}
private fun showSettingsPage() {

View file

@ -47,6 +47,10 @@ class BTScanModel @Inject constructor(
private val bleDevices = MutableLiveData<List<BluetoothDevice>>(listOf())
private val usbDevices = MutableLiveData<Map<String, UsbSerialDriver>>(mapOf())
val isMockInterfaceAddressValid: Boolean by lazy {
radioInterfaceService.isAddressValid(radioInterfaceService.mockInterfaceAddress)
}
init {
combine(
bluetoothRepository.state,
@ -180,7 +184,7 @@ class BTScanModel @Inject constructor(
private fun setupScan(): Boolean {
selectedAddress = radioInterfaceService.getDeviceAddress()
return if (radioInterfaceService.isAddressValid(radioInterfaceService.mockInterfaceAddress)) {
return if (isMockInterfaceAddressValid) {
warn("Running under emulator/test lab")
val testnodes = listOf(

View file

@ -8,11 +8,12 @@ enum class InterfaceId(val id: Char) {
MOCK('m'),
NOP('n'),
SERIAL('s'),
TCP('t');
TCP('t'),
;
companion object {
fun forIdChar(id: Char): InterfaceId? {
return values().firstOrNull { it.id == id }
return entries.firstOrNull { it.id == id }
}
}
}

View file

@ -1,6 +1,5 @@
package com.geeksville.mesh.repository.radio
import android.annotation.SuppressLint
import android.app.Application
import android.content.SharedPreferences
import androidx.core.content.edit
@ -16,7 +15,10 @@ import com.geeksville.mesh.repository.nsd.NsdRepository
import com.geeksville.mesh.util.anonymize
import com.geeksville.mesh.util.ignoreException
import com.geeksville.mesh.util.toRemoteExceptions
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
@ -46,7 +48,6 @@ class RadioInterfaceService @Inject constructor(
private val processLifecycle: Lifecycle,
@RadioRepositoryQualifier private val prefs: SharedPreferences,
private val interfaceFactory: InterfaceFactory,
private val mockInterfaceSpec: MockInterfaceSpec
) : Logging {
private val _connectionState = MutableStateFlow(RadioServiceConnectionState())
@ -132,8 +133,9 @@ class RadioInterfaceService @Inject constructor(
var address = prefs.getString(DEVADDR_KEY, null)
// If we are running on the emulator we default to the mock interface, so we can have some data to show to the user
if (address == null && mockInterfaceSpec.addressValid(""))
address = "${InterfaceId.MOCK.id}"
if (address == null && isAddressValid(mockInterfaceAddress)) {
address = mockInterfaceAddress
}
return address
}
@ -145,7 +147,6 @@ class RadioInterfaceService @Inject constructor(
* where a is either x for bluetooth or s for serial
* and t is an interface specific address (macaddr or a device path)
*/
@SuppressLint("NewApi")
fun getBondedDeviceAddress(): String? {
// If the user has unpaired our device, treat things as if we don't have one
val address = getDeviceAddress()
@ -248,7 +249,6 @@ class RadioInterfaceService @Inject constructor(
*
* @return true if the device changed, false if no change
*/
@SuppressLint("NewApi")
private fun setBondedDeviceAddress(address: String?): Boolean {
return if (getBondedDeviceAddress() == address && isStarted) {
warn("Ignoring setBondedDevice ${address.anonymize}, because we are already using that device")

View file

@ -34,7 +34,7 @@ class SerialInterface @AssistedInject constructor(
errormsg("Can't find device")
} else {
info("Opening $device")
val onConnect: () -> Unit = { super.connect() }
val onConnect: () -> Unit = { super.connect() }
usbRepository.createSerialConnection(device, object : SerialConnectionListener {
override fun onMissingPermission() {
errormsg("Need permissions for port")

View file

@ -8,7 +8,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.os.Handler
@ -41,7 +40,6 @@ import com.geeksville.mesh.model.BluetoothViewModel
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.model.getInitials
import com.geeksville.mesh.repository.location.LocationRepository
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.service.SoftwareUpdateService
import com.geeksville.mesh.util.PendingIntentCompat
@ -66,9 +64,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
private val bluetoothViewModel: BluetoothViewModel by activityViewModels()
private val model: UIViewModel by activityViewModels()
@Inject
internal lateinit var radioInterfaceServiceLazy: dagger.Lazy<RadioInterfaceService>
@Inject
internal lateinit var locationRepository: LocationRepository
@ -502,8 +497,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
// If we are running on an emulator, always leave this message showing so we can test the worst case layout
val curRadio = scanModel.selectedAddress
val radioInterfaceService = radioInterfaceServiceLazy.get()
if (curRadio != null && !radioInterfaceService.isAddressValid(radioInterfaceService.mockInterfaceAddress)) {
if (curRadio != null && !scanModel.isMockInterfaceAddressValid) {
binding.warningNotPaired.visibility = View.GONE
} else if (bluetoothViewModel.enabled.value == true) {
binding.warningNotPaired.visibility = View.VISIBLE
@ -574,22 +568,13 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (BTScanModel.ACTION_USB_PERMISSION == intent.action) {
if (BTScanModel.ACTION_USB_PERMISSION != intent.action) return
val device: UsbDevice? =
intent.getParcelableExtraCompat(UsbManager.EXTRA_DEVICE)
val deviceName: String = device?.deviceName ?: "unknown"
if (intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED,
false
)
) {
info("User approved USB access")
changeDeviceAddress(it.fullAddress)
} else {
errormsg("USB permission denied for device $deviceName")
}
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
info("User approved USB access")
changeDeviceAddress(it.fullAddress)
} else {
errormsg("USB permission denied for device ${it.address}")
}
// We don't need to stay registered
requireActivity().unregisterReceiver(this)
@ -600,7 +585,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
activity,
0,
Intent(BTScanModel.ACTION_USB_PERMISSION),
PendingIntentCompat.FLAG_IMMUTABLE
PendingIntentCompat.FLAG_MUTABLE
)
val filter = IntentFilter(BTScanModel.ACTION_USB_PERMISSION)
requireActivity().registerReceiver(usbReceiver, filter)
@ -622,12 +607,8 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
// We need this receiver to get informed when the bond attempt finished
val bondChangedReceiver = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) = exceptionReporter {
val state =
intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)
debug("Received bond state changed $state")
if (state != BluetoothDevice.BOND_BONDING) {