Remove device from ContactsFragment when disconnected.
Also changed some variable/class/function names.
This commit is contained in:
parent
2129107684
commit
6b2ef30888
4 changed files with 49 additions and 42 deletions
|
@ -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)
|
||||
|
||||
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))
|
||||
deviceListeners.foreach(l => l.get match {
|
||||
case Some(_) => l.apply().onDeviceConnected(devices)
|
||||
case None => deviceListeners -= l
|
||||
}
|
||||
|
||||
connectionListeners.foreach(l => l.get match {
|
||||
case Some(_) => l.apply().onConnectionChanged(devices)
|
||||
case None => connectionListeners -= l
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Reference in a new issue