feat: remember recently entered IPs (#2242) (#2319)

This commit is contained in:
Łukasz Kosson 2025-07-03 13:26:59 +02:00 committed by GitHub
parent c3483db7f3
commit cca51e765a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -21,6 +21,7 @@ import android.annotation.SuppressLint
import android.app.Application
import android.bluetooth.BluetoothDevice
import android.content.Context
import android.content.SharedPreferences
import android.hardware.usb.UsbManager
import android.os.RemoteException
import androidx.lifecycle.MutableLiveData
@ -51,8 +52,10 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import org.json.JSONArray
@HiltViewModel
@Suppress("LongParameterList", "TooManyFunctions")
class BTScanModel @Inject constructor(
private val application: Application,
private val serviceRepository: ServiceRepository,
@ -61,12 +64,15 @@ class BTScanModel @Inject constructor(
private val usbManagerLazy: dagger.Lazy<UsbManager>,
private val networkRepository: NetworkRepository,
private val radioInterfaceService: RadioInterfaceService,
private val preferences: SharedPreferences,
) : ViewModel(), Logging {
private val context: Context get() = application.applicationContext
val devices = MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf())
val errorText = MutableLiveData<String?>(null)
private val recentIpAddresses = MutableStateFlow(getRecentAddresses())
private val showMockInterface: StateFlow<Boolean>
get() =
MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow()
@ -75,9 +81,10 @@ class BTScanModel @Inject constructor(
combine(
bluetoothRepository.state,
networkRepository.resolvedList,
recentIpAddresses.asStateFlow(),
usbRepository.serialDevicesWithDrivers,
showMockInterface,
) { ble, tcp, usb, showMockInterface ->
) { ble, tcp, recent, usb, showMockInterface ->
devices.value = mutableMapOf<String, DeviceListEntry>().apply {
fun addDevice(entry: DeviceListEntry) {
this[entry.fullAddress] = entry
@ -118,6 +125,11 @@ class BTScanModel @Inject constructor(
addDevice(DeviceListEntry(displayName, "t$address", true))
}
// Include saved IP connections
recent?.forEach { address ->
addDevice(DeviceListEntry(context.getString(R.string.meshtastic), address, true))
}
usb.forEach { (_, d) ->
addDevice(USBDeviceListEntry(radioInterfaceService, usbManagerLazy.get(), d))
}
@ -282,11 +294,36 @@ class BTScanModel @Inject constructor(
}.launchIn(viewModelScope)
}
private fun getRecentAddresses(): List<String> {
val jsonAddresses = preferences.getString("recent-ip-addresses", "[]") ?: "[]"
val listAddresses = JSONArray(jsonAddresses).let { jsonArray ->
List(jsonArray.length()) { index -> jsonArray.getString(index) }
}
return listAddresses
}
private fun setRecentAddresses(addresses: List<String>) {
val editor = preferences.edit()
editor.putString("recent-ip-addresses", addresses.toString())
editor.apply()
recentIpAddresses.value = addresses
}
fun addRecentAddress(address: String) {
if (!address.startsWith("t")) return
val existingItems = getRecentAddresses()
val updatedList = mutableListOf<String>()
updatedList.add(address)
updatedList.addAll(existingItems.filter { it != address }.take(2))
setRecentAddresses(updatedList)
}
// Called by the GUI when a new device has been selected by the user
// @returns true if we were able to change to that item
fun onSelected(it: DeviceListEntry): Boolean {
// If the device is paired, let user select it, otherwise start the pairing flow
if (it.bonded) {
addRecentAddress(it.fullAddress)
changeDeviceAddress(it.fullAddress)
return true
} else {