Remove device from ContactsFragment when disconnected.

Also changed some variable/class/function names.
This commit is contained in:
Felix Ableitner 2014-11-07 18:25:53 +02:00
parent 2129107684
commit 6b2ef30888
4 changed files with 49 additions and 42 deletions

View file

@ -8,7 +8,7 @@ import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
import android.os.Handler
import android.util.Log
import com.nutomic.ensichat.R
import com.nutomic.ensichat.bluetooth.ChatService.{OnDeviceConnectedListener, OnMessageReceivedListener}
import com.nutomic.ensichat.bluetooth.ChatService.{OnConnectionChangedListener, OnMessageReceivedListener}
import com.nutomic.ensichat.messages._
import scala.collection.immutable.{HashMap, HashSet, TreeSet}
@ -24,8 +24,8 @@ object ChatService {
val KEY_GENERATION_FINISHED = "com.nutomic.ensichat.messages.KEY_GENERATION_FINISHED"
trait OnDeviceConnectedListener {
def onDeviceConnected(devices: Map[Device.ID, Device]): Unit
trait OnConnectionChangedListener {
def onConnectionChanged(devices: Map[Device.ID, Device]): Unit
}
trait OnMessageReceivedListener {
@ -52,7 +52,7 @@ class ChatService extends Service {
* but on a Nexus S (Android 4.1.2), these functions are garbage collected even when
* referenced.
*/
private var deviceListeners = new HashSet[WeakReference[OnDeviceConnectedListener]]()
private var connectionListeners = new HashSet[WeakReference[OnConnectionChangedListener]]()
private val messageListeners =
mutable.HashMap[Device.ID, mutable.Set[WeakReference[OnMessageReceivedListener]]]()
@ -137,7 +137,7 @@ class ChatService extends Service {
val device: Device =
new Device(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), false)
devices += (device.id -> device)
new ConnectThread(device, onConnected).start()
new ConnectThread(device, onConnectionChanged).start()
}
}
@ -168,34 +168,40 @@ class ChatService extends Service {
private def startBluetoothConnections(): Unit = {
cancelDiscovery = false
discover()
ListenThread = new ListenThread(getString(R.string.app_name), bluetoothAdapter, onConnected)
ListenThread =
new ListenThread(getString(R.string.app_name), bluetoothAdapter, onConnectionChanged)
ListenThread.start()
}
/**
* Registers a listener that is called whenever a new device is connected.
*/
def registerDeviceListener(listener: OnDeviceConnectedListener): Unit = {
deviceListeners += new WeakReference[OnDeviceConnectedListener](listener)
listener.onDeviceConnected(devices)
def registerConnectionListener(listener: OnConnectionChangedListener): Unit = {
connectionListeners += new WeakReference[OnConnectionChangedListener](listener)
listener.onConnectionChanged(devices)
}
/**
* Called when a Bluetooth device is connected.
*
* Adds the device to [[connections]], notifies all [[deviceListeners]], sends DeviceInfoMessage.
* Adds the device to [[connections]], notifies all [[connectionListeners]], sends DeviceInfoMessage.
*
* @param device The updated device info for the remote device.
* @param socket A socket for data transfer if device.connected is true, otherwise null.
*/
def onConnected(device: Device, socket: BluetoothSocket): Unit = {
val updatedDevice: Device = new Device(device.bluetoothDevice, true)
devices += (device.id -> updatedDevice)
connections += (device.id ->
new TransferThread(updatedDevice, socket, localDeviceId, Encrypt, handleNewMessage))
connections(device.id).start()
def onConnectionChanged(device: Device, socket: BluetoothSocket): Unit = {
devices += (device.id -> device)
send(new DeviceInfoMessage(localDeviceId, device.id, new Date(), Encrypt.getLocalPublicKey))
deviceListeners.foreach(l => l.get match {
case Some(_) => l.apply().onDeviceConnected(devices)
case None => deviceListeners -= l
if (device.connected) {
connections += (device.id ->
new TransferThread(device, socket, this, Encrypt, handleNewMessage))
connections(device.id).start()
send(new DeviceInfoMessage(localDeviceId, device.id, new Date(), Encrypt.getLocalPublicKey))
}
connectionListeners.foreach(l => l.get match {
case Some(_) => l.apply().onConnectionChanged(devices)
case None => connectionListeners -= l
})
}

View file

@ -33,7 +33,7 @@ class ConnectThread(device: Device, onConnected: (Device, BluetoothSocket) => Un
}
Log.i(Tag, "Successfully connected to device " + device.name)
onConnected(device, Socket)
onConnected(new Device(device.bluetoothDevice, true), Socket)
}
}

View file

@ -13,11 +13,11 @@ import com.nutomic.ensichat.messages.{Crypto, DeviceInfoMessage, Message, TextMe
*
* @param device The bluetooth device to interact with.
* @param socket An open socket to the given device.
* @param encrypt Object used to handle signing and encryption of messages.
* @param crypto Object used to handle signing and encryption of messages.
* @param onReceive Called when a message was received from the other device.
*/
class TransferThread(device: Device, socket: BluetoothSocket, localDevice: Device.ID,
encrypt: Crypto, onReceive: (Message) => Unit) extends Thread {
class TransferThread(device: Device, socket: BluetoothSocket, service: ChatService,
crypto: Crypto, onReceive: (Message) => Unit) extends Thread {
private val Tag: String = "TransferThread"
@ -44,58 +44,59 @@ class TransferThread(device: Device, socket: BluetoothSocket, localDevice: Devic
override def run(): Unit = {
Log.i(Tag, "Starting data transfer with " + device.toString)
// Keep listening to the InputStream while connected
while (true) {
while (socket.isConnected) {
try {
val bytes = new Array[Byte](MaxMessageLength)
InStream.read(bytes)
val (msg, signature) = Message.read(bytes)
val (message, signature) = Message.read(bytes)
var messageValid = true
if (msg.sender != device.id) {
if (message.sender != device.id) {
Log.i(Tag, "Dropping message with invalid sender from " + device.id)
messageValid = false
}
if (msg.receiver != localDevice) {
if (message.receiver != service.localDeviceId) {
Log.i(Tag, "Dropping message with different receiver from " + device.id)
messageValid = false
}
// Add public key for new, directly connected device.
// Explicitly check that message was not forwarded or spoofed.
if (msg.isInstanceOf[DeviceInfoMessage] && !encrypt.havePublicKey(msg.sender) &&
msg.sender == device.id) {
val dim = msg.asInstanceOf[DeviceInfoMessage]
if (message.isInstanceOf[DeviceInfoMessage] && !crypto.havePublicKey(message.sender) &&
message.sender == device.id) {
val dim = message.asInstanceOf[DeviceInfoMessage]
// Permanently store public key for new local devices (also check signature).
if (encrypt.isValidSignature(msg, signature, dim.publicKey)) {
encrypt.addPublicKey(device.id, dim.publicKey)
if (crypto.isValidSignature(message, signature, dim.publicKey)) {
crypto.addPublicKey(device.id, dim.publicKey)
Log.i(Tag, "Added public key for new device " + device.name)
}
}
if (!encrypt.isValidSignature(msg, signature)) {
if (!crypto.isValidSignature(message, signature)) {
Log.i(Tag, "Dropping message with invalid signature from " + device.id)
messageValid = false
}
if (messageValid) {
msg match {
message match {
case m: TextMessage => onReceive(m)
case m: DeviceInfoMessage => encrypt.addPublicKey(msg.sender, m.publicKey)
case m: DeviceInfoMessage => crypto.addPublicKey(message.sender, m.publicKey)
}
}
} catch {
case e: IOException =>
Log.e(Tag, "Disconnected from device", e)
Log.w(Tag, "Connection to " + device.name + " closed with exception", e)
service.onConnectionChanged(new Device(device.bluetoothDevice, false), null)
return
}
}
service.onConnectionChanged(new Device(device.bluetoothDevice, false), null)
}
def send(message: Message): Unit = {
try {
val sig = encrypt.calculateSignature(message)
val sig = crypto.calculateSignature(message)
val bytes = message.write(sig)
OutStream.write(bytes)
} catch {

View file

@ -13,7 +13,7 @@ import com.nutomic.ensichat.util.DevicesAdapter
/**
* Lists all nearby, connected devices.
*/
class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedListener {
class ContactsFragment extends ListFragment with ChatService.OnConnectionChangedListener {
private var chatService: ChatService = _
@ -21,7 +21,7 @@ class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedLi
override def onServiceConnected(componentName: ComponentName, iBinder: IBinder): Unit = {
val binder: ChatServiceBinder = iBinder.asInstanceOf[ChatServiceBinder]
chatService = binder.getService
chatService.registerDeviceListener(ContactsFragment.this)
chatService.registerConnectionListener(ContactsFragment.this)
}
override def onServiceDisconnected(componentName: ComponentName): Unit = {
@ -71,7 +71,7 @@ class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedLi
/**
* Displays newly connected devices in the list.
*/
override def onDeviceConnected(devices: Map[Device.ID, Device]): Unit = {
override def onConnectionChanged(devices: Map[Device.ID, Device]): Unit = {
if (getActivity == null)
return