mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
commit
69dc99cdbc
6 changed files with 74 additions and 65 deletions
|
|
@ -43,8 +43,8 @@ android {
|
||||||
applicationId "com.geeksville.mesh"
|
applicationId "com.geeksville.mesh"
|
||||||
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
|
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
|
||||||
targetSdkVersion 30 // 30 can't work until an explicit location permissions dialog is added
|
targetSdkVersion 30 // 30 can't work until an explicit location permissions dialog is added
|
||||||
versionCode 20257 // format is Mmmss (where M is 1+the numeric major number
|
versionCode 20258 // format is Mmmss (where M is 1+the numeric major number
|
||||||
versionName "1.2.57"
|
versionName "1.2.58"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
// per https://developer.android.com/studio/write/vector-asset-studio
|
// per https://developer.android.com/studio/write/vector-asset-studio
|
||||||
|
|
|
||||||
|
|
@ -737,24 +737,32 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSnackbar(msgId: Int) {
|
private fun showSnackbar(msgId: Int) {
|
||||||
Snackbar.make(
|
try {
|
||||||
findViewById(android.R.id.content),
|
Snackbar.make(
|
||||||
msgId,
|
findViewById(android.R.id.content),
|
||||||
Snackbar.LENGTH_LONG
|
msgId,
|
||||||
).show()
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
} catch (ex: IllegalStateException) {
|
||||||
|
reportError("Snackbar couldn't find view for msgId $msgId")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSnackbar(msg: String) {
|
private fun showSnackbar(msg: String) {
|
||||||
Snackbar.make(
|
try {
|
||||||
findViewById(android.R.id.content),
|
Snackbar.make(
|
||||||
msg,
|
findViewById(android.R.id.content),
|
||||||
Snackbar.LENGTH_INDEFINITE
|
msg,
|
||||||
)
|
Snackbar.LENGTH_INDEFINITE
|
||||||
.apply { view.findViewById<TextView>(R.id.snackbar_text).isSingleLine = false }
|
)
|
||||||
.setAction(R.string.okay) {
|
.apply { view.findViewById<TextView>(R.id.snackbar_text).isSingleLine = false }
|
||||||
// dismiss
|
.setAction(R.string.okay) {
|
||||||
}
|
// dismiss
|
||||||
.show()
|
}
|
||||||
|
.show()
|
||||||
|
} catch (ex: IllegalStateException) {
|
||||||
|
reportError("Snackbar couldn't find view for msgString $msg")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun perhapsChangeChannel(url: Uri? = requestedChannelUrl) {
|
fun perhapsChangeChannel(url: Uri? = requestedChannelUrl) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.BufferedWriter
|
import java.io.BufferedWriter
|
||||||
|
import java.io.FileNotFoundException
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -350,12 +351,16 @@ class UIViewModel @Inject constructor(
|
||||||
|
|
||||||
private suspend inline fun writeToUri(uri: Uri, crossinline block: suspend (BufferedWriter) -> Unit) {
|
private suspend inline fun writeToUri(uri: Uri, crossinline block: suspend (BufferedWriter) -> Unit) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor ->
|
try {
|
||||||
FileWriter(parcelFileDescriptor.fileDescriptor).use { fileWriter ->
|
app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor ->
|
||||||
BufferedWriter(fileWriter).use { writer ->
|
FileWriter(parcelFileDescriptor.fileDescriptor).use { fileWriter ->
|
||||||
block.invoke(writer)
|
BufferedWriter(fileWriter).use { writer ->
|
||||||
|
block.invoke(writer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (ex: FileNotFoundException) {
|
||||||
|
errormsg("Can't write file error: ${ex.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,15 +112,14 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||||
/** Return true if this address is still acceptable. For BLE that means, still bonded */
|
/** Return true if this address is still acceptable. For BLE that means, still bonded */
|
||||||
@SuppressLint("NewApi", "MissingPermission")
|
@SuppressLint("NewApi", "MissingPermission")
|
||||||
override fun addressValid(context: Context, rest: String): Boolean {
|
override fun addressValid(context: Context, rest: String): Boolean {
|
||||||
val allPaired = if (hasCompanionDeviceApi(context)) {
|
/* val allPaired = if (hasCompanionDeviceApi(context)) {
|
||||||
val deviceManager: CompanionDeviceManager by lazy {
|
val deviceManager: CompanionDeviceManager by lazy {
|
||||||
context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
|
context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
|
||||||
}
|
}
|
||||||
deviceManager.associations.map { it }.toSet()
|
deviceManager.associations.map { it }.toSet()
|
||||||
} else {
|
} else { */
|
||||||
getBluetoothAdapter(context)?.bondedDevices.orEmpty()
|
val allPaired = getBluetoothAdapter(context)?.bondedDevices.orEmpty()
|
||||||
.map { it.address }.toSet()
|
.map { it.address }.toSet()
|
||||||
}
|
|
||||||
return if (!allPaired.contains(rest)) {
|
return if (!allPaired.contains(rest)) {
|
||||||
warn("Ignoring stale bond to ${rest.anonymize}")
|
warn("Ignoring stale bond to ${rest.anonymize}")
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -286,22 +286,6 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
|
|
||||||
debug("Setting bonded device to ${address.anonymize}")
|
debug("Setting bonded device to ${address.anonymize}")
|
||||||
|
|
||||||
// We only keep an association to one device at a time... (move to BluetoothInterface?)
|
|
||||||
if (BluetoothInterface.hasCompanionDeviceApi(this)) {
|
|
||||||
if (address != null) {
|
|
||||||
val deviceManager = getSystemService(CompanionDeviceManager::class.java)
|
|
||||||
val c = address[0]
|
|
||||||
val rest = address.substring(1)
|
|
||||||
|
|
||||||
deviceManager.associations.forEach { old ->
|
|
||||||
if (rest != old) {
|
|
||||||
debug("Forgetting old BLE association ${old.anonymize}")
|
|
||||||
deviceManager.disassociate(old)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrefs(this).edit(commit = true) {
|
getPrefs(this).edit(commit = true) {
|
||||||
if (address == null)
|
if (address == null)
|
||||||
this.remove(DEVADDR_KEY)
|
this.remove(DEVADDR_KEY)
|
||||||
|
|
|
||||||
|
|
@ -813,7 +813,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
if (curRadio != null && !MockInterface.addressValid(requireContext(), "")) {
|
if (curRadio != null && !MockInterface.addressValid(requireContext(), "")) {
|
||||||
binding.warningNotPaired.visibility = View.GONE
|
binding.warningNotPaired.visibility = View.GONE
|
||||||
// binding.scanStatusText.text = getString(R.string.current_pair).format(curRadio)
|
// binding.scanStatusText.text = getString(R.string.current_pair).format(curRadio)
|
||||||
} else {
|
} else if (model.bluetoothEnabled.value == true){
|
||||||
binding.warningNotPaired.visibility = View.VISIBLE
|
binding.warningNotPaired.visibility = View.VISIBLE
|
||||||
binding.scanStatusText.text = getString(R.string.not_paired_yet)
|
binding.scanStatusText.text = getString(R.string.not_paired_yet)
|
||||||
}
|
}
|
||||||
|
|
@ -883,7 +883,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
deviceManager.associate(
|
deviceManager.associate(
|
||||||
pairingRequest,
|
pairingRequest,
|
||||||
object : CompanionDeviceManager.Callback() {
|
object : CompanionDeviceManager.Callback() {
|
||||||
|
|
||||||
override fun onDeviceFound(chooserLauncher: IntentSender) {
|
override fun onDeviceFound(chooserLauncher: IntentSender) {
|
||||||
debug("Found one device - enabling changeRadioButton")
|
debug("Found one device - enabling changeRadioButton")
|
||||||
binding.changeRadioButton.isEnabled = true
|
binding.changeRadioButton.isEnabled = true
|
||||||
|
|
@ -914,8 +913,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
updateDevicesButtons(devices)
|
updateDevicesButtons(devices)
|
||||||
startCompanionScan()
|
startCompanionScan()
|
||||||
}
|
}
|
||||||
|
|
||||||
startCompanionScan()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|
@ -952,13 +949,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
|
|
||||||
fun weNeedAccess(warningReason: String) {
|
fun weNeedAccess(warningReason: String) {
|
||||||
warn("Telling user we need need location access")
|
warn("Telling user we need need location access")
|
||||||
|
showSnackbar(warningReason)
|
||||||
Snackbar.make(requireView(), warningReason, Snackbar.LENGTH_INDEFINITE)
|
|
||||||
.apply { view.findViewById<TextView>(R.id.snackbar_text).isSingleLine = false }
|
|
||||||
.setAction(R.string.okay) {
|
|
||||||
// dismiss
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locationSettingsResponse.addOnSuccessListener {
|
locationSettingsResponse.addOnSuccessListener {
|
||||||
|
|
@ -1001,6 +992,23 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showSnackbar(msg: String) {
|
||||||
|
try {
|
||||||
|
Snackbar.make(
|
||||||
|
requireView(),
|
||||||
|
msg,
|
||||||
|
Snackbar.LENGTH_INDEFINITE
|
||||||
|
)
|
||||||
|
.apply { view.findViewById<TextView>(R.id.snackbar_text).isSingleLine = false }
|
||||||
|
.setAction(R.string.okay) {
|
||||||
|
// dismiss
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
} catch (ex: IllegalStateException) {
|
||||||
|
reportError("Snackbar couldn't find view for msgString $msg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
|
||||||
|
|
@ -1022,15 +1030,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
if (!hasUSB) {
|
if (!hasUSB) {
|
||||||
// Warn user if BLE is disabled
|
// Warn user if BLE is disabled
|
||||||
if (scanModel.bluetoothAdapter?.isEnabled != true) {
|
if (scanModel.bluetoothAdapter?.isEnabled != true) {
|
||||||
Snackbar.make(
|
showSnackbar(getString(R.string.error_bluetooth))
|
||||||
requireView(),
|
|
||||||
R.string.error_bluetooth,
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
.setAction(R.string.okay) {
|
|
||||||
// dismiss
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
} else {
|
} else {
|
||||||
if (binding.provideLocationCheckbox.isChecked)
|
if (binding.provideLocationCheckbox.isChecked)
|
||||||
checkLocationEnabled(getString(R.string.location_disabled))
|
checkLocationEnabled(getString(R.string.location_disabled))
|
||||||
|
|
@ -1038,17 +1038,30 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
if (hasCompanionDeviceApi && myActivity.hasConnectPermission()
|
||||||
&& requestCode == MainActivity.SELECT_DEVICE_REQUEST_CODE
|
&& requestCode == MainActivity.SELECT_DEVICE_REQUEST_CODE
|
||||||
&& resultCode == Activity.RESULT_OK
|
&& resultCode == Activity.RESULT_OK
|
||||||
) {
|
) {
|
||||||
val deviceToPair: BluetoothDevice =
|
val deviceToPair: BluetoothDevice =
|
||||||
data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!!
|
data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!!
|
||||||
if (deviceToPair.bondState != BOND_BONDED) {
|
|
||||||
deviceToPair.createBond()
|
// We only keep an association to one device at a time...
|
||||||
|
deviceManager.associations.forEach { old ->
|
||||||
|
if (deviceToPair.address != old) {
|
||||||
|
debug("Forgetting old BLE association ${old.anonymize}")
|
||||||
|
deviceManager.disassociate(old)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
scanModel.changeScanSelection(myActivity, "x${deviceToPair.address}")
|
scanModel.onSelected(
|
||||||
|
myActivity,
|
||||||
|
BTScanModel.DeviceListEntry(
|
||||||
|
deviceToPair.name,
|
||||||
|
"x${deviceToPair.address}",
|
||||||
|
deviceToPair.bondState == BOND_BONDED
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue