Addded basic ping functionality between devices.
This commit is contained in:
parent
6c4fe96f10
commit
70fec6ad08
|
@ -9,26 +9,38 @@ import android.widget.Toast
|
||||||
import com.nutomic.ensichat.R
|
import com.nutomic.ensichat.R
|
||||||
import com.nutomic.ensichat.bluetooth.ChatService
|
import com.nutomic.ensichat.bluetooth.ChatService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main activity, holds fragments and requests bluetooth to be enabled.
|
||||||
|
*/
|
||||||
class MainActivity extends Activity {
|
class MainActivity extends Activity {
|
||||||
|
|
||||||
private final val REQUEST_ENABLE_BLUETOOTH = 1
|
private final val RequestEnableBluetooth = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes layout, starts service and requests bluetooth.
|
||||||
|
*/
|
||||||
override def onCreate(savedInstanceState: Bundle): Unit = {
|
override def onCreate(savedInstanceState: Bundle): Unit = {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
startService(new Intent(this, classOf[ChatService]))
|
startService(new Intent(this, classOf[ChatService]))
|
||||||
val intent: Intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
val intent: Intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||||
startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH)
|
startActivityForResult(intent, RequestEnableBluetooth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes menu.
|
||||||
|
*/
|
||||||
override def onCreateOptionsMenu(menu: Menu): Boolean = {
|
override def onCreateOptionsMenu(menu: Menu): Boolean = {
|
||||||
getMenuInflater().inflate(R.menu.main, menu)
|
getMenuInflater().inflate(R.menu.main, menu)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exits with error if bluetooth was not enabled,
|
||||||
|
*/
|
||||||
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {
|
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {
|
||||||
requestCode match {
|
requestCode match {
|
||||||
case REQUEST_ENABLE_BLUETOOTH =>
|
case RequestEnableBluetooth =>
|
||||||
if (resultCode != Activity.RESULT_OK) {
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
Toast.makeText(this, R.string.bluetooth_required, Toast.LENGTH_LONG).show()
|
Toast.makeText(this, R.string.bluetooth_required, Toast.LENGTH_LONG).show()
|
||||||
finish()
|
finish()
|
||||||
|
@ -36,6 +48,9 @@ class MainActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu click handler.
|
||||||
|
*/
|
||||||
override def onOptionsItemSelected(item: MenuItem): Boolean = {
|
override def onOptionsItemSelected(item: MenuItem): Boolean = {
|
||||||
item.getItemId match {
|
item.getItemId match {
|
||||||
case R.id.exit =>
|
case R.id.exit =>
|
||||||
|
|
|
@ -1,67 +1,145 @@
|
||||||
package com.nutomic.ensichat.bluetooth
|
package com.nutomic.ensichat.bluetooth
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.bluetooth.{BluetoothDevice, BluetoothAdapter}
|
import android.bluetooth.{BluetoothAdapter, BluetoothDevice, BluetoothSocket}
|
||||||
import android.content.{Context, BroadcastReceiver, IntentFilter, Intent}
|
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.nutomic.ensichat.bluetooth.ChatService.DeviceListener
|
import android.widget.Toast
|
||||||
|
import com.nutomic.ensichat.{Message, R}
|
||||||
|
import android.os.Handler
|
||||||
|
|
||||||
|
import scala.collection.immutable.{HashMap, Set}
|
||||||
|
|
||||||
object ChatService {
|
object ChatService {
|
||||||
trait DeviceListener {
|
|
||||||
def onDeviceConnected(device: Device): Unit
|
/**
|
||||||
}
|
* Bluetooth service UUID version 5, created with namespace URL and "ensichat.nutomic.com".
|
||||||
|
*/
|
||||||
|
val appUuid: UUID = UUID.fromString("8ed52b7a-4501-5348-b054-3d94d004656e")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatService extends Service {
|
class ChatService extends Service {
|
||||||
|
|
||||||
private val TAG = "ChatService"
|
private val Tag = "ChatService"
|
||||||
|
|
||||||
private final val mBinder = new ChatServiceBinder(this)
|
private val SCAN_INTERVAL: Int = 5000
|
||||||
|
|
||||||
private var mBluetoothAdapter: BluetoothAdapter = _
|
private final val Binder = new ChatServiceBinder(this)
|
||||||
|
|
||||||
private var mDeviceListener: DeviceListener = _
|
private var bluetoothAdapter: BluetoothAdapter = _
|
||||||
|
|
||||||
|
private var deviceListener: Set[Map[Device.ID, Device] => Unit] =
|
||||||
|
Set[Map[Device.ID, Device] => Unit]()
|
||||||
|
|
||||||
|
private var devices: HashMap[Device.ID, Device] = new HashMap[Device.ID, Device]()
|
||||||
|
|
||||||
|
private var connections: HashMap[Device.ID, TransferThread] =
|
||||||
|
new HashMap[Device.ID, TransferThread]()
|
||||||
|
|
||||||
|
private var ListenThread: ListenThread = _
|
||||||
|
|
||||||
|
private var isDestroyed = false
|
||||||
|
|
||||||
|
private val MainHandler: Handler = new Handler()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes BroadcastReceiver for discovery, starts discovery and listens for connections.
|
||||||
|
*/
|
||||||
override def onCreate(): Unit = {
|
override def onCreate(): Unit = {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||||
|
|
||||||
var filter: IntentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND)
|
var filter: IntentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND)
|
||||||
registerReceiver(mReceiver, filter)
|
registerReceiver(mReceiver, filter)
|
||||||
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
|
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
|
||||||
registerReceiver(mReceiver, filter)
|
registerReceiver(mReceiver, filter)
|
||||||
doDiscovery()
|
Log.i(Tag, "Discovery started")
|
||||||
|
discover()
|
||||||
|
ListenThread = new ListenThread(getString(R.string.app_name), bluetoothAdapter, onConnected)
|
||||||
|
ListenThread.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onBind(intent: Intent): IBinder = {
|
override def onBind(intent: Intent): IBinder = {
|
||||||
return mBinder
|
return Binder
|
||||||
}
|
}
|
||||||
|
|
||||||
def doDiscovery() {
|
override def onDestroy(): Unit = {
|
||||||
// If we're already discovering, stop it.
|
ListenThread.cancel()
|
||||||
if (mBluetoothAdapter.isDiscovering()) {
|
isDestroyed = true
|
||||||
mBluetoothAdapter.cancelDiscovery()
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops any current discovery, then starts a new one, recursively until service is stopped.
|
||||||
|
*/
|
||||||
|
def discover(): Unit = {
|
||||||
|
if (isDestroyed)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!bluetoothAdapter.isDiscovering()) {
|
||||||
|
Log.v(Tag, "Running discovery")
|
||||||
|
bluetoothAdapter.startDiscovery()
|
||||||
}
|
}
|
||||||
|
|
||||||
mBluetoothAdapter.startDiscovery()
|
MainHandler.postDelayed(new Runnable {
|
||||||
Log.i(TAG, "Discovery started")
|
override def run(): Unit = discover()
|
||||||
|
}, SCAN_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives newly discovered devices and connects to them.
|
||||||
|
*/
|
||||||
private final def mReceiver: BroadcastReceiver = new BroadcastReceiver() {
|
private final def mReceiver: BroadcastReceiver = new BroadcastReceiver() {
|
||||||
override def onReceive(context: Context, intent: Intent) {
|
override def onReceive(context: Context, intent: Intent) {
|
||||||
intent.getAction() match {
|
intent.getAction() match {
|
||||||
case BluetoothDevice.ACTION_FOUND =>
|
case BluetoothDevice.ACTION_FOUND =>
|
||||||
val btDevice: BluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
|
val device: Device =
|
||||||
mDeviceListener.onDeviceConnected(new Device(btDevice))
|
new Device(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), false)
|
||||||
|
devices = devices + (device.id -> device)
|
||||||
|
new ConnectThread(device, onConnected).start()
|
||||||
|
deviceListener.foreach(d => d(devices))
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def registerDeviceListener(listener: DeviceListener): Unit = {
|
/**
|
||||||
mDeviceListener = listener
|
* Registers a listener that is called whenever a new device is connected.
|
||||||
|
*/
|
||||||
|
def registerDeviceListener(listener: Map[Device.ID, Device] => Unit): Unit = {
|
||||||
|
deviceListener = deviceListener + listener
|
||||||
|
listener(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a device listener.
|
||||||
|
*/
|
||||||
|
def unregisterDeviceListener(listener: Map[Device.ID, Device] => Unit): Unit = {
|
||||||
|
deviceListener = deviceListener - listener
|
||||||
|
}
|
||||||
|
|
||||||
|
def onConnected(device: Device, socket: BluetoothSocket): Unit = {
|
||||||
|
val updatedDevice: Device = new Device(device.bluetoothDevice, true)
|
||||||
|
devices = devices + (device.id -> updatedDevice)
|
||||||
|
connections = connections + (device.id -> new TransferThread(updatedDevice, socket, onReceive))
|
||||||
|
connections(device.id).start()
|
||||||
|
deviceListener.foreach(d => d(devices))
|
||||||
|
}
|
||||||
|
|
||||||
|
def send(device: Device.ID, message: Message): Unit = {
|
||||||
|
connections.apply(device).send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
def onReceive(device: Device.ID, message: Message): Unit = {
|
||||||
|
MainHandler.post(new Runnable {
|
||||||
|
override def run(): Unit =
|
||||||
|
Toast.makeText(ChatService.this, devices(device).name + " sent: " + message, Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.nutomic.ensichat.bluetooth
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to connect to another device and calls [[onConnected]] on success.
|
||||||
|
*/
|
||||||
|
class ConnectThread(device: Device, onConnected: (Device, BluetoothSocket) => Unit)
|
||||||
|
extends Thread {
|
||||||
|
|
||||||
|
val Tag = "ConnectThread"
|
||||||
|
|
||||||
|
val socket: BluetoothSocket =
|
||||||
|
device.bluetoothDevice.createInsecureRfcommSocketToServiceRecord(ChatService.appUuid)
|
||||||
|
|
||||||
|
override def run(): Unit = {
|
||||||
|
try {
|
||||||
|
socket.connect()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
try {
|
||||||
|
socket.close()
|
||||||
|
} catch {
|
||||||
|
case e2: IOException =>
|
||||||
|
Log.e(Tag, "Failed to close socket", e2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(Tag, "Successfully connected to device " + device.name)
|
||||||
|
onConnected(device, socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,8 +2,34 @@ package com.nutomic.ensichat.bluetooth
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
|
|
||||||
class Device(mBluetoothDevice: BluetoothDevice) {
|
object Device {
|
||||||
|
|
||||||
def name = mBluetoothDevice.getName()
|
/**
|
||||||
|
* Holds bluetooth device IDs, which are just wrapped addresses (used for type safety).
|
||||||
|
*
|
||||||
|
* @param Id A bluetooth device address.
|
||||||
|
*/
|
||||||
|
class ID(private val Id: String) {
|
||||||
|
override def hashCode = Id.hashCode
|
||||||
|
override def equals(a: Any) = a match {
|
||||||
|
case other: Device.ID => Id == other.Id
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds information about a remote bluetooth device.
|
||||||
|
*/
|
||||||
|
class Device(BluetoothDevice: BluetoothDevice, Connected: Boolean) {
|
||||||
|
|
||||||
|
def id = new Device.ID(bluetoothDevice.getAddress)
|
||||||
|
|
||||||
|
def name = BluetoothDevice.getName
|
||||||
|
|
||||||
|
def connected = Connected
|
||||||
|
|
||||||
|
def bluetoothDevice = BluetoothDevice
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.nutomic.ensichat.bluetooth
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
import android.bluetooth.{BluetoothAdapter, BluetoothServerSocket, BluetoothSocket}
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for incoming connections from other devices.
|
||||||
|
*/
|
||||||
|
class ListenThread(name: String, adapter: BluetoothAdapter,
|
||||||
|
onConnected: (Device, BluetoothSocket) => Unit) extends Thread {
|
||||||
|
|
||||||
|
val Tag: String = "ListenThread"
|
||||||
|
|
||||||
|
val ServerSocket: BluetoothServerSocket =
|
||||||
|
try {
|
||||||
|
adapter.listenUsingInsecureRfcommWithServiceRecord(name, ChatService.appUuid)
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to create listener", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
override def run(): Unit = {
|
||||||
|
var socket: BluetoothSocket = null
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
// This is a blocking call and will only return on a
|
||||||
|
// successful connection or an exception
|
||||||
|
socket = ServerSocket.accept()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to accept new connection", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val device: Device = new Device(socket.getRemoteDevice, true)
|
||||||
|
onConnected(device, socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def cancel(): Unit = {
|
||||||
|
try {
|
||||||
|
ServerSocket.close()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to close listener", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.nutomic.ensichat.bluetooth
|
||||||
|
|
||||||
|
import java.io.{OutputStream, InputStream}
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket
|
||||||
|
import android.util.Log
|
||||||
|
import com.nutomic.ensichat.Message
|
||||||
|
import java.io.IOException
|
||||||
|
/**
|
||||||
|
* Transfers data between connnected devices.
|
||||||
|
*
|
||||||
|
* @param device The bluetooth device to interact with.
|
||||||
|
* @param socket An open socket to the given device.
|
||||||
|
* @param onReceive Called when a message was received from the other device.
|
||||||
|
*/
|
||||||
|
class TransferThread(device: Device, socket: BluetoothSocket,
|
||||||
|
onReceive: (Device.ID, Message) => Unit) extends Thread {
|
||||||
|
|
||||||
|
val Tag: String = "TransferThread"
|
||||||
|
|
||||||
|
val InStream: InputStream =
|
||||||
|
try {
|
||||||
|
socket.getInputStream()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to open stream", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val OutStream: OutputStream =
|
||||||
|
try {
|
||||||
|
socket.getOutputStream()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to open stream", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
override def run(): Unit = {
|
||||||
|
var buffer: Array[Byte] = new Array(1024)
|
||||||
|
|
||||||
|
// Keep listening to the InputStream while connected
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
InStream.read(buffer)
|
||||||
|
val msg: Message = Message.fromByteArray(buffer)
|
||||||
|
onReceive(device.id, msg)
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Disconnected from device", e);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def send(message: Message): Unit = {
|
||||||
|
try {
|
||||||
|
OutStream.write(message.toByteArray())
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to write message", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def cancel(): Unit = {
|
||||||
|
try {
|
||||||
|
socket.close()
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(Tag, "Failed to close socket", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,30 +3,30 @@ package com.nutomic.ensichat.fragments
|
||||||
import android.app.ListFragment
|
import android.app.ListFragment
|
||||||
import android.content.{ComponentName, Context, Intent, ServiceConnection}
|
import android.content.{ComponentName, Context, Intent, ServiceConnection}
|
||||||
import android.os.{Bundle, IBinder}
|
import android.os.{Bundle, IBinder}
|
||||||
|
import android.util.Log
|
||||||
import android.view.{LayoutInflater, View, ViewGroup}
|
import android.view.{LayoutInflater, View, ViewGroup}
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.{ArrayAdapter, ListView}
|
||||||
import com.nutomic.ensichat.R
|
|
||||||
import com.nutomic.ensichat.bluetooth.ChatService.DeviceListener
|
|
||||||
import com.nutomic.ensichat.bluetooth.{ChatService, ChatServiceBinder, Device}
|
import com.nutomic.ensichat.bluetooth.{ChatService, ChatServiceBinder, Device}
|
||||||
import com.nutomic.ensichat.util.DevicesAdapter
|
import com.nutomic.ensichat.util.DevicesAdapter
|
||||||
|
import com.nutomic.ensichat.{Message, R}
|
||||||
|
|
||||||
class ContactsFragment extends ListFragment with DeviceListener {
|
class ContactsFragment extends ListFragment {
|
||||||
|
|
||||||
private var mChatService: ChatService = _
|
private var chatService: ChatService = _
|
||||||
|
|
||||||
private final val mChatServiceConnection: ServiceConnection = new ServiceConnection {
|
private final val mChatServiceConnection: ServiceConnection = new ServiceConnection {
|
||||||
override def onServiceConnected(componentName: ComponentName, iBinder: IBinder): Unit = {
|
override def onServiceConnected(componentName: ComponentName, iBinder: IBinder): Unit = {
|
||||||
val binder: ChatServiceBinder = iBinder.asInstanceOf[ChatServiceBinder]
|
val binder: ChatServiceBinder = iBinder.asInstanceOf[ChatServiceBinder]
|
||||||
mChatService = binder.getService()
|
chatService = binder.getService()
|
||||||
mChatService.registerDeviceListener(ContactsFragment.this)
|
chatService.registerDeviceListener(onDeviceConnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onServiceDisconnected(componentName: ComponentName): Unit = {
|
override def onServiceDisconnected(componentName: ComponentName): Unit = {
|
||||||
mChatService = null
|
chatService = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mAdapter: ArrayAdapter[Device] = _
|
private var adapter: ArrayAdapter[Device] = _
|
||||||
|
|
||||||
override def onCreateView(inflater: LayoutInflater, container: ViewGroup,
|
override def onCreateView(inflater: LayoutInflater, container: ViewGroup,
|
||||||
savedInstanceState: Bundle): View = {
|
savedInstanceState: Bundle): View = {
|
||||||
|
@ -37,8 +37,8 @@ class ContactsFragment extends ListFragment with DeviceListener {
|
||||||
override def onCreate(savedInstanceState: Bundle): Unit = {
|
override def onCreate(savedInstanceState: Bundle): Unit = {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
mAdapter = new DevicesAdapter(getActivity)
|
adapter = new DevicesAdapter(getActivity)
|
||||||
setListAdapter(mAdapter)
|
setListAdapter(adapter)
|
||||||
getActivity.bindService(new Intent(getActivity, classOf[ChatService]),
|
getActivity.bindService(new Intent(getActivity, classOf[ChatService]),
|
||||||
mChatServiceConnection, Context.BIND_AUTO_CREATE)
|
mChatServiceConnection, Context.BIND_AUTO_CREATE)
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,27 @@ class ContactsFragment extends ListFragment with DeviceListener {
|
||||||
override def onDestroy(): Unit = {
|
override def onDestroy(): Unit = {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
getActivity.unbindService(mChatServiceConnection)
|
getActivity.unbindService(mChatServiceConnection)
|
||||||
|
chatService.unregisterDeviceListener(onDeviceConnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onDeviceConnected(device: Device): Unit = {
|
/**
|
||||||
mAdapter.add(device)
|
* Displays all connected devices in the listview.
|
||||||
|
*/
|
||||||
|
def onDeviceConnected(devices: Map[Device.ID, Device]): Unit = {
|
||||||
|
val filtered = devices.filter{ case (_, d) => d.connected }
|
||||||
|
getActivity.runOnUiThread(new Runnable {
|
||||||
|
override def run(): Unit = {
|
||||||
|
adapter.clear()
|
||||||
|
filtered.values.foreach(f => adapter.add(f))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a ping message to the clicked device.
|
||||||
|
*/
|
||||||
|
override def onListItemClick(l: ListView, v: View, position: Int, id: Long): Unit = {
|
||||||
|
chatService.send(adapter.getItem(position).id, new Message("Ping"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.nutomic.ensichat
|
||||||
|
|
||||||
|
object Message {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new message from transferred bytes.
|
||||||
|
*/
|
||||||
|
def fromByteArray(data: Array[Byte]): Message = {
|
||||||
|
return new Message(new String(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all messages that can be passed between bluetooth devices.
|
||||||
|
*
|
||||||
|
* Provides methods for (de-)serialization.
|
||||||
|
*/
|
||||||
|
class Message(text: String) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts message to bytes for transfer.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def toByteArray(): Array[Byte] = {
|
||||||
|
return text.getBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = text
|
||||||
|
|
||||||
|
}
|
Reference in New Issue