package com.zugaldia.robocar.software.controller.nes30; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.text.TextUtils; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Set; import java.util.UUID; import timber.log.Timber; /** * Manages the Bluetooth connection to the NES30 controller. */ public class Nes30Connection { private Context context; private String deviceAddress; private BluetoothAdapter bluetoothAdapter; /** * Public constructor. */ public Nes30Connection(Context context, String deviceAddress) { this.context = context; this.deviceAddress = deviceAddress; bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { Timber.e("This device does not support Bluetooth."); } else if (!isEnabled()) { Timber.d("Bluetooth isn't enabled, enabling: %b", bluetoothAdapter.enable()); } } public boolean isEnabled() { return (bluetoothAdapter != null && bluetoothAdapter.isEnabled()); } public Set<BluetoothDevice> getPairedDevices() { return bluetoothAdapter.getBondedDevices(); } /** * Checks whether the device is already paired. */ public BluetoothDevice getSelectedDevice() { Set<BluetoothDevice> pairedDevices = getPairedDevices(); for (BluetoothDevice pairedDevice : pairedDevices) { if (isSelectedDevice(pairedDevice.getAddress())) { return pairedDevice; } } return null; } public boolean startDiscovery() { registerReceiver(); return bluetoothAdapter.startDiscovery(); } // Create a BroadcastReceiver for ACTION_FOUND. private final BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // Discovery has found a device. if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object and its info from the Intent. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int bondState = device.getBondState(); String foundName = device.getName(); String foundAddress = device.getAddress(); // MAC address Timber.d("Discovery has found a device: %d/%s/%s", bondState, foundName, foundAddress); if (isSelectedDevice(foundAddress)) { createBond(device); } else { Timber.d("Unknown device, skipping bond attempt."); } } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); switch (state) { case BluetoothDevice.BOND_NONE: Timber.d("The remote device is not bonded."); break; case BluetoothDevice.BOND_BONDING: Timber.d("Bonding is in progress with the remote device."); break; case BluetoothDevice.BOND_BONDED: Timber.d("The remote device is bonded."); break; default: Timber.d("Unknown remote device bonding state."); break; } } } }; private void registerReceiver() { // Register for broadcasts when a device is discovered. IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); context.registerReceiver(receiver, filter); } public void cancelDiscovery() { bluetoothAdapter.cancelDiscovery(); context.unregisterReceiver(receiver); } private boolean isSelectedDevice(String foundAddress) { // MAC address is set and recognized return !TextUtils.isEmpty(deviceAddress) && deviceAddress.equals(foundAddress); } /** * Pair with the specific device. */ public boolean createBond(BluetoothDevice device) { boolean result = device.createBond(); Timber.d("Creating bond with: %s/%s/%b", device.getName(), device.getAddress(), result); return result; } /** * Remove bond with the specific device. */ public void removeBond(BluetoothDevice device) { try { Timber.w("Removing bond."); Method m = device.getClass().getMethod("removeBond", (Class[]) null); m.invoke(device, (Object[]) null); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { Timber.e(e, "Failed to remove bond."); } } }