package iWish_Bluetooth; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; import iWish_Activities.Activities; import iWish_Control.ControlActivities; import iWish_Control.ControlConnection; import iWish_Control.ControlSession; import iWish_Session.Session; import java.util.ArrayList; import java.util.UUID; /** * Service for managing connection and data communication with a GATT server hosted on a * given Bluetooth LE device. */ public class BluetoothLeService extends Service { //servono per individuare il servizio e la caratteristica del servizio in un dispostitivo bt public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb"; // Heart Rate Measurement public static String HEART_RATE_SERVICE = "0000180d-0000-1000-8000-00805f9b34fb"; // Heart Rate Service public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; // Client Characteristic Config private final static String TAG = BluetoothLeService.class.getSimpleName(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private int mConnectionState = STATE_DISCONNECTED; private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTING = 1; private static final int STATE_CONNECTED = 2; public final static String ACTION_GATT_CONNECTED = "iWish_Activity.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "iWish_Activity.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "iWish_Activity.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "iWish_Activity.ACTION_DATA_AVAILABLE"; public final static String ACTION_DATA_ALL = "iWish_Activity.ACTION_DATA_ALL"; //lo utilizzo per inviare tutti i dati al grafico da definire public final static String EXTRA_DATA = "iWish_Activity.EXTRA_DATA"; public final static String EXTRA_MAX = "iWish_Activity.EXTRA_MAX"; public final static String EXTRA_MIN = "iWish_Activity.EXTRA_MIN"; public final static String EXTRA_MED = "iWish_Activity.EXTRA_MED"; public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(BluetoothLeService.HEART_RATE_MEASUREMENT); public final static UUID UUID_HEART_RATE_SERVICE = UUID.fromString(BluetoothLeService.HEART_RATE_SERVICE); private Activities mActivities; private Session mSession; private int sommaMed; private int divisoreMedia; private int hMin; private int hMax; private int hMed; private boolean mStarting; private long tempo; private int startDateTime; private int stopDateTime; private int durataTempo; private String totBeats; private int tempoInPausa; private int inizioPausa; //private String totBeatsLocal; private int beatsSelect; private ArrayList<String> totBeatsLocal; // Implements callback methods for GATT events that the app cares about. // For example, connection change and services discovered. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "Disconnected from GATT server."); broadcastUpdate(intentAction); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } }; private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); } private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // This is special handling for the Heart Rate Measurement profile. Data parsing is // carried out as per profile specifications: // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); int format = -1; if ((flag & 0x01) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(TAG, "Heart rate format UINT16."); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(TAG, "Heart rate format UINT8."); } final int heartRate = characteristic.getIntValue(format, 1); Log.d(TAG, String.format("Received heart rate: %d", heartRate)); //passo il battito attuale intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); //imposto max min e med if(mStarting){ MinMaxMed(String.valueOf(heartRate)); if(beatsSelect%5==0){ setBeatsTot(String.valueOf(heartRate)); } beatsSelect++; setBeatsTotLocal(String.valueOf(heartRate)); } intent.putExtra(EXTRA_MAX, ""+hMax); intent.putExtra(EXTRA_MIN, ""+hMin); intent.putExtra(EXTRA_MED, ""+hMed); } else { // For all other profiles, writes the data formatted in HEX. final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for(byte byteChar : data){ stringBuilder.append(String.format("%02X ", byteChar)); } intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); } } sendBroadcast(intent); } public class LocalBinder extends Binder { public BluetoothLeService getService() { return BluetoothLeService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { // After using a given device, you should make sure that BluetoothGatt.close() is called // such that resources are cleaned up properly. In this particular example, close() is // invoked when the UI is disconnected from the Service. close(); return super.onUnbind(intent); } private final IBinder mBinder = new LocalBinder(); /** * Initializes a reference to the local Bluetooth adapter. * @return Return true if the initialization is successful. */ public boolean initialize() { //dati per la media il max e il min sommaMed = 0; divisoreMedia = 0; hMin = 300; hMax = 30; hMed = 30; totBeats = ""; tempoInPausa =0; inizioPausa =0; //totBeatsLocal = ""; mStarting=false; beatsSelect=0; totBeatsLocal = new ArrayList<String>(); // For API level 18 and above, get a reference to BluetoothAdapter through // BluetoothManager. if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { Log.e(TAG, "Unable to initialize BluetoothManager."); return false; } } mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; } /** * Connects to the GATT server hosted on the Bluetooth LE device. * * @param address The device address of the destination device. * * @return Return true if the connection is initiated successfully. The connection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */ public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } // Previously connected device. Try to reconnect. if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING; return true; } else { return false; } } final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); return false; } // We want to directly connect to the device, so we are setting the autoConnect // parameter to false. mBluetoothGatt = device.connectGatt(this, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); mBluetoothDeviceAddress = address; mConnectionState = STATE_CONNECTING; return true; } /** * Disconnects an existing connection or cancel a pending connection. The disconnection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */ public void disconnect() { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.disconnect(); } /** * After using a given BLE device, the app must call this method to ensure resources are * released properly. */ public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; } /** * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} * callback. * * @param characteristic The characteristic to read from. */ public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); } /** * Enables or disables notification on a give characteristic. * * @param characteristic Characteristic to act on. * @param enabled If true, enable notification. False otherwise. */ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); // This is specific to Heart Rate Measurement. if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(BluetoothLeService.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } } //ritorna il GattService che si chiama heart rate service e ha characteristic Heart Rate Measurement public BluetoothGattService getSupportedGattService(){ if (mBluetoothGatt == null){ return null; } return mBluetoothGatt.getService(UUID_HEART_RATE_SERVICE); } public void setActivitiesAndSession(Activities attivita){ mActivities = attivita; mSession = new Session(); mSession.setKeyActivities(mActivities.getKeyActivities()); mSession.setStartDateActivities(mActivities.getStartDate()); tempo = System.currentTimeMillis(); startDateTime = (int) (tempo/1000); mSession.setStartDate(startDateTime); } public void onStart(){ mStarting = true; } public void onStop(){ if(!mStarting){ tempoInPausa = tempoInPausa + ((int) (System.currentTimeMillis()/1000) - inizioPausa); } mStarting = false; mSession.setBattitiMax(hMax); mSession.setBattitiMin(hMin); mSession.setBattitiMed(hMed); mSession.setBattiti(totBeats); long fine = System.currentTimeMillis(); int fineTime = (int) (fine/1000); int durataTempo = fineTime - startDateTime - tempoInPausa; mSession.setDurataTempo(durataTempo); try { Log.i("BluetoothLeService", "PRIMA DI INSERIRE NEL DB SESSION"); ControlSession.getIstanceControlSession().saveOnDbSession(mSession, getApplicationContext()); Log.i("BluetoothLeService", "DOPO L'INSERIMENTO NEL DB SESSION"); ControlConnection.getIstanceControlConnection().onInsertSession(totBeats); Log.i("BluetoothLeService", "DOPO L'INSERIMENTO online"); } catch (Exception e) { //Toast.makeText(c ,"errore di salvataggio", Toast.LENGTH_LONG).show(); Log.i("BluetoothLeService", "errore INSERIMENTO NEL DB SESSION"); e.printStackTrace(); } Log.i("WeightActivity", "SALVATAGGIO SUL DB ANDATO A BUON FINE"); } public void onPause(){ if(mStarting){ mStarting = false; inizioPausa =(int) (System.currentTimeMillis()/1000); } else{ mStarting = true; tempoInPausa = tempoInPausa + ((int) (System.currentTimeMillis()/1000) - inizioPausa); } } private void MinMaxMed(String data){ int dato; try { dato = Integer.parseInt(data); if(dato != 0){ divisoreMedia++; sommaMed += dato; } if(dato < hMin && dato!=0){ hMin = dato; if(hMax == 30){ hMax = dato; } } else{ if(dato > hMax){ hMax = dato; } } hMed = sommaMed / divisoreMedia; } catch (Exception e){ e.printStackTrace(); } } private void setBeatsTot(String beat){ if(!beat.equals("0")){ totBeats = totBeats + beat + ","; } } private void setBeatsTotLocal(String beat){ if(!beat.equals("0")){ if(totBeatsLocal.size() < 62){ totBeatsLocal.add(beat); } else{ totBeatsLocal.add(beat); totBeatsLocal.remove(0); } //totBeatsLocal = totBeatsLocal + beat + ","; } } public ArrayList<String> getBeatsTotLocal(){ return totBeatsLocal; } }