Java tutorial
/* * Copyright (C) 2014 Dario Scoppelletti, <http://www.scoppelletti.it/>. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.scoppelletti.mobilepower.bluetooth; import android.bluetooth.*; import android.content.*; import android.os.*; import org.apache.commons.lang3.*; import org.slf4j.*; import it.scoppelletti.mobilepower.os.*; /** * Gestore dei dispositivi Bluetooth. * * @since 1.0 */ public class BTManager { private static final Logger myLogger = LoggerFactory.getLogger("BTManager"); private final Context myCtx; private BluetoothAdapter myBTAdapter; private BTTask myPendingTask; private OnBTListener myOnListener; /** * Costruttore. * * @param ctx Contesto. */ public BTManager(Context ctx) { if (ctx == null) { throw new NullPointerException("Argument ctx is null."); } myCtx = ctx; } /** * Restituisce il contesto. * * @return Oggetto. */ protected final Context getContext() { return myCtx; } /** * Imposta il gestore dell’evento di dispositivo rilevato. * * @param listener Oggetto. */ public final void setOnBTListener(OnBTListener obj) { myOnListener = obj; } /** * Inizializza il protocollo Bluetooth. * * <P>Il metodo statico {@code getDefaultAdapter} della classe * {@code BluetoothAdpter} verifica una-tantum di essere eseguito * all’interno di un thread che abbia inizializzato la coda dei * messaggi (anche se in realtà non sembra che ne abbia bisogno), e, * in caso contrario, inoltra un’eccezione.<BR> * Se il metodo {@code getDefaultAdapter} è eseguito per la prima * volta all’interno di un thread di lavoro, è pertanto * inoltrata l’eccezione, e l’unico modo per evitarlo sembra * essere inserire una <I>esecuzione di inizializzazione</I> del metodo * {@code getDefaultAdapter} nel thread principale (normalmente nel metodo * {@code onCreate} dell’attività principale.</P> * * <P>L’inizializzazione del protocollo Bluetooth non sembra essere * necessaria nel contesto di un servizio locale.</P> */ public static void initBluetooth() { // stackoverflow.com/questions/5920578, Kocus, 07/05/2011 // // BluetoothAdapter.getDefault() throwing RuntimeException while not in // Activity // // When I'm trying to get default bluetooth adapter while i'm NOT in // Activity, but in TimerTask (created inside Service) by using: // // BluetoothAdapter.getDefaultAdapter(); // // I get the following exception: // // Exception while invoking java.lang.RuntimeException: Can't create // handler inside thread that has not called Looper.prepare() // My application do not have any activity - so is there any possibility // to get this adapter away from Activity? // // - Toland H, 23/02/2013 // // This appears to be a bug in Android and still exists in Android 4.0 // (Ice Cream Sandwich) // // To workaround this and be able to call // BluetoothAdapter.getDefaultAdapter() from a worker thread (e.g. // AsyncTask), all you have to do is call // BluetoothAdapter.getDefaultAdapter() once on the main UI thread (e.g. // inside the onCreate() of your current activity). // // The RuntimeException is only thrown during initialization, and // BluetoothAdapter.getDefaultAdapter() only initializes the first time // you call it. Subsequent calls to it will succeed, even in background // threads. // // - user2304686, 21/04/2013 // // calling BluetoothAdapter.getDefaultAdapter() in the UI thread works, // but is not very practical. I have tried the workaround with a fake // Activity, but since I hate such workarounds, I decided to READ what // the error message really says and it is nothing more than that the // thread didn't call Looper.prepare(). // So calling Looper.prepare() just before calling // BluetoothAdapter.getDefaultAdapter() should solve the problem // anywhere, not just in a UI thread. // Works fine for me so far. // // - Flipper, 02/10/2013 // // Beware of a gotcha that exists in 2.3.x, but which has been fixed in // 4.x: if you call BluetoothAdapter.getDefaultAdapter() on any thread // other than the main application thread, that thread must call // Looper.prepare() and also subsequently Looper.loop(). // Failing to do so will cause at least one problem that I ran into: // accept() will succeed the first time you try to connect, but then not // succeed on successive attempts, even after using close() on the // ServerSocket. // This happens because in the older implementation of BluetoothAdapter, // the cleanup of the SDP entry occurs by way of a message posted to a // handler created on the thread where getDefaultAdapter() is called. if (Build.VERSION.SDK_INT < BuildCompat.VERSION_CODES.JELLY_BEAN) { BluetoothAdapter.getDefaultAdapter(); } } /** * Restituisce il nome di un dispositivo. * * @param device Dispositivo. * @return Nome del dispositivo. Se il nome del dispositivo non * è determinabile, restituisce l’indirizzo del * dispositivo. */ public static String getName(BluetoothDevice device) { String s; if (device == null) { throw new NullPointerException("Argument device is null."); } s = device.getName(); if (StringUtils.isBlank(s)) { s = device.getAddress(); } return s; } /** * Esegue un’operazione. * * @param task Operazione. */ public final void run(BTTask task) { if (task == null) { throw new NullPointerException("Argument task is null."); } myPendingTask = task; attachAdapter(); } /** * Esegue l’eventuale operazione richiesta. */ protected final void onRun() { try { cancelDiscovery(); if (myPendingTask != null) { myPendingTask.run(myBTAdapter); myPendingTask = null; } } finally { detachAdapter(); } } /** * Collega il gestore dei dispositivi. */ private void attachAdapter() { myBTAdapter = BluetoothAdapter.getDefaultAdapter(); if (myBTAdapter == null) { myLogger.warn("Bluetooth not supported."); if (myOnListener != null) { myOnListener.onNotSupported(); } return; } if (myBTAdapter.isEnabled()) { onRun(); } else { onDisabled(); } } /** * Gestisce l’evento di Bluetooth disabilitato. */ protected void onDisabled() { detachAdapter(); myLogger.warn("Bluetooth disabled."); if (myOnListener != null) { myOnListener.onDisabled(); } } /** * Scollega il gestore dei dispositivi. */ protected final void detachAdapter() { cancelDiscovery(); myBTAdapter = null; } /** * Annulla la ricerca dei dispositivi BT rilevabili. */ private void cancelDiscovery() { if (myBTAdapter == null) { return; } if (!myBTAdapter.isDiscovering()) { return; } if (myBTAdapter.cancelDiscovery()) { myLogger.trace("Discovery of Bluetooth devices is cancelled."); } else { myLogger.warn("Failed to cancel discovery of Bluetooth devices."); } } }