Merge pull request #106 from geeksville/dev

Dev
This commit is contained in:
Kevin Hester
2020-07-18 14:28:16 -07:00
committed by GitHub
7 changed files with 59 additions and 57 deletions

View File

@@ -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'

View File

@@ -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()
}
}
}

View File

@@ -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")

View File

@@ -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)

View File

@@ -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")
}
}
}
}

View File

@@ -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>