Fixed crash on start if Bluetooth is disabled (fixes #3).

This commit is contained in:
Felix Ableitner 2015-02-25 20:25:49 +01:00
parent 748c8c27c5
commit 3baf87be3d
3 changed files with 45 additions and 19 deletions

View file

@ -0,0 +1,20 @@
package com.nutomic.ensichat.bluetooth
import android.bluetooth.BluetoothAdapter
import android.os.Handler
import android.test.AndroidTestCase
class BluetoothInterfaceTest extends AndroidTestCase {
private lazy val adapter = new BluetoothInterface(getContext, new Handler(), Message => Unit,
() => Unit, Message => false)
/**
* Test for issue [[https://github.com/Nutomic/ensichat/issues/3 #3]].
*/
def testStartBluetoothOff(): Unit = {
BluetoothAdapter.getDefaultAdapter.disable()
adapter.create()
}
}

View file

@ -28,19 +28,23 @@ object BluetoothInterface {
/** /**
* Handles all Bluetooth connectivity. * Handles all Bluetooth connectivity.
*/ */
class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Handler) class BluetoothInterface(context: Context, mainHandler: Handler,
onMessageReceived: Message => Unit, callConnectionListeners: () => Unit,
onConnectionOpened: (Message) => Boolean)
extends InterfaceHandler { extends InterfaceHandler {
private val Tag = "BluetoothInterface" private val Tag = "BluetoothInterface"
private lazy val btAdapter = BluetoothAdapter.getDefaultAdapter private lazy val btAdapter = BluetoothAdapter.getDefaultAdapter
private lazy val crypto = new Crypto(context)
private var devices = new HashMap[Device.ID, Device]() private var devices = new HashMap[Device.ID, Device]()
private var connections = new HashMap[Device.ID, TransferThread]() private var connections = new HashMap[Device.ID, TransferThread]()
private lazy val listenThread = private lazy val listenThread =
new ListenThread(service.getString(R.string.app_name), btAdapter, onConnectionOpened) new ListenThread(context.getString(R.string.app_name), btAdapter, connectionOpened)
private var cancelDiscovery = false private var cancelDiscovery = false
@ -52,13 +56,15 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
* Initializes and starts discovery and listening. * Initializes and starts discovery and listening.
*/ */
override def create(): Unit = { override def create(): Unit = {
service.registerReceiver(DeviceDiscoveredReceiver, context.registerReceiver(DeviceDiscoveredReceiver,
new IntentFilter(BluetoothDevice.ACTION_FOUND)) new IntentFilter(BluetoothDevice.ACTION_FOUND))
service.registerReceiver(BluetoothStateReceiver, context.registerReceiver(BluetoothStateReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
service.registerReceiver(DiscoveryFinishedReceiver, context.registerReceiver(DiscoveryFinishedReceiver,
new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED))
startBluetoothConnections() // Otherwise, connections are started in [[BluetoothStateReceiver]].
if (btAdapter.isEnabled)
startBluetoothConnections()
} }
/** /**
@ -67,9 +73,9 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
override def destroy(): Unit = { override def destroy(): Unit = {
listenThread.cancel() listenThread.cancel()
cancelDiscovery = true cancelDiscovery = true
service.unregisterReceiver(DeviceDiscoveredReceiver) context.unregisterReceiver(DeviceDiscoveredReceiver)
service.unregisterReceiver(BluetoothStateReceiver) context.unregisterReceiver(BluetoothStateReceiver)
service.unregisterReceiver(DiscoveryFinishedReceiver) context.unregisterReceiver(DiscoveryFinishedReceiver)
} }
/** /**
@ -93,9 +99,9 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
btAdapter.startDiscovery() btAdapter.startDiscovery()
} }
val pm = PreferenceManager.getDefaultSharedPreferences(service) val pm = PreferenceManager.getDefaultSharedPreferences(context)
val scanInterval = pm.getString(SettingsFragment.KeyScanInterval, val scanInterval = pm.getString(SettingsFragment.KeyScanInterval,
service.getResources.getString(R.string.default_scan_interval)).toInt * 1000 context.getResources.getString(R.string.default_scan_interval)).toInt * 1000
mainHandler.postDelayed(new Runnable { mainHandler.postDelayed(new Runnable {
override def run(): Unit = discover() override def run(): Unit = discover()
}, scanInterval) }, scanInterval)
@ -117,7 +123,7 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
override def onReceive(context: Context, intent: Intent): Unit = { override def onReceive(context: Context, intent: Intent): Unit = {
discovered.filterNot(d => connections.keySet.contains(d.id)) discovered.filterNot(d => connections.keySet.contains(d.id))
.foreach { d => .foreach { d =>
new ConnectThread(d, onConnectionOpened).start() new ConnectThread(d, connectionOpened).start()
devices += (d.id -> d) devices += (d.id -> d)
} }
discovered = Set[Device]() discovered = Set[Device]()
@ -131,8 +137,7 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
override def onReceive(context: Context, intent: Intent): Unit = { override def onReceive(context: Context, intent: Intent): Unit = {
intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) match { intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) match {
case BluetoothAdapter.STATE_ON => case BluetoothAdapter.STATE_ON =>
if (crypto.localKeysExist) startBluetoothConnections()
startBluetoothConnections()
case BluetoothAdapter.STATE_TURNING_OFF => case BluetoothAdapter.STATE_TURNING_OFF =>
Log.i(Tag, "Bluetooth disabled, stopping connectivity") Log.i(Tag, "Bluetooth disabled, stopping connectivity")
listenThread.cancel() listenThread.cancel()
@ -147,7 +152,7 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
/** /**
* Initiates data transfer with device. * Initiates data transfer with device.
*/ */
def onConnectionOpened(device: Device, socket: BluetoothSocket): Unit = { def connectionOpened(device: Device, socket: BluetoothSocket): Unit = {
devices += (device.id -> device) devices += (device.id -> device)
connections += (device.id -> connections += (device.id ->
new TransferThread(device, socket, this, crypto, onReceiveMessage)) new TransferThread(device, socket, this, crypto, onReceiveMessage))
@ -160,7 +165,7 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
def onConnectionClosed(device: Device, socket: BluetoothSocket): Unit = { def onConnectionClosed(device: Device, socket: BluetoothSocket): Unit = {
devices -= device.id devices -= device.id
connections -= device.id connections -= device.id
service.callConnectionListeners() callConnectionListeners()
addressDeviceMap.inverse().remove(device.id) addressDeviceMap.inverse().remove(device.id)
} }
@ -177,10 +182,10 @@ class BluetoothInterface(service: ChatService, crypto: Crypto, mainHandler: Hand
val address = crypto.calculateAddress(info.key) val address = crypto.calculateAddress(info.key)
// Service.onConnectionOpened sends message, so mapping already needs to be in place. // Service.onConnectionOpened sends message, so mapping already needs to be in place.
addressDeviceMap.put(address, device) addressDeviceMap.put(address, device)
if (!service.onConnectionOpened(msg)) if (!onConnectionOpened(msg))
addressDeviceMap.remove(address) addressDeviceMap.remove(address)
case _ => case _ =>
service.onMessageReceived(msg) onMessageReceived(msg)
} }
/** /**

View file

@ -59,7 +59,8 @@ class ChatService extends Service {
private lazy val crypto = new Crypto(this) private lazy val crypto = new Crypto(this)
private lazy val btInterface = new BluetoothInterface(this, crypto, mainHandler) private lazy val btInterface = new BluetoothInterface(this, mainHandler,
onMessageReceived, callConnectionListeners, onConnectionOpened)
private lazy val router = new Router(connections, sendVia) private lazy val router = new Router(connections, sendVia)