# CaptureSDK Features

CaptureSDK-level features for managing the SDK session. Covers retrieving the CaptureSDK version, BLE device discovery for deployments with multiple contactless readers, and data confirmation modes. Data confirmation can be set to device mode (scanner confirms immediately with beep/LED) or application mode (app confirms after processing, enabling validation logic before acknowledgment).

One piece of information that is useful, especially in case of an anomaly, is to retrieve the version of CaptureSDK and the version of the Scanner connected to the host.

Also in the case of contactless reader/writer, that uses Bluetooth Low Energy (BLE) to connect to the host and in the event the deployment has multiple contactless reader/writer, it is possible to discover them and let the user picking the appropriate one.

Another useful feature is the scenario where the host computer is out of range from where the scan happens, and to confirm the barcode was actually received by the application.

You can find more features integrated into our [sample apps on our Github](https://github.com/SocketMobile).

## Display the CaptureSDK Version

This feature is useful when an issue arises in a deployment in order to help identify the version of CaptureSDK actually in use. Here is a sample of code for retrieving the CaptureSDK version:

## Java

```java
// Retrieve the version of CaptureSDK currently in use.
void getCaptureVersion(CaptureClient capture) {
    capture.getCaptureVersion(new PropertyCallback() {
        @Override
        public void onComplete(CaptureError error, Property property) {
            if (error != null) {
                Log.e(TAG, "Get CaptureSDK version failed: " + error.getMessage());
                return;
            }
            Version version = property.getVersion();
            Log.d(TAG, "CaptureSDK version: "
                    + version.major + "."
                    + version.middle + "."
                    + version.minor);
        }
    });
}
```

## Kotlin

```kotlin
// Retrieve the version of CaptureSDK currently in use.
fun getCaptureVersion(capture: CaptureClient) {
    capture.getCaptureVersion(PropertyCallback { error, property ->
        if (error != null) {
            Log.e(TAG, "Get CaptureSDK version failed: ${error.message}")
            return@PropertyCallback
        }
        val version = property.version
        Log.d(
            TAG,
            "CaptureSDK version: ${version.major}.${version.middle}.${version.minor}"
        )
    })
}
```

## Swift

```swift
CaptureHelper.sharedInstance.getVersionWithCompletionHandler { result, version in
    if result == .E_NOERROR {
        let major = String(format:"%d",(version?.major)!)
        let middle = String(format:"%d",(version?.middle)!)
        let minor = String(format:"%d",(version?.minor)!)
        print("CaptureSDK version: \(major).\(middle).\(minor)")
    }
}
```

## C#

```csharp
// capture is of type "CaptureHelper" and is initialized
CaptureHelper.VersionResult versionResult = await capture.GetCaptureVersionAsync();

if (versionResult.IsSuccessful())
{
    Console.WriteLine(
        "CaptureSDK version: " +
        versionResult.Major + "." +
        versionResult.Middle + "." +
        versionResult.Minor + "."
    );
}
```

## Dart

```dart
Future<void> getCaptureVersion() async {
  try {
    dynamic res = await captureHelper.getVersion();
    print('CaptureSDK version: $res');
  } catch (err) {
    print('error while getting the CaptureSDK version: $err');
  }
}
```

## JavaScript

```javascript
const version = await capture.getVersion();
console.log(`CaptureSDK version: ${version.major}.${version.middle}.${version.minor}`);
```

## Discovery and Connect to Bluetooth Low Energy scanners and readers

Please refer to this page: [Connect Bluetooth Low Energy Devices](/docs/connect-bluetooth-le-devices)

## Data Confirmation Modes

There are 2 different modes for the scanner to acknowledge decoded data:

1. **Data Confirmation from the device**

  This is the default mode of data acknowledgement. However, it is also the fastest way to scan because the data acknowledgment is done locally from firmware within the scanner.

2. **Data Confirmation from the Application**

  The application verifies the decoded data, and sends an acknowledgement.

  For some deployments where the user might be unable to see the screen of the host when scanning a barcode, there is a way to have the application confirm receipt of the barcode.
  
  In this case, the scanner’s trigger button will be disabled until the application confirms the decoded data or until the trigger lock out timer elapses.
  
  First, CaptureSDK needs to be configured with Data Confirmation Mode set to **App**.
  
  This mode is persistent in CaptureSDK. If a scanner is already connected at this time, it will be configured to App mode as well.
  
  Otherwise, when a scanner connects it will be automatically configured to App mode during the connection process.

The following example shows how to set the confirmation to the application:

## Java

```java
// Configure the CaptureSDK so the application is responsible for
// acknowledging decoded data (App mode).
void setConfirmationModeApp(CaptureClient capture) {
    Property property = Property.create(Property.DATA_CONFIRMATION_MODE, Confirmation.MODE_CLIENT);
    capture.setProperty(property, new PropertyCallback() {
        @Override
        public void onComplete(CaptureError error, Property result) {
            if (error != null) {
                Log.e(TAG, "Unable to set Data Confirmation Mode: " + error.getCode());
            }
        }
    });
}
```

## Kotlin

```kotlin
// Configure the CaptureSDK so the application is responsible for
// acknowledging decoded data (App mode).
fun setConfirmationModeApp(capture: CaptureClient) {
    val property = Property.create(Property.DATA_CONFIRMATION_MODE, Confirmation.MODE_CLIENT)
    capture.setProperty(property, PropertyCallback { error, _ ->
        if (error != null) {
            Log.e(TAG, "Unable to set Data Confirmation Mode: ${error.code}")
        }
    })
}
```

## Swift

```swift
CaptureHelper.sharedInstance.setConfirmationMode(.modeApp) { result in
    print("Setting the application mode confirmation returned \(result.rawValue)")
}
```

## C#

```csharp
// Configure the CaptureSDK so the application is responsible for
// acknowledging decoded data (App mode).
byte mode = ICaptureProperty.Values.DataConfirmationMode.kModeApp;

CaptureHelper.AsyncResult result = await capture.SetDataConfirmationModeAsync(mode);

if (!result.IsSuccessful())
{
    Console.WriteLine("Unable to change the Data Confirmation Mode: "
        + result.Result);
}
```

## Dart

```dart
// Configure the CaptureSDK so the application is responsible for
// acknowledging decoded data (App mode).
final CaptureHelper _helper = CaptureHelper();

Future<void> setConfirmationModeApp() async {
  CaptureProperty property = CaptureProperty(
    CapturePropertyIds.dataConfirmationMode,
    CapturePropertyTypes.byte,
    DataConfirmationMode.modeApp
  );
  try {
    await _helper.setProperty(property);
  } catch (err) {
    print('error while setting the Data Confirmation Mode: $err');
  }
}
```

## JavaScript

```javascript
import { DataConfirmationMode } from 'react-native-capture';

// Configure the CaptureSDK so the application is responsible for
// acknowledging decoded data (App mode).
try {
  await capture.setConfirmationMode(DataConfirmationMode.App);
} catch (err) {
  console.error('SetConfirmationMode error:', err?.code ?? err?.error?.code);
}
```

If the Data Confirmation Mode is configured to "App", then the application is responsible for acknowledging the decoded data to the scanner. Here is example code showing how an application can acknowledge the decoded data:

## Java

```java
// When Data Confirmation Mode is set to App, the application must
// acknowledge each scan. Use Confirmation.create() to combine beep,
// rumble and LED feedback into a single value sent to the scanner.
@Subscribe(threadMode = ThreadMode.MAIN)
public void onScan(DataEvent event) {
    DecodedData data = event.getData();
    if (data.result != DecodedData.RESULT_SUCCESS) {
        return;
    }

    // Validate the decoded data: here we accept only non-empty QR codes
    String decodedData = data.getString();
    boolean isValidQrCode = !decodedData.isEmpty()
            && data.getDataSource().id == DataSource.ID_QR_CODE;

    int action = isValidQrCode
            ? Confirmation.create(Confirmation.ACTION_BEEP_GOOD,
                                  Confirmation.ACTION_RUMBLE_GOOD,
                                  Confirmation.ACTION_FLASH_GOOD)
            : Confirmation.create(Confirmation.ACTION_BEEP_BAD,
                                  Confirmation.ACTION_RUMBLE_BAD,
                                  Confirmation.ACTION_FLASH_BAD);

    event.getDevice().setDataConfirmationAction(action, new PropertyCallback() {
        @Override
        public void onComplete(CaptureError error, Property property) {
            if (error != null) {
                Log.e(TAG, "Failed to send data confirmation: " + error.getCode());
            }
        }
    });
}
```

## Kotlin

```kotlin
// When Data Confirmation Mode is set to App, the application must
// acknowledge each scan. Use Confirmation.create() to combine beep,
// rumble and LED feedback into a single value sent to the scanner.
@Subscribe(threadMode = ThreadMode.MAIN)
fun onScan(event: DataEvent) {
    val data = event.data
    if (data.result != DecodedData.RESULT_SUCCESS) {
        return
    }

    // Validate the decoded data: here we accept only non-empty QR codes
    val decodedData = data.string
    val isValidQrCode = decodedData.isNotEmpty() &&
        data.dataSource.id == DataSource.ID_QR_CODE

    val action = if (isValidQrCode) {
        Confirmation.create(
            Confirmation.ACTION_BEEP_GOOD,
            Confirmation.ACTION_RUMBLE_GOOD,
            Confirmation.ACTION_FLASH_GOOD
        )
    } else {
        Confirmation.create(
            Confirmation.ACTION_BEEP_BAD,
            Confirmation.ACTION_RUMBLE_BAD,
            Confirmation.ACTION_FLASH_BAD
        )
    }

    event.device.setDataConfirmationAction(action, PropertyCallback { error, _ ->
        if (error != null) {
            Log.e(TAG, "Failed to send data confirmation: ${error.code}")
        }
    })
}
```

## Swift

```swift
func didReceiveDecodedData(_ decodedData: SKTCaptureDecodedData?, fromDevice device: CaptureHelperDevice, withResult result: SKTResult) {
    if result == .E_NOERROR {
        if let decodedDataString = decodedData!.stringFromDecodedData() {
            print("Decoded Data \(decodedDataString) - Type: \(decodedData?.dataSourceName ?? "Unknown")")

            // Here we check if the decoded data is QRcode and has a length. Depending on this result, the device will behave differently
            let isValidQrCode = decodedDataString.count > 0 && decodedData?.dataSourceID == .symbologyQRCode
            device.setDataConfirmationWithLed(isValidQrCode ? .green : .red, withBeep: isValidQrCode ? .good : .bad, withRumble: isValidQrCode ? .good : .bad) { result in
                print("Set data confirmation result: \(result.rawValue)")
            }
        }
    }
}
```

## C#

```csharp
// capture.DecodedData += CaptureDecodedData;

// When Data Confirmation Mode is set to App, the application must
// acknowledge each scan. Send the combination of Beep, LED and Rumble
// to give the user clear feedback on the scanner.
async void CaptureDecodedData(object sender, CaptureHelper.DecodedDataArgs e)
{
    string decodedData = e.DecodedData.DataToUTF8String;

    // Validate the decoded data: here we accept only non-empty QR codes
    bool isValidQrCode = !string.IsNullOrEmpty(decodedData)
        && e.DecodedData.DataSourceID
            == ICaptureDataSource.Id.kSymbologyQRCode;

    int beep = isValidQrCode
        ? ICaptureProperty.Values.DataConfirmationBeep.kGood
        : ICaptureProperty.Values.DataConfirmationBeep.kBad;
    int led = isValidQrCode
        ? ICaptureProperty.Values.DataConfirmationLed.kGreen
        : ICaptureProperty.Values.DataConfirmationLed.kRed;
    int rumble = isValidQrCode
        ? ICaptureProperty.Values.DataConfirmationRumble.kGood
        : ICaptureProperty.Values.DataConfirmationRumble.kBad;

    await e.CaptureDevice.SetDataConfirmationAsync(beep, led, rumble);
}
```

## Dart

```dart
// When Data Confirmation Mode is set to App, the application must
// acknowledge each scan. Combine LED, Beep and Rumble into a single
// confirmation value sent back to the scanner.
void onDecodedData(CaptureEvent event, CaptureHelperDevice device) async {
  DecodedData? decodedData = event.value as DecodedData?;
  if (decodedData == null) return;

  // Validate the decoded data: here we accept only non-empty QR codes
  final bool isValidQrCode = data.data.isNotEmpty
      && data.id == CaptureDataSourceID.symbologyQRCode;

  final int confirmation = isValidQrCode
      ? (DataConfirmationBeep.good
            | DataConfirmationLed.green
            | DataConfirmationRumble.good)
      : (DataConfirmationBeep.bad
            | DataConfirmationLed.red
            | DataConfirmationRumble.bad);

  try {
    await device.setDataConfirmation(confirmation);
  } catch (err) {
    print('error while sending the data confirmation: $err');
  }
}
```

## JavaScript

```javascript
import {
  CaptureHelper,
  CaptureDataSourceID,
  DataConfirmationLed,
  DataConfirmationBeep,
  DataConfirmationRumble,
} from 'react-native-capture';

// When Data Confirmation Mode is set to App, the application must
// acknowledge each scan by calling setDataConfirmation on the device
// with the desired LED, Beep and Rumble feedback.
const capture = new CaptureHelper({
  onDecodedData: async (data, device) => {
    // Validate the decoded data: here we accept only non-empty QR codes
    const isValidQrCode =
      data.data.length > 0
      && data.dataSourceId === CaptureDataSourceID.SymbologyQRCode;

    try {
      await device.setDataConfirmation(
        isValidQrCode ? DataConfirmationLed.Green : DataConfirmationLed.Red,
        isValidQrCode ? DataConfirmationBeep.Good : DataConfirmationBeep.Bad,
        isValidQrCode ? DataConfirmationRumble.Good : DataConfirmationRumble.Bad
      );
    } catch (err: any) {
      console.error('SetDataConfirmation error:', err?.code ?? err?.error?.code);
    }
  },
});
```

> **Note**
>
> The Data Confirmation Mode set to <b>App</b> is the slowest way of scanning barcodes. Another way to handle cases where the barcode might be incorrect, is to have the application sending a Bad Beep, Bad Rumble and Red LED to the scanner and disabling the trigger button until the user acknowledges a message on the host screen explaining the issue. Please refer to <a href="/docs/using-capturesdk/trigger-scanner#disable-the-trigger-scanner-button">Disabling the Scanner Trigger Button</a>.