Bug(s) or Feature(s)

Hex Encoding/Decoding

Work with Hex encoded data to and from your device


The largest number of issues and forks are created around how to change the encoding/decoding of message data when communicating with the device. Some examples of this are:

which have been solved by the community. One of the most important features was the ability for you to create and customize these within your own application, without needing to fork/modify/install locally the project. The following describes how to implement the request from issues 50 and 51 in order to work with HEX data.

Installation

First thing first, you need to grab the latest version:

npm install --save react-native-bluetooth-classic

Android

Next you'll want to sanity check that things are working. Once you've installed and run/sync Gradle you should have the PackageList class generated and containing the RNBluetoothClassicPackage.

// com.facebook.react.PackageList

  public ArrayList<ReactPackage> getPackages() {
    return new ArrayList<>(Arrays.<ReactPackage>asList(
      new MainReactPackage(),
      new RNBluetoothClassicPackage()
    ));
  }

Create your Implementations

There are a number of custom classes that are required, I'm not entirely happy with it as is, but it works and can be further absracted for a future release. This assumes that the rfcomm connector/acceptor are going to be used to perform connections. This means we only need to:

Implement DeviceConnection

Since we're not changing how the device communicates, only how the incoming and outgoing data is encoded/decoded, we should just need to extend the AbstractDeviceConnection and override the required methods. Since Hex data is more of a stream, we don't need to worry about the deliminater.

// com.example.bluetooth.conn.HexStreamDeviceConnectionImpl extends AbstractDeviceConnection

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

/**
 * Incoming bytes are in hex form, they need to be 
 */
@Override
protected void receivedData(byte[] bytes) {
  char[] hexChars = new char[bytes.length * 2];
  for (int j = 0; j < bytes.length; j++) {
    int v = bytes[j] & 0xFF;
    hexChars[j * 2] = HEX_ARRAY[v >>> 4];
    hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
  }

  String data = new String(hexChars);
  
  // Add them to the buffer or send directly to the onReceivedData listener based on
  // the requirements.  Most likely the latter.
}

Don't forget to do the Accept DeviceConnection if you're going to need a server connection.

Implement DeviceConnectionFactory

If your DeviceConnection requires some configuration or logic, you may want to create your own DeviceConnectionFactory to perform that logic. Otherwise you can just lambda it up during configuration.

Implement RNBluetoothClassicPackage

Finally you'll need to customize the package so that you create the appropriate RNBluetoothClassicModule. This isn't needed, but it makes life a lot simpler when working on the next step.

public abstract class CustomBluetoothPackage {
  public static RNBluetoothClassicPackage PACKAGE = RNBluetoothClassicPackage.DEFAULT_BUILDER
    .withConnectionFactory('hex', () -> new HexStreamDeviceConnectionImpl())  // Does not override 'delimited' so it's also available as a CONNECTION_TYPE
    .build();
}

It's wise to start with RNBluetoothClassicPackage.DEFAULT_BUILDER if you're not changing the connectors/acceptors.

React Native Modules

Once we have the DeviceConnection(s) and possibly DeviceConnectionFactory(s) we need to inform pass these into the RNBluetoothClassicModule. This can be done in a number of ways:

Customize Auto Linking

The preferred method (from React Native) is to provide a react-native.config.js file which customizes the react-native-bluetooth-classic dependency configuration. For this to work (easily) you'll need to create your own ReactPackage that builds the appropriate RNBluetoothClassicModule and then tell auto linking to use it:

// react-native.config.js
// This file must be added setting android to null so that auto linking is skipped
module.exports = {
  dependencies: {
    'react-native-bluetooth-classic': {
      platforms: {
        android: {
          packageImportPath: 'import com.example.bluetooth.CustomBluetoothPackage;',
          packageInstance: 'CustomBluetoothPackage.PACKAGE'
        }
      }
    }
  }
}

Skip Auto Linking Completely

You can skip auto linking completely by nulling out the Android dependency:

// react-native.config.js
// This file must be added setting android to null so that auto linking is skipped
module.exports = {
  dependencies: {
    'react-native-bluetooth-classic': {
      platforms: {
        android: null
      }
    }
  }
}

and then manually adding the custom package to React Native:

// MainApplication.java 
@Override
protected List<ReactPackage> getPackages() {
  @SuppressWarnings("UnnecessaryLocalVariable")
  List<ReactPackage> packages = new PackageList(this).getPackages();
  // Packages that cannot be autolinked yet can be added manually here, for example:
  packages.add(CustomBluetoothPackage.PACKAGE)
  return packages;
}

IOS

IOS has not currently been updated to use new DeviceConnection.