mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-02-05 05:12:51 -05:00
@@ -17,8 +17,8 @@ android {
|
||||
applicationId "com.geeksville.mesh"
|
||||
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
|
||||
targetSdkVersion 29
|
||||
versionCode 10795 // format is Mmmss (where M is 1+the numeric major number
|
||||
versionName "0.7.95"
|
||||
versionCode 10801 // format is Mmmss (where M is 1+the numeric major number
|
||||
versionName "0.8.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
@@ -128,7 +128,7 @@ dependencies {
|
||||
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0'
|
||||
|
||||
// mapbox specifies a really old version of okhttp3 which causes lots of API warnings. trying a newer version
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
|
||||
|
||||
// location services
|
||||
implementation 'com.google.android.gms:play-services-location:17.0.0'
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import com.vorlonsoft.android.rate.AppRate
|
||||
import com.vorlonsoft.android.rate.StoreType
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
@@ -629,29 +630,37 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||
private fun perhapsChangeChannel() {
|
||||
// If the is opening a channel URL, handle it now
|
||||
requestedChannelUrl?.let { url ->
|
||||
val channel = Channel(url)
|
||||
requestedChannelUrl = null
|
||||
try {
|
||||
val channel = Channel(url)
|
||||
requestedChannelUrl = null
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.new_channel_rcvd)
|
||||
.setMessage(getString(R.string.do_you_want_switch).format(channel.name))
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
// Do nothing
|
||||
}
|
||||
.setPositiveButton(R.string.accept) { _, _ ->
|
||||
debug("Setting channel from URL")
|
||||
try {
|
||||
model.setChannel(channel.settings)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("Couldn't change channel ${ex.message}")
|
||||
Toast.makeText(
|
||||
this,
|
||||
"Couldn't change channel, because radio is not yet connected. Please try again.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.new_channel_rcvd)
|
||||
.setMessage(getString(R.string.do_you_want_switch).format(channel.name))
|
||||
.setNeutralButton(R.string.cancel) { _, _ ->
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
.show()
|
||||
.setPositiveButton(R.string.accept) { _, _ ->
|
||||
debug("Setting channel from URL")
|
||||
try {
|
||||
model.setChannel(channel.settings)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("Couldn't change channel ${ex.message}")
|
||||
Toast.makeText(
|
||||
this,
|
||||
"Couldn't change channel, because radio is not yet connected. Please try again.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.channel_invalid,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||
|
||||
// NRF52 targets do not need the nasty force refresh hack that ESP32 needs (because they keep their
|
||||
// BLE handles stable. So turn the hack off for these devices. FIXME - find a better way to know that the board is NRF52 based
|
||||
// and Amazon fire devices seem to not need this hack either
|
||||
// Build.MANUFACTURER != "Amazon" &&
|
||||
private var needForceRefresh = !address.startsWith("FD:10:04")
|
||||
|
||||
init {
|
||||
@@ -351,6 +353,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||
}
|
||||
} catch (ex: CancellationException) {
|
||||
warn("retryDueToException was cancelled")
|
||||
} finally {
|
||||
reconnectJob = null
|
||||
}
|
||||
|
||||
/// We only try to set MTU once, because some buggy implementations fail
|
||||
@@ -415,7 +419,9 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||
if (needForceRefresh) { // Our ESP32 code doesn't properly generate "service changed" indications. Therefore we need to force a refresh on initial start
|
||||
//needForceRefresh = false // In fact, because of tearing down BLE in sleep on the ESP32, our handle # assignments are not stable across sleep - so we much refetch every time
|
||||
forceServiceRefresh() // this article says android should not be caching, but it does on some phones: https://punchthrough.com/attribute-caching-in-ble-advantages-and-pitfalls/
|
||||
delay(200) // From looking at the android C code it seems that we need to give some time for the refresh message to reach that worked _before_ we try to set mtu/get services
|
||||
|
||||
delay(500) // From looking at the android C code it seems that we need to give some time for the refresh message to reach that worked _before_ we try to set mtu/get services
|
||||
// 200ms was not enough on an Amazon Fire
|
||||
}
|
||||
|
||||
// we begin by setting our MTU size as high as it can go (if we can)
|
||||
@@ -443,7 +449,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||
|
||||
override fun close() {
|
||||
reconnectJob?.cancel() // Cancel any queued reconnect attempts
|
||||
reconnectJob = null
|
||||
|
||||
if (safe != null) {
|
||||
info("Closing BluetoothInterface")
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.geeksville.util.toRemoteExceptions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
|
||||
|
||||
class RadioNotConnectedException(message: String = "Not connected to radio") :
|
||||
@@ -121,8 +122,10 @@ class RadioInterfaceService : Service(), Logging {
|
||||
private lateinit var sentPacketsLog: BinaryLogFile // inited in onCreate
|
||||
private lateinit var receivedPacketsLog: BinaryLogFile
|
||||
|
||||
private val serviceJob = Job()
|
||||
val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
/**
|
||||
* We recreate this scope each time we stop an interface
|
||||
*/
|
||||
var serviceScope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
private val nopIf = NopInterface()
|
||||
private var radioIf: IRadioInterface = nopIf
|
||||
@@ -198,7 +201,7 @@ class RadioInterfaceService : Service(), Logging {
|
||||
override fun onDestroy() {
|
||||
unregisterReceiver(bluetoothStateReceiver)
|
||||
stopInterface()
|
||||
serviceJob.cancel()
|
||||
serviceScope.cancel("Destroying RadioInterface")
|
||||
runningService = null
|
||||
super.onDestroy()
|
||||
}
|
||||
@@ -248,6 +251,10 @@ class RadioInterfaceService : Service(), Logging {
|
||||
radioIf = nopIf
|
||||
r.close()
|
||||
|
||||
// cancel any old jobs and get ready for the new ones
|
||||
serviceScope.cancel("stopping interface")
|
||||
serviceScope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
if (logSends)
|
||||
sentPacketsLog.close()
|
||||
if (logReceives)
|
||||
|
||||
@@ -609,7 +609,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
||||
|
||||
private fun queueDiscoverServices(cont: Continuation<Unit>) {
|
||||
queueWork("discover", cont) {
|
||||
gatt!!.discoverServices()
|
||||
gatt?.discoverServices() ?: throw BLEException("GATT is null")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,6 +742,10 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
||||
null // clear gat before calling close, bcause close might throw dead object exception
|
||||
g2.close()
|
||||
}
|
||||
} catch (ex: NullPointerException) {
|
||||
// Attempt to invoke virtual method 'com.android.bluetooth.gatt.AdvertiseClient com.android.bluetooth.gatt.AdvertiseManager.getAdvertiseClient(int)' on a null object reference
|
||||
//com.geeksville.mesh.service.SafeBluetooth.closeGatt
|
||||
warn("Ignoring NPE in close - probably buggy Samsung BLE")
|
||||
} catch (ex: DeadObjectException) {
|
||||
warn("Ignoring dead object exception, probably bluetooth was just disabled")
|
||||
} finally {
|
||||
@@ -787,30 +791,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
||||
notifyHandlers[c.uuid] = onChanged
|
||||
// c.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
|
||||
gatt!!.setCharacteristicNotification(c, enable)
|
||||
|
||||
/*
|
||||
c is null sometimes
|
||||
2020-04-13 15:59:38.222 2111-2182/com.geeksville.mesh D/BluetoothGatt: setCharacteristicNotification() - uuid: ed9da18c-a800-4f66-a670-aa7547e34453 enable: true
|
||||
2020-04-13 15:59:38.225 2111-2182/com.geeksville.mesh E/com.geeksville.util.Exceptions: exceptionReporter Uncaught Exception
|
||||
kotlin.KotlinNullPointerException
|
||||
at com.geeksville.mesh.service.SafeBluetooth.setNotify(SafeBluetooth.kt:505)
|
||||
at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:328)
|
||||
at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:90)
|
||||
at com.geeksville.concurrent.CallbackContinuation.resume(SyncContinuation.kt:20)
|
||||
at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:329)
|
||||
at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:33)
|
||||
at com.geeksville.util.ExceptionsKt.exceptionReporter(Exceptions.kt:34)
|
||||
at com.geeksville.mesh.service.SafeBluetooth.completeWork(SafeBluetooth.kt:312)
|
||||
at com.geeksville.mesh.service.SafeBluetooth.access$completeWork(SafeBluetooth.kt:33)
|
||||
at com.geeksville.mesh.service.SafeBluetooth$gattCallback$1.onMtuChanged(SafeBluetooth.kt:221)
|
||||
at android.bluetooth.BluetoothGatt$1$13.run(BluetoothGatt.java:658)
|
||||
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:780)
|
||||
at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:41)
|
||||
at android.bluetooth.BluetoothGatt$1.onConfigureMTU(BluetoothGatt.java:653)
|
||||
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:330)
|
||||
at android.os.Binder.execTransactInternal(Binder.java:1021)
|
||||
at android.os.Binder.execTransact(Binder.java:994)
|
||||
*/
|
||||
|
||||
// per https://stackoverflow.com/questions/27068673/subscribe-to-a-ble-gatt-notification-android
|
||||
val descriptor: BluetoothGattDescriptor = c.getDescriptor(configurationDescriptorUUID)
|
||||
?: throw BLEException("Notify descriptor not found for ${c.uuid}") // This can happen on buggy BLE implementations
|
||||
@@ -820,5 +801,4 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
||||
debug("Notify enable=$enable completed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,4 +73,5 @@
|
||||
<string name="about">About</string>
|
||||
<string name="a_list_of_nodes_in_the_mesh">A list of nodes in the mesh</string>
|
||||
<string name="text_messages">Text messages</string>
|
||||
<string name="channel_invalid">This Channel URL is invalid and can not be used</string>
|
||||
</resources>
|
||||
|
||||
Submodule geeksville-androidlib updated: cfe31d66e4...bdb2685aef
Reference in New Issue
Block a user