Android Open Source - BLEService Characteristic Details Activity






From Project

Back to project page BLEService.

License

The source code is released under:

Copyright (c) 2014, Ratio LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: ...

If you think the Android project BLEService listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.ratio.btdemo;
//from   w ww .j  a  va2  s.c om
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.ratio.btdemo.ServicesActivity.RSSIReceiver;
import com.ratio.btdemo.adapters.CharacteristicsListAdapter;
import com.ratio.btdemo.adapters.ServicesListAdapter;
import com.ratio.deviceService.BTCharacteristicProfile;
import com.ratio.deviceService.BTDeviceProfile;
import com.ratio.deviceService.BTServiceProfile;
import com.ratio.deviceService.DeviceService;
import com.ratio.deviceService.IDeviceCommand;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ToggleButton;

public class CharacteristicDetailsActivity extends Activity {
  protected final static String       TAG = CharacteristicDetailsActivity.class.getSimpleName();
  public static final String        EXTRA_DEVICE = "device";
  public static final String        EXTRA_SERVICE = "service";
  public static final String        EXTRA_CHARACTERISTIC = "characteristic";
    public static final String         PREFS = "BT_PREFS";
  public static final String         RSSI_REFRESH_MSEC = "rssi_refresh_msec";
  public static final int          RSSI_REFRESH_MSEC_DEFAULT = 2000;
  
  protected boolean            mNotificationEnabled = false;
  protected IDeviceCommand         mService;            // service for device interface.
  protected DeviceServiceConnection     mConnection;          // connection to the device service.
  protected BluetoothDevice        mDevice;
  protected BTServiceProfile        mServiceProfile;
  protected BTCharacteristicProfile    mCharacteristicProfile;
  protected CharacteristicsListAdapter  mAdapter;
  protected RSSIReceiver          mRSSIReceiver;
  protected ConnectionStateReceiver    mConnectionStateReceiver;
  protected CharacteristicReadReceiver  mCharacteristicReadReceiver;
  protected CharacteristicChangedReceiver  mCharacteristicChangedReceiver;
  protected CharacteristicWriteReceiver  mCharacteristicWriteReceiver;
  
  protected Timer              mRSSITimer;
  protected TimerTask            mRSSITimerTask;
  private byte[]               mRawValue = null;
  private int               mIntValue = 0;
  private String               mAsciiValue = "";
  private String               mStrValue = "";
  private String               mLastUpdateTime = "";
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE);
    mServiceProfile = getIntent().getParcelableExtra(EXTRA_SERVICE);
    mCharacteristicProfile = getIntent().getParcelableExtra(EXTRA_CHARACTERISTIC);
    setContentView(R.layout.peripheral_details_characteristic);
    initializeDeviceService();
    mRSSITimer = new Timer();
  }  
  
  
  @Override
  public void onContentChanged() {
    updateUI();
  }

  public void updateUI() {
    final BluetoothGattService service = mServiceProfile.getService();
    final BluetoothGattCharacteristic characteristic = mCharacteristicProfile.getCharacteristic();
      TextView charPeripheralName = (TextView) findViewById(R.id.char_details_peripheral_name);
      TextView charPeripheralAddress = (TextView) findViewById(R.id.char_details_peripheral_address);
      TextView charServiceName = (TextView) findViewById(R.id.char_details_service);
      TextView charServiceUuid = (TextView) findViewById(R.id.char_details_service_uuid);
      TextView charName = (TextView) findViewById(R.id.char_details_name);
      TextView charUuid = (TextView) findViewById(R.id.char_details_uuid);
      
      TextView charDataType = (TextView) findViewById(R.id.char_details_type);
      TextView charProperties = (TextView) findViewById(R.id.char_details_properties);
      
      TextView charStrValue = (TextView) findViewById(R.id.char_details_ascii_value);
      TextView charDecValue = (TextView) findViewById(R.id.char_details_decimal_value);
      final EditText charHexValue = (EditText) findViewById(R.id.char_details_hex_value);
      charHexValue.setText("0x");
      TextView charDateValue = (TextView) findViewById(R.id.char_details_timestamp);
      
      ToggleButton notificationBtn = (ToggleButton) findViewById(R.id.char_details_notification_switcher);
      Button readBtn = (Button) findViewById(R.id.char_details_read_btn);
      Button writeBtn = (Button) findViewById(R.id.char_details_write_btn);
      //Button writeBtn.setTag(fields.charHexValue);
      
      readBtn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        try {
          mService.readCharacteristic(mDevice.getAddress(), service.getUuid().toString(),
              characteristic.getUuid().toString());
        } catch (RemoteException rex) {
          rex.printStackTrace();
        }
      }
    });

      writeBtn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        String newValue =  charHexValue.getText().toString().toLowerCase(Locale.getDefault());
        byte[] dataToWrite = parseHexStringToBytes(newValue);
        try {
          mService.writeCharacteristicByteArray(mDevice.getAddress(), service.getUuid().toString(), 
                              characteristic.getUuid().toString(), dataToWrite);
        } catch (RemoteException rex) {
          rex.printStackTrace();
        }
      }
    });            
      
      notificationBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked == mNotificationEnabled) return; // no need to update anything
        try {
          mService.setCharacteristicNotification(mDevice.getAddress(), service.getUuid().toString(), 
                              characteristic.getUuid().toString(), isChecked);
        } catch (RemoteException rex) {
          rex.printStackTrace();
        }
        mNotificationEnabled = isChecked;
      }
    } );
   
      // set proper values into the view
      charPeripheralName.setText(mDevice.getName());
      charPeripheralAddress.setText(mDevice.getAddress());
      
      String tmp = characteristic.getUuid().toString().toLowerCase(Locale.getDefault());
      charServiceUuid.setText(tmp);
      charServiceName.setText(BleNamesResolver.resolveServiceName(tmp));
      
      String uuid = characteristic.getUuid().toString().toLowerCase(Locale.getDefault());
      String name = BleNamesResolver.resolveCharacteristicName(uuid);
      
      charName.setText(name);
      charUuid.setText(uuid);
      
      int format = getValueFormat(characteristic);
      charDataType.setText(BleNamesResolver.resolveValueTypeDescription(format));
      int props = characteristic.getProperties();
      String propertiesString = getCharacteristicPropertiesString(props);
      charProperties.setText(propertiesString);
      
      notificationBtn.setEnabled((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0);
      notificationBtn.setChecked(mNotificationEnabled);
      readBtn.setEnabled((props & BluetoothGattCharacteristic.PROPERTY_READ) != 0);
      writeBtn.setEnabled((props & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) != 0);
      charHexValue.setEnabled(writeBtn.isEnabled());
      
      charHexValue.setText(mAsciiValue);
      charStrValue.setText(mStrValue);
      charDecValue.setText(String.format("%d", mIntValue));
      charDateValue.setText(mLastUpdateTime);
  }
  
  // set up the broadcast receivers for service and device discovery
  @Override
  public void onResume() {
    super.onResume();
    mConnectionStateReceiver = new ConnectionStateReceiver();
    addReceiver(mConnectionStateReceiver, DeviceService.ACTION_CONNECTION_STATE);
    mRSSIReceiver = new RSSIReceiver();
    addReceiver(mRSSIReceiver, DeviceService.ACTION_READ_RSSI);
    mCharacteristicChangedReceiver = new CharacteristicChangedReceiver();
    addReceiver(mCharacteristicChangedReceiver, DeviceService.ACTION_CHARACTERISTIC_CHANGED);
    mCharacteristicReadReceiver = new CharacteristicReadReceiver();
    addReceiver(mCharacteristicReadReceiver, DeviceService.ACTION_CHARACTERISTIC_READ);
    mCharacteristicWriteReceiver = new CharacteristicWriteReceiver();
    addReceiver(mCharacteristicWriteReceiver, DeviceService.ACTION_CHARACTERISTIC_WRITE);
    
  }

  @Override
  public void onPause() {
    super.onPause();
    unregisterReceiver(mConnectionStateReceiver);
    unregisterReceiver(mCharacteristicChangedReceiver);
    unregisterReceiver(mCharacteristicWriteReceiver);
    unregisterReceiver(mCharacteristicReadReceiver);
    unregisterReceiver(mRSSIReceiver);
  }
    
  @Override
  public void onDestroy() {
    super.onDestroy();
    unbindService(mConnection);
  }
  
  protected void addReceiver(BroadcastReceiver receiver, String action) {
    IntentFilter filter = new IntentFilter();
    filter.addAction(action);
    registerReceiver(receiver, filter);
  }
  
  private void initializeDeviceService() {
      mConnection = new DeviceServiceConnection();
      Intent i = new Intent(this, DeviceService.class);
      boolean ret = bindService(i, mConnection, Context.BIND_AUTO_CREATE);
      Log.d(TAG, "initService() bound with " + ret);
  }

  // interface to make requests from the device service. data is returned via broadcast receivers.
    private class DeviceServiceConnection implements ServiceConnection {

        public void onServiceConnected(ComponentName name, IBinder boundService) {
            mService = IDeviceCommand.Stub.asInterface((IBinder) boundService);
            Log.d(TAG, "onServiceConnected() connected");
        }
            
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
            Log.d(TAG, "onServiceDisconnected() disconnected");
        }
    }
     
    public class ConnectionStateReceiver extends com.ratio.deviceService.receivers.ConnectionStateReceiver {

    @Override
    public void onConnectionState(String deviceAdress, int state) {
          TextView tvState = (TextView) CharacteristicDetailsActivity.this.findViewById(R.id.peripheral_status);
           tvState.setText(Integer.toString(state));            
    }
    }
    
    protected class RSSITimerTask extends TimerTask {
    @Override
    public void run() {
      try {
        mService.readRemoteRSSI(mDevice.getAddress());  
      } catch (RemoteException rex) {
        rex.printStackTrace();
      }
    }
    }
    
  
  public static byte[] parseHexStringToBytes(final String hex) {
    String tmp = null;
    if (hex.startsWith("0x")) {
      tmp = hex.substring(2).replaceAll("[^[0-9][a-f]]", "");
    } else {
      tmp = hex;
    }
    byte[] bytes = new byte[tmp.length() / 2]; // every two letters in the string are one byte finally
    
    String part = "";
    
    for(int i = 0; i < bytes.length; ++i) {
      part = "0x" + tmp.substring(i*2, i*2+2);
      bytes[i] = Long.decode(part).byteValue();
    }
    
    return bytes;
  }
  
  public static String getHexString(byte[] rawValue) {
       if (rawValue != null && rawValue.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(rawValue.length);
            for (byte byteChar : rawValue) {
                stringBuilder.append(String.format("%02X", byteChar));
            }
            return "0x" + stringBuilder.toString();
        } else {
          return "";
        } 
  }
  
     /* reads and return what what FORMAT is indicated by characteristic's properties
     * seems that value makes no sense in most cases */
    public int getValueFormat(BluetoothGattCharacteristic ch) {
      int properties = ch.getProperties();
      
      if((BluetoothGattCharacteristic.FORMAT_FLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_FLOAT;
      if((BluetoothGattCharacteristic.FORMAT_SFLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SFLOAT;
      if((BluetoothGattCharacteristic.FORMAT_SINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT16;
      if((BluetoothGattCharacteristic.FORMAT_SINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT32;
      if((BluetoothGattCharacteristic.FORMAT_SINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT8;
      if((BluetoothGattCharacteristic.FORMAT_UINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT16;
      if((BluetoothGattCharacteristic.FORMAT_UINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT32;
      if((BluetoothGattCharacteristic.FORMAT_UINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT8;
      
      return 0;
    }
    
    public static String getCharacteristicPropertiesString(int props) {
      String propertiesString = String.format("0x%04X [", props);
      if((props & BluetoothGattCharacteristic.PROPERTY_READ) != 0) propertiesString += "read ";
      if((props & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) propertiesString += "write ";
      if((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) propertiesString += "notify ";
      if((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) propertiesString += "indicate ";
      if((props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) propertiesString += "write_no_response ";
      propertiesString += "]";
      return propertiesString;

    }

    /* get characteristic's value (and parse it for some types of characteristics) 
     * before calling this You should always update the value by calling requestCharacteristicValue() */
    public void getCharacteristicValue(BluetoothGattCharacteristic ch) {   
        byte[] rawValue = ch.getValue();
        String strValue = null;
        int intValue = 0;
        
        // lets read and do real parsing of some characteristic to get meaningful value from it 
        UUID uuid = ch.getUuid();
        
        if(uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT)) { // heart rate
          // follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
          // first check format used by the device - it is specified in bit 0 and tells us if we should ask for index 1 (and uint8) or index 2 (and uint16)
          int index = ((rawValue[0] & 0x01) == 1) ? 2 : 1;
          // also we need to define format
          int format = (index == 1) ? BluetoothGattCharacteristic.FORMAT_UINT8 : BluetoothGattCharacteristic.FORMAT_UINT16;
          // now we have everything, get the value
          intValue = ch.getIntValue(format, index);
          strValue = intValue + " bpm"; // it is always in bpm units
        }
        else if (uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT) || // manufacturer name string
             uuid.equals(BleDefinedUUIDs.Characteristic.MODEL_NUMBER_STRING) || // model number string)
             uuid.equals(BleDefinedUUIDs.Characteristic.FIRMWARE_REVISION_STRING)) // firmware revision string
        {
          // follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml etc.
          // string value are usually simple utf8s string at index 0
          strValue = ch.getStringValue(0);
        }
        else if(uuid.equals(BleDefinedUUIDs.Characteristic.APPEARANCE)) { // appearance
          // follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
          intValue  = ((int)rawValue[1]) << 8;
          intValue += rawValue[0];
          strValue = BleNamesResolver.resolveAppearance(intValue);
        }
        else if(uuid.equals(BleDefinedUUIDs.Characteristic.BODY_SENSOR_LOCATION)) { // body sensor location
          // follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml
          intValue = rawValue[0];
          strValue = BleNamesResolver.resolveHeartRateSensorLocation(intValue);
        }
        else if(uuid.equals(BleDefinedUUIDs.Characteristic.BATTERY_LEVEL)) { // battery level
          // follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml
          intValue = rawValue[0];
          strValue = "" + intValue + "% battery level";
        }        
        else {
          // not known type of characteristic, so we need to handle this in "general" way
          // get first four bytes and transform it to integer
          intValue = 0;
          if(rawValue.length > 0) intValue = (int)rawValue[0];
          if(rawValue.length > 1) intValue = intValue + ((int)rawValue[1] << 8); 
          if(rawValue.length > 2) intValue = intValue + ((int)rawValue[2] << 8); 
          if(rawValue.length > 3) intValue = intValue + ((int)rawValue[3] << 8); 
          
            if (rawValue.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(rawValue.length);
                for(byte byteChar : rawValue) {
                  try {
                    stringBuilder.append(String.format("%c", byteChar));
                  } catch (Exception ex) {
                    // swallow illegal characters
                  }
                }
                strValue = stringBuilder.toString();
            }
        }
        
        String timestamp = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS").format(new Date());
      mRawValue = rawValue;
      mIntValue = intValue;
      mAsciiValue = getHexString(rawValue);
      mStrValue = strValue;
      mLastUpdateTime = timestamp;
    }
    
    public class CharacteristicReadReceiver extends com.ratio.deviceService.receivers.CharacteristicReceiver {
      @Override
    public void onCharacteristic(String deviceAddress,
        BluetoothGattService service,
        BluetoothGattCharacteristic characteristic, 
        byte[] chValue,
        int status) {
        getCharacteristicValue(characteristic);
        updateUI();
      }
    }
    
    public class CharacteristicWriteReceiver extends com.ratio.deviceService.receivers.CharacteristicReceiver {
      @Override
    public void onCharacteristic(String deviceAddress,
        BluetoothGattService service,
        BluetoothGattCharacteristic characteristic, 
        byte[] chValue,
        int status) {
         getCharacteristicValue(characteristic);
        updateUI();
      }
    }
    
    public class CharacteristicChangedReceiver extends com.ratio.deviceService.receivers.CharacteristicReceiver {
    @Override
    public void onCharacteristic(String deviceAddress,
                  BluetoothGattService service,
                  BluetoothGattCharacteristic characteristic, 
                  byte[] chValue,
                  int status) {
        if (deviceAddress.equals(mDevice.getAddress()) && 
          service.getUuid().equals(mServiceProfile.getService().getUuid()) &&
          characteristic.getUuid().equals(mCharacteristicProfile.getCharacteristic().getUuid())) {
          getCharacteristicValue(characteristic);
          updateUI();
        }
      }
    }
    
    
    public class RSSIReceiver extends com.ratio.deviceService.receivers.RSSIReceiver {

    @Override
    public void onRSSI(String deviceAdress, int rssi, int status) {
        TextView tvRssi = (TextView) findViewById(R.id.peripheral_rssi);
        tvRssi.setText(Integer.toString(rssi) + " db");       
      
    }
    }

 }




Java Source Code List

com.ratio.btdemo.BleDefinedUUIDs.java
com.ratio.btdemo.BleNamesResolver.java
com.ratio.btdemo.CharacteristicDetailsActivity.java
com.ratio.btdemo.CharacteristicsActivity.java
com.ratio.btdemo.ScanActivity.java
com.ratio.btdemo.ServicesActivity.java
com.ratio.btdemo.adapters.CharacteristicsListAdapter.java
com.ratio.btdemo.adapters.ServicesListAdapter.java
com.ratio.deviceService.BTCharacteristicProfile.java
com.ratio.deviceService.BTDescriptorProfile.java
com.ratio.deviceService.BTDeviceProfile.java
com.ratio.deviceService.BTLEDeviceManager.java
com.ratio.deviceService.BTServiceProfile.java
com.ratio.deviceService.BTUUID.java
com.ratio.deviceService.BluetoothBroadcastReceiver.java
com.ratio.deviceService.DeviceErrorCodes.java
com.ratio.deviceService.DeviceService.java
com.ratio.deviceService.command.BTLECommandDisconnect.java
com.ratio.deviceService.command.BTLECommandDiscoverServices.java
com.ratio.deviceService.command.BTLECommandReadCharacteristic.java
com.ratio.deviceService.command.BTLECommandReadDescriptor.java
com.ratio.deviceService.command.BTLECommandSetCharacteristicNotification.java
com.ratio.deviceService.command.BTLECommandWriteCharacteristic.java
com.ratio.deviceService.command.BTLECommandWriteDescriptor.java
com.ratio.deviceService.command.BTLECommand.java
com.ratio.deviceService.receivers.CharacteristicReceiver.java
com.ratio.deviceService.receivers.CharactertisticListReceiver.java
com.ratio.deviceService.receivers.ConnectionStateReceiver.java
com.ratio.deviceService.receivers.DescriptorReceiver.java
com.ratio.deviceService.receivers.DeviceDiscoveredRecevier.java
com.ratio.deviceService.receivers.RSSIReceiver.java
com.ratio.deviceService.receivers.ServicesDiscoveredReceiver.java
com.ratio.exceptions.DeviceManagerException.java
com.ratio.exceptions.DeviceNameNotFoundException.java
com.ratio.util.BitUtils.java
com.ratio.util.StringUtil.java
com.ratio.util.UUIDUtils.java
org.bluetooth.bledemo.BleDefinedUUIDs.java
org.bluetooth.bledemo.BleNamesResolver.java
org.bluetooth.bledemo.BleWrapperUiCallbacks.java
org.bluetooth.bledemo.BleWrapper.java
org.bluetooth.bledemo.CharacteristicDetailsAdapter.java
org.bluetooth.bledemo.CharacteristicsListAdapter.java
org.bluetooth.bledemo.DeviceListAdapter.java
org.bluetooth.bledemo.HRDemoActivity.java
org.bluetooth.bledemo.PeripheralActivity.java
org.bluetooth.bledemo.ScanningActivity.java
org.bluetooth.bledemo.ServicesListAdapter.java