From 5546e74b46f35562601e1be43d5f33a800088a70 Mon Sep 17 00:00:00 2001 From: Mike Cumings Date: Fri, 20 May 2022 14:26:03 -0700 Subject: [PATCH] Extract `RadioInterfaceService` companion object functions In preparation for replacing the `InterfaceFactory` with an injectable form we need to convert static methods that call `InterfaceFactory` into non-static, injected forms. Also: - Updated kotlin `-Xopt-in` to `-opt-in` to remove build time warnings. - Removed some unused `RadioInterfaceService` code. --- app/build.gradle | 2 +- .../java/com/geeksville/mesh/MainActivity.kt | 5 +- .../repository/radio/RadioInterfaceService.kt | 118 +++++++----------- .../repository/radio/RadioRepositoryModule.kt | 19 +++ .../radio/RadioRepositoryQualifier.kt | 9 ++ .../geeksville/mesh/service/MeshService.kt | 8 +- .../geeksville/mesh/ui/SettingsFragment.kt | 9 +- 7 files changed, 82 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt create mode 100644 app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryQualifier.kt diff --git a/app/build.gradle b/app/build.gradle index 3a832a351..27b21c59f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -88,7 +88,7 @@ android { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs += [ '-Xopt-in=kotlin.RequiresOptIn' ] + freeCompilerArgs += [ '-opt-in=kotlin.RequiresOptIn' ] } lint { abortOnError false diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index b4d0215be..508ce8396 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -42,7 +42,6 @@ import com.geeksville.mesh.model.DeviceVersion import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.repository.radio.RadioInterfaceService import com.geeksville.mesh.repository.radio.SerialInterface -import com.geeksville.mesh.repository.usb.UsbRepository import com.geeksville.mesh.service.* import com.geeksville.mesh.ui.* import com.geeksville.util.Exceptions @@ -140,7 +139,7 @@ class MainActivity : BaseActivity(), Logging, val model: UIViewModel by viewModels() @Inject - internal lateinit var usbRepository: UsbRepository + internal lateinit var radioInterfaceService: RadioInterfaceService data class TabInfo(val text: String, val icon: Int, val content: Fragment) @@ -982,7 +981,7 @@ class MainActivity : BaseActivity(), Logging, errormsg("Bind of MeshService failed") } - val bonded = RadioInterfaceService.getBondedDeviceAddress(this, usbRepository) != null + val bonded = radioInterfaceService.getBondedDeviceAddress() != null if (!bonded && usbDevice == null) // we will handle USB later showSettingsPage() } diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt index 902784f37..35afe97fb 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt @@ -2,8 +2,6 @@ package com.geeksville.mesh.repository.radio import android.annotation.SuppressLint import android.app.Application -import android.content.Context -import android.content.Intent import android.content.SharedPreferences import androidx.core.content.edit import androidx.lifecycle.Lifecycle @@ -15,8 +13,6 @@ import com.geeksville.concurrent.handledLaunch import com.geeksville.mesh.CoroutineDispatchers import com.geeksville.mesh.repository.bluetooth.BluetoothRepository import com.geeksville.mesh.repository.usb.UsbRepository -import com.geeksville.mesh.service.EXTRA_PAYLOAD -import com.geeksville.mesh.service.prefix import com.geeksville.util.anonymize import com.geeksville.util.ignoreException import com.geeksville.util.toRemoteExceptions @@ -42,7 +38,8 @@ class RadioInterfaceService @Inject constructor( private val dispatchers: CoroutineDispatchers, private val bluetoothRepository: BluetoothRepository, private val processLifecycle: Lifecycle, - private val usbRepository: UsbRepository + private val usbRepository: UsbRepository, + @RadioRepositoryQualifier private val prefs: SharedPreferences ): Logging { private val _connectionState = MutableStateFlow(RadioServiceConnectionState()) @@ -85,17 +82,6 @@ class RadioInterfaceService @Inject constructor( } companion object : Logging { - /** - * The RECEIVED_FROMRADIO - * Payload will be the raw bytes which were contained within a MeshProtos.FromRadio protobuf - */ - const val RECEIVE_FROMRADIO_ACTION = "$prefix.RECEIVE_FROMRADIO" - - /** - * This is broadcast when connection state changed - */ - const val RADIO_CONNECTED_ACTION = "$prefix.CONNECT_CHANGED" - const val DEVADDR_KEY = "devAddr2" // the new name for devaddr init { @@ -109,61 +95,49 @@ class RadioInterfaceService @Inject constructor( ) info("Using ${factories.size} interface factories") } + } - /// This is public only so that SimRadio can bootstrap our message flow - fun broadcastReceivedFromRadio(context: Context, payload: ByteArray) { - val intent = Intent(RECEIVE_FROMRADIO_ACTION) - intent.putExtra(EXTRA_PAYLOAD, payload) - context.sendBroadcast(intent) - } - - fun getPrefs(context: Context): SharedPreferences = - context.getSharedPreferences("radio-prefs", Context.MODE_PRIVATE) - - /** Return the device we are configured to use, or null for none - * device address strings are of the form: - * - * at - * - * 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 getDeviceAddress(context: Context, usbRepository: UsbRepository): String? { - // If the user has unpaired our device, treat things as if we don't have one - val prefs = getPrefs(context) - 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 && MockInterface.addressValid(context, usbRepository, "")) - address = MockInterface.prefix.toString() - - return address - } - - /** Like getDeviceAddress, but filtered to return only devices we are currently bonded with - * - * at - * - * 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(context: Context, usbRepository: UsbRepository): String? { - // If the user has unpaired our device, treat things as if we don't have one - val address = getDeviceAddress(context, usbRepository) - - /// Interfaces can filter addresses to indicate that address is no longer acceptable - if (address != null) { - val c = address[0] - val rest = address.substring(1) - val isValid = InterfaceFactory.getFactory(c) - ?.addressValid(context, usbRepository, rest) ?: false - if (!isValid) - return null - } - return address + /** Return the device we are configured to use, or null for none + * device address strings are of the form: + * + * at + * + * where a is either x for bluetooth or s for serial + * and t is an interface specific address (macaddr or a device path) + */ + fun getDeviceAddress(): String? { + // If the user has unpaired our device, treat things as if we don't have one + 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 && MockInterface.addressValid(context, usbRepository, "")) + address = MockInterface.prefix.toString() + + return address + } + + /** Like getDeviceAddress, but filtered to return only devices we are currently bonded with + * + * at + * + * 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() + + /// Interfaces can filter addresses to indicate that address is no longer acceptable + if (address != null) { + val c = address[0] + val rest = address.substring(1) + val isValid = InterfaceFactory.getFactory(c) + ?.addressValid(context, usbRepository, rest) ?: false + if (!isValid) + return null } + return address } private fun broadcastConnectionChanged(isConnected: Boolean, isPermanent: Boolean) { @@ -214,7 +188,7 @@ class RadioInterfaceService @Inject constructor( if (radioIf !is NopInterface) warn("Can't start interface - $radioIf is already running") else { - val address = getBondedDeviceAddress(context, usbRepository) + val address = getBondedDeviceAddress() if (address == null) warn("No bonded mesh radio, can't start interface") else { @@ -263,7 +237,7 @@ class RadioInterfaceService @Inject constructor( */ @SuppressLint("NewApi") private fun setBondedDeviceAddress(address: String?): Boolean { - return if (getBondedDeviceAddress(context, usbRepository) == address && isStarted) { + return if (getBondedDeviceAddress() == address && isStarted) { warn("Ignoring setBondedDevice ${address.anonymize}, because we are already using that device") false } else { @@ -281,7 +255,7 @@ class RadioInterfaceService @Inject constructor( debug("Setting bonded device to ${address.anonymize}") - getPrefs(context).edit { + prefs.edit { if (address == null) this.remove(DEVADDR_KEY) else diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt new file mode 100644 index 000000000..611e39f1f --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt @@ -0,0 +1,19 @@ +package com.geeksville.mesh.repository.radio + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +object RadioRepositoryModule { + @Provides + @RadioRepositoryQualifier + fun provideSharedPreferences(application: Application): SharedPreferences { + return application.getSharedPreferences("radio-prefs", Context.MODE_PRIVATE) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryQualifier.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryQualifier.kt new file mode 100644 index 000000000..223d83b30 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryQualifier.kt @@ -0,0 +1,9 @@ +package com.geeksville.mesh.repository.radio + +import javax.inject.Qualifier + +/** + * Qualifier to distinguish radio repository- specific object instances. + */ +@Qualifier +annotation class RadioRepositoryQualifier 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 b40c0cb36..6b3b4ff86 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -22,7 +22,6 @@ import com.geeksville.mesh.repository.location.LocationRepository import com.geeksville.mesh.repository.radio.BluetoothInterface import com.geeksville.mesh.repository.radio.RadioInterfaceService import com.geeksville.mesh.repository.radio.RadioServiceConnectionState -import com.geeksville.mesh.repository.usb.UsbRepository import com.geeksville.util.* import com.google.protobuf.ByteString import com.google.protobuf.InvalidProtocolBufferException @@ -55,9 +54,6 @@ class MeshService : Service(), Logging { @Inject lateinit var radioInterfaceService: RadioInterfaceService - @Inject - lateinit var usbRepository: Lazy - @Inject lateinit var locationRepository: LocationRepository @@ -211,7 +207,7 @@ class MeshService : Service(), Logging { * tell android not to kill us */ private fun startForeground() { - val a = RadioInterfaceService.getBondedDeviceAddress(this, usbRepository.get()) + val a = radioInterfaceService.getBondedDeviceAddress() val wantForeground = a != null && a != "n" info("Requesting foreground service=$wantForeground") @@ -1180,7 +1176,7 @@ class MeshService : Service(), Logging { private fun regenMyNodeInfo() { val myInfo = rawMyNodeInfo if (myInfo != null) { - val a = RadioInterfaceService.getBondedDeviceAddress(this, usbRepository.get()) + val a = radioInterfaceService.getBondedDeviceAddress() val isBluetoothInterface = a != null && a.startsWith("x") val nodeNum = 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 31d0bcf12..4476d91a0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -4,11 +4,7 @@ import android.annotation.SuppressLint import android.app.Application import android.app.PendingIntent import android.bluetooth.BluetoothDevice -import android.bluetooth.le.BluetoothLeScanner -import android.bluetooth.le.ScanCallback -import android.bluetooth.le.ScanSettings -import android.bluetooth.le.ScanFilter -import android.bluetooth.le.ScanResult +import android.bluetooth.le.* import android.companion.AssociationRequest import android.companion.BluetoothDeviceFilter import android.companion.CompanionDeviceManager @@ -127,6 +123,7 @@ class BTScanModel @Inject constructor( private val bluetoothRepository: BluetoothRepository, private val usbRepository: UsbRepository, private val nsdRepository: NsdRepository, + private val radioInterfaceService: RadioInterfaceService, ) : ViewModel(), Logging { private val context: Context get() = application.applicationContext @@ -258,7 +255,7 @@ class BTScanModel @Inject constructor( * returns true if we could start scanning, false otherwise */ fun setupScan(): Boolean { - selectedAddress = RadioInterfaceService.getDeviceAddress(context, usbRepository) + selectedAddress = radioInterfaceService.getDeviceAddress() return if (bluetoothAdapter == null || MockInterface.addressValid(context, usbRepository, "")) { warn("No bluetooth adapter. Running under emulation?")