I. Introduction▲
Pour bien commencer, assurons-nous d'avoir le droit de l'utiliser et que le terminal qui exécute notre application possède un périphérique Bluetooth.
<
uses
-
permission android:name=
"android.permission.BLUETOOTH"
/>
BluetoothAdapter blueAdapter =
BluetoothAdapter.getDefaultAdapter
(
);
if
(
blueAdapter ==
null
) {
// Le terminal ne possède pas le Bluetooth
}
Avant d'être en mesure d'utiliser le Bluetooth dans votre application, il peut être de bonne pratique de vérifier si le Bluetooth est activé.
II. Activer le Bluetooth▲
Comme il est fort probable que le Bluetooth ne soit pas activé au moment voulu (beaucoup d'utilisateurs le désactivent pour éviter de recevoir des notifications de connexion ou tout simplement pour économiser leur batterie), voyons les deux manières possibles de faire pour l'allumer.
La première façon est de demander à l'utilisateur de le faire lui-même. Pour cela nous allons afficher une boîte de dialogue demandant à l'utilisateur s'il veut oui ou non activer son Bluetooth. Je vous rassure, pas la peine de coder vous-même la boîte de dialogue, le système sait le faire tout seul. Elle contiendra le message dans la langue du système de l'utilisateur ainsi que deux boutons, « Oui » et « Non ».
if
(!
blueAdapter.isEnabled
(
)) {
Intent enableBtIntent =
new
Intent
(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult
(
enableBtIntent, REQUEST_ENABLE_BT);
}
En utilisant « onActivityResult() », vous serez capable de savoir si l'utilisateur a cliqué sur oui ou non. Si le Bluetooth s'est correctement allumé, vous aurez un « RESULT_OK ». Dans le cas contraire, le retour sera « RESULT_CANCELED ».
Vous pouvez également écouter l'Intent de broadcast « ACTION_STATE_CHANGED » pour savoir quand l'état du Bluetooth change. Vous pourrez alors utiliser « EXTRA_STATE » et « EXTRA_PREVIOUS_STATE » pour connaître l'état courant et l'état antérieur du Bluetooth. Les valeurs possibles sont « STATE_TURNING_ON », « STATE_ON », « STATE_TURNING_OFF » et « STATE_OFF ».
La deuxième façon de faire est d'activer le Bluetooth directement, sans demander son consentement à l'utilisateur. Pour ce faire il va nous falloir demander une nouvelle permission.
<
uses
-
permission android:name=
"android.permission.BLUETOOTH_ADMIN"
/>
if
(!
blueAdapter.isEnabled
(
)) {
blueAdapter.enable
(
);
Activer la visibilité de votre terminal par Bluetooth active automatiquement le Bluetooth s'il n'est pas déjà actif (cf. plus loin).
III. Découvrir les périphériques▲
De même, activer la découverte des périphériques proches peut réduire de manière significative le débit d'une connexion Bluetooth préalablement établie.
Avant de chercher les périphériques à proximité pour une éventuelle connexion, commençons par regarder parmi les périphériques que nous connaissons. Pour cela nous allons appeler une fonction nommée « getBondedDevices() » qui renvoie une liste de « BluetoothDevice », chacun étant un périphérique connu.
Set<
BluetoothDevice>
pairedDevices =
blueAdapter.getBondedDevices
(
);
Si le périphérique ne fait pas partie de ceux qui sont connus, il va falloir rechercher les terminaux visibles proches. Pour cela nous allons appeler « startDiscovery() », « cancelDiscovery() » permettant de stopper cette recherche. En général le processus dure une grosse dizaine de secondes, suivi d'une page qui affiche les périphériques découverts. Voyons comment faire pour récupérer les résultats.
// On crée un BroadcastReceiver pour ACTION_FOUND
private
final
BroadcastReceiver receiver =
new
BroadcastReceiver
(
) {
public
void
onReceive
(
Context context, Intent intent) {
String action =
intent.getAction
(
);
// Quand la recherche trouve un terminal
if
(
BluetoothDevice.ACTION_FOUND.equals
(
action)) {
// On récupère l'object BluetoothDevice depuis l'Intent
BluetoothDevice device =
intent.getParcelableExtra
(
BluetoothDevice.EXTRA_DEVICE);
// On ajoute le nom et l'adresse du périphérique dans un ArrayAdapter (par exemple pour l'afficher dans une ListView)
mArrayAdapter.add
(
device.getName
(
) +
"
\n
"
+
device.getAddress
(
));
}
}
}
;
// Inscrire le BroadcastReceiver
IntentFilter filter =
new
IntentFilter
(
BluetoothDevice.ACTION_FOUND);
registerReceiver
(
receiver, filter); // N'oubliez pas de le désinscrire lors du OnDestroy() !
La découverte des périphériques Bluetooth à proximité est très coûteuse en énergie. Pensez donc à appeler « cancelDiscovery » dès que possible.
Si les deux terminaux qui vont se connecter n'ont jamais été interconnectés, le système va automatiquement afficher une boîte de dialogue de confirmation. De ce fait vous n'avez pas à vous préoccuper au niveau de votre application de savoir si les terminaux se connaissent ou pas. La connexion va attendre que l'utilisateur valide la demande ou elle va échouer en cas de refus ou de timeout.
Par défaut, même si le Bluetooth est activé, les terminaux Android ne sont pas visibles. Pour activer la visibilité Bluetooth, nous allons procéder de la même manière que pour activer le Bluetooth. Nous allons demander à l'utilisateur de le faire par le biais d'une boîte de dialogue. Notez que par défaut la durée de visibilité est de 120 secondes, mais vous pouvez définir une durée de votre choix, inférieure ou égale à 300 secondes.
Intent discoverableIntent =
new
Intent
(
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra
(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, timeVisible); // Cette ligne permet de définir une durée de visibilité de notre choix
startActivityForResult
(
discoverableIntent, REQUEST_DISCOVERABLE_BT);
Une fois encore vous pourrez récupérer le choix de l'utilisateur dans le « onActivityResult() », de la même manière que précédemment. Un « RESULT_OK » vous assurera de la bonne visibilité de votre terminal.
Vous pouvez également être informé du changement de visibilité de votre terminal en inscrivant un « BroadcastReceiver » pour l'Intent « ACTION_SCAN_MODE_CHANGED ». Les champs « EXTRA_SCAN_MODE » et « EXTRA_PREVIOUS_SCAN_MODE » vous donneront les informations sur l'état précédent et actuel de la visibilité du périphérique. Les valeurs possibles sont « SCAN_MODE_CONNECTABLE_DISCOVERABLE », « SCAN_MODE_CONNECTABLE », ou « SCAN_MODE_NONE ».
Il n'est pas nécessaire pour un terminal d'être visible pour établir une connexion vers un autre terminal. Seul celui qui va héberger la connexion doit être visible pour que le client puisse le découvrir et initier la connexion.
IV. Établir une connexion côté serveur▲
La procédure à suivre est la suivante. Nous allons récupérer une « BluetoothServerSocket » en appelant « listenUsingRfcommWithServiceRecord(String, UUID) ». Puis nous allons attendre une connexion par le biais de la méthode « accept() » de la socket. Une fois le(s) client(s) connecté(s), on bloque les demandes arrivant avec un appel à « close() ».
Essayez de faire votre appel à la méthode « accept() » dans un Thread séparé, car la méthode est bloquante. Cela aura pour effet de provoquer un ANR (Activity Not Responding) si le client met trop de temps à se connecter. Notez que toutes les méthodes des classes « BluetoothServerSocket » et « BluetoothSocket » sont « thread-safe ».
Voici un thread simple qui accepte les connexions Bluetooth entrantes.
private
class
ServeurBluetooth extends
Thread {
private
final
BluetoothServerSocket blueServerSocket;
public
ServeurBluetooth
(
) {
// On utilise un objet temporaire qui sera assigné plus tard à blueServerSocket, car blueServerSocket est "final"
BluetoothServerSocket tmp =
null
;
try
{
// MON_UUID est l'UUID (comprenez identifiant serveur) de l'application. Cette valeur est nécessaire côté client également !
tmp =
blueAdapter.listenUsingRfcommWithServiceRecord
(
NOM, MON_UUID);
}
catch
(
IOException e) {
}
blueServerSocket =
tmp;
}
public
void
run
(
) {
BluetoothSocket blueSocket =
null
;
// On attend une erreur ou une connexion entrante
while
(
true
) {
try
{
blueSocket =
blueServerSocket.accept
(
);
}
catch
(
IOException e) {
break
;
}
// Si une connexion est acceptée
if
(
blueSocket !=
null
) {
// On fait ce qu'on veut de la connexion (dans un thread séparé), à vous de la créer
manageConnectedSocket
(
blueSocket);
blueServerSocket.close
(
);
break
;
}
}
}
// On stoppe l'écoute des connexions et on tue le thread
public
void
cancel
(
) {
try
{
blueServerSocket.close
(
);
}
catch
(
IOException e) {
}
}
}
V. Établir une connexion côté client▲
Voyons maintenant comment connecter un client à notre serveur. Nous allons supposer que nous avons déjà l'objet « BluetoothDevice » du terminal serveur (voir ci-avant).
private
class
ClientBluetooth extends
Thread {
private
final
BluetoothSocket blueSocket;
private
final
BluetoothDevice blueDevice;
public
ClientBluetooth
(
BluetoothDevice device) {
// On utilise un objet temporaire, car blueSocket et blueDevice sont "final"
BluetoothSocket tmp =
null
;
blueDevice =
device;
// On récupère un objet BluetoothSocket grâce à l'objet BluetoothDevice
try
{
// MON_UUID est l'UUID (comprenez identifiant serveur) de l'application. Cette valeur est nécessaire côté serveur également !
tmp =
device.createRfcommSocketToServiceRecord
(
MON_UUID);
}
catch
(
IOException e) {
}
blueSocket =
tmp;
}
public
void
run
(
) {
// On annule la découverte des périphériques (inutile puisqu'on est en train d'essayer de se connecter)
blueAdapter.cancelDiscovery
(
);
try
{
// On se connecte. Cet appel est bloquant jusqu'à la réussite ou la levée d'une erreur
blueSocket.connect
(
);
}
catch
(
IOException connectException) {
// Impossible de se connecter, on ferme la socket et on tue le thread
try
{
blueSocket.close
(
);
}
catch
(
IOException closeException) {
}
return
;
}
// Utilisez la connexion (dans un thread séparé) pour faire ce que vous voulez
manageConnectedSocket
(
blueSocket);
}
// Annule toute connexion en cours et tue le thread
public
void
cancel
(
) {
try
{
blueSocket.close
(
);
}
catch
(
IOException e) {
}
}
}
VI. Remerciements▲
Je voudrais remercier Claude Leloup et _Max_ pour leur relecture attentive.