Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/opticonosedevelopment/opticonnect_sdk_android
OptiConnect SDK provides BLE connectivity, barcode streaming, and command control for Opticon OPN-2500 and OPN-6000 scanners on Android.
https://github.com/opticonosedevelopment/opticonnect_sdk_android
Last synced: about 2 months ago
JSON representation
OptiConnect SDK provides BLE connectivity, barcode streaming, and command control for Opticon OPN-2500 and OPN-6000 scanners on Android.
- Host: GitHub
- URL: https://github.com/opticonosedevelopment/opticonnect_sdk_android
- Owner: OpticonOSEDevelopment
- Created: 2024-11-11T09:39:54.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2024-11-11T10:33:39.000Z (2 months ago)
- Last Synced: 2024-11-11T10:39:45.797Z (2 months ago)
- Language: Kotlin
- Size: 0 Bytes
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# OptiConnect SDK
OptiConnect SDK enables seamless integration with [Opticon](https://opticon.com/)'s BLE [OPN-2500](https://opticon.com/product/opn-2500/) and [OPN-6000](https://opticon.com/product/opn-6000/) barcode scanners. This SDK allows you to manage Bluetooth Low Energy (BLE) connections, handle scanner data streams, and programmatically control scanner settings via commands.
## Documentation
For comprehensive information on the SDK setup, usage, and API reference, please visit the [OptiConnect SDK Documentation](https://opticonosedevelopment.github.io/opticonnect_sdk_android/).
## Features
- Bluetooth discovery and connection management for OPN-2500 and OPN-6000 BLE scanners.
- Real-time data streaming, including barcode data reception and BLE device state monitoring.
- Programmatic control of scanner settings (e.g., scan modes, illumination, connection pooling, etc.).
- Exclusive connection management: Ensure stable device pairing in multi-device environments by assigning unique connection pool IDs, preventing previously paired devices from hijacking active connections.
- Command management and customization for BLE services and scanner configurations.## Getting Started
### 1. Prerequisites
At least one of the following Opticon BLE barcode scanners is required:
| OPN-2500 | OPN-6000 |
|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| [![OPN-2500](opticonnectsdk/build/dokka/html/images/OPN-2500.png)](https://opticon.com/product/opn-2500/) | [![OPN-6000](opticonnectsdk/build/dokka/html/images/OPN-6000.png)](https://opticon.com/product/opn-6000/) |
| [OPN-2500](https://opticon.com/product/opn-2500/) | [OPN-6000](https://opticon.com/product/opn-6000/)### 2. System Requirements
- **Android Minimum SDK**: 26
- **Java Version**: 11 or higher
- **Kotlin Version**: 1.8.20 or higher
- **Gradle Version**: 7.4 or higher
- **AGP (Android Gradle Plugin)**: 7.2.2 or higher### 3. Building the opticonnect .aar library (optional)
To build the `.aar` file for the OptiConnect SDK with shadowed dependencies, follow these steps:
1. Run the shadowJar task: `./gradlew shadowJar`
2. Package the final `.aar`: `./gradlew bundleShadowedReleaseAar`The generated `.aar` file will be located in `build/outputs/aar/`.
### 4. Adding the `.aar` library to your project
The .aar file (`opticonnectsdk.aar`) is provided at the [following location](https://github.com/OpticonOSEDevelopment/opticonnect_sdk_android/tree/main/libs). The library is already included in both the Kotlin and Java examples in their libs directories. To integrate the library into your own project, perform the following steps:
1. Copy `opticonnectsdk.aar` to your project’s libs directory if it’s not already there.
2. Add the .aar file to your dependencies in build.gradle(.kts) as explained in the following section.### 5. Updating your `build.gradle(.kts)`
Add the `.aar` file and required dependencies to your `build.gradle(.kts)` file under `dependencies`. Below is the recommended setup:
#### Shared Dependencies for Java and Kotlin Projects
```kotlin
dependencies {
// Include the .aar file
implementation(files("libs/opticonnectsdk.aar"))// Core Android dependencies
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)// RxAndroidBLE and RxKotlin for BLE and reactive programming
implementation(libs.rxandroidble)
implementation(libs.rxkotlin)
}
```#### Additional Kotlin Dependencies
If using Kotlin, add the Coroutines libraries for asynchronous handling:
```kotlin
dependencies {
// Coroutines dependencies
implementation(libs.coroutines)
implementation(libs.coroutines.android)
implementation(libs.coroutines.rx3)
}
```- Note: Java projects do not require Coroutines as they use callbacks instead of coroutines for asynchronous handling.
#### Important: Kotlin Plugin Requirement for Java Projects
The Kotlin plugin is necessary even for Java-based projects due to the Kotlin-based .aar library. This ensures compatibility with any Kotlin classes or extensions within the SDK.
### 6. Android Manifest Bluetooth Permissions
To enable Bluetooth discovery and connection on Android, add the following permissions to your AndroidManifest.xml file located at android/app/src/main/AndroidManifest.xml below the manifest entry:
```xml
```
## Examples
These examples demonstrate how to integrate the OptiConnect SDK to discover devices, manage Bluetooth connections, retrieve barcode data, and monitor battery status for the OPN-2500 and OPN-6000 in both Kotlin and Java.
#### Main Components in the Example
- **DeviceState**: A data class managing the state of the connected BLE scanner, including device ID, connection state, barcode data, battery percentage, and charging status.
- **MainActivity**: The main activity for setting up the SDK, handling Bluetooth permissions, discovering devices, managing connection and disconnection events, and starting listeners for barcode data and battery updates.
#### User Interfaces
- **Kotlin (Compose)**: `ConnectionStatusScreen` displays the connection status, barcode data, battery level, and charging state in a simple Compose layout.
- **Java (XML Layout)**: `activity_main.xml` provides a similar UI using TextView components to display the connection status, barcode data, battery level, and charging state, along with a "Disconnect" button.### Kotlin Example
This example demonstrates how to integrate the OptiConnect SDK using Kotlin. It is available in [examples/kotlin](https://github.com/OpticonOSEDevelopment/opticonnect_sdk_android/tree/main/examples/kotlin).
*MainActivity.kt*
```Kotlin
package com.opticon.opticonnect_sdk_exampleimport android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.opticon.opticonnect.sdk.api.OptiConnect
import com.opticon.opticonnect.sdk.api.enums.BleDeviceConnectionState
import com.opticon.opticonnect_sdk_example.ui.theme.Opticonnect_SDK_ExampleTheme
import kotlinx.coroutines.launch// Holds device-specific connection and data state
data class DeviceState(
val connectedDeviceId: String = "",
val connectionState: BleDeviceConnectionState = BleDeviceConnectionState.DISCONNECTED,
val barcodeData: String? = null,
val batteryPercentage: Int? = null,
val isCharging: Boolean? = null
)class MainActivity : ComponentActivity() {
private var deviceState by mutableStateOf(DeviceState())override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MainScreen() }
checkBluetoothPermissions()
}// Sets up the main UI screen
@Composable
private fun MainScreen() {
Opticonnect_SDK_ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ConnectionStatusScreen(deviceState) { disconnectDevice(it) }
}
}
}// Checks for required Bluetooth permissions and requests them if not granted
private fun checkBluetoothPermissions() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
listOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
} else listOf(Manifest.permission.ACCESS_FINE_LOCATION)val toRequest = permissions.filter {
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
}if (toRequest.isNotEmpty()) {
requestPermissionsLauncher.launch(toRequest.toTypedArray())
} else initializeOptiConnectAndStartDiscovery()
}// Launcher for permission requests
private val requestPermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.all { it.value }) initializeOptiConnectAndStartDiscovery()
else Toast.makeText(this, "Bluetooth permissions are required.", Toast.LENGTH_LONG).show()
}// Initializes OptiConnect SDK and starts device discovery
private fun initializeOptiConnectAndStartDiscovery() {
OptiConnect.initialize(this).apply {
OptiConnect.bluetoothManager.startDiscovery()
lifecycleScope.launch {
// Collects discovered devices and connects if disconnected
OptiConnect.bluetoothManager.listenToDiscoveredDevices.collect { device ->
if (deviceState.connectionState == BleDeviceConnectionState.DISCONNECTED) {
deviceState = deviceState.copy(
connectedDeviceId = device.deviceId,
connectionState = BleDeviceConnectionState.CONNECTING
)
connectToDevice(device.deviceId)
}
}
}
}
}// Connects to the discovered device
private fun connectToDevice(deviceId: String) {
lifecycleScope.launch {
OptiConnect.bluetoothManager.apply {
try {
// Initiates the connection and listens to connection state
connect(deviceId)
startListeningToDeviceData(deviceId)listenToConnectionState(deviceId).collect { state ->
deviceState = deviceState.copy(
connectionState = state,
connectedDeviceId = if (state == BleDeviceConnectionState.CONNECTED) deviceId else ""
)Log.d("OptiConnect", "Device $deviceId state changed to: $state")
if (state == BleDeviceConnectionState.DISCONNECTED) {
deviceState = DeviceState() // Reset state on disconnect
}
}
} catch (e: Exception) {
// Handle connection failure and reset device state
Toast.makeText(this@MainActivity, "Failed to connect: ${e.message}", Toast.LENGTH_SHORT).show()
deviceState = DeviceState()
}
}
}
}// Listens to data from the connected device (barcode, battery, charging status)
private fun startListeningToDeviceData(deviceId: String) {
lifecycleScope.launch {
OptiConnect.bluetoothManager.listenToBarcodeData(deviceId).collect { barcode ->
deviceState = deviceState.copy(barcodeData = barcode.data)
}
}lifecycleScope.launch {
OptiConnect.bluetoothManager.listenToBatteryPercentage(deviceId).collect { battery ->
deviceState = deviceState.copy(batteryPercentage = battery)
}
}lifecycleScope.launch {
OptiConnect.bluetoothManager.listenToBatteryStatus(deviceId).collect { status ->
deviceState = deviceState.copy(isCharging = status.isCharging)
}
}
}// Disconnects from the device and resets the state
private fun disconnectDevice(deviceId: String) {
lifecycleScope.launch {
OptiConnect.bluetoothManager.disconnect(deviceId)
deviceState = DeviceState()
}
}override fun onDestroy() {
super.onDestroy()
OptiConnect.close()
}
}// UI for showing the connection status and device data
@Composable
fun ConnectionStatusScreen(
connectionState: DeviceState,
onDisconnect: (String) -> Unit
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize()
) {
when (connectionState.connectionState) {
BleDeviceConnectionState.CONNECTING -> {
Text(
text = "Connecting to device...",
style = MaterialTheme.typography.headlineMedium
)
CircularProgressIndicator(
modifier = Modifier.padding(top = 16.dp)
)
}
BleDeviceConnectionState.CONNECTED -> {
Text(
text = "Connected to device: ${connectionState.connectedDeviceId}",
style = MaterialTheme.typography.headlineMedium
)
Text(
text = "Barcode Data: ${connectionState.barcodeData ?: "No barcode scanned yet."}"
)
Text(
text = "Battery: ${connectionState.batteryPercentage ?: "N/A"}%"
)
Text(
text = "Charging: ${if (connectionState.isCharging == true) "Yes" else "No"}"
)
Button(
onClick = { onDisconnect(connectionState.connectedDeviceId) },
modifier = Modifier.padding(top = 16.dp)
) {
Text("Disconnect")
}
}
BleDeviceConnectionState.DISCONNECTED -> {
Text(
text = "Searching for devices...",
style = MaterialTheme.typography.headlineMedium
)
}
BleDeviceConnectionState.DISCONNECTING -> {
Text(
text = "Disconnecting from device...",
style = MaterialTheme.typography.headlineMedium
)
}
}
}
}
}
```### Java Example
This example demonstrates how to integrate the OptiConnect SDK using Java. It is available in [examples/java](https://github.com/OpticonOSEDevelopment/opticonnect_sdk_android/tree/main/examples/java).
*DeviceState.java*
```java
package com.example.opticonnect_sdk_example_java;import com.opticon.opticonnect.sdk.api.enums.BleDeviceConnectionState;
public class DeviceState {
// The ID of the currently connected BLE device, initially set to an empty string
private String connectedDeviceId = "";// The current connection state of the BLE device, initially set to DISCONNECTED
private BleDeviceConnectionState connectionState = BleDeviceConnectionState.DISCONNECTED;// Holds the most recent barcode data received from the connected device
private String barcodeData = null;// Stores the current battery percentage of the connected device, if available
private Integer batteryPercentage = null;// Indicates whether the connected device is currently charging, if available
private Boolean isCharging = null;// Getter and setter for the connected device ID
public String getConnectedDeviceId() { return connectedDeviceId; }
public void setConnectedDeviceId(String id) { this.connectedDeviceId = id; }// Getter and setter for the device connection state
public BleDeviceConnectionState getConnectionState() { return connectionState; }
public void setConnectionState(BleDeviceConnectionState state) { this.connectionState = state; }// Getter and setter for barcode data
public String getBarcodeData() { return barcodeData; }
public void setBarcodeData(String data) { this.barcodeData = data; }// Getter and setter for battery percentage
public Integer getBatteryPercentage() { return batteryPercentage; }
public void setBatteryPercentage(Integer percentage) { this.batteryPercentage = percentage; }// Getter and setter for the charging status
public Boolean getIsCharging() { return isCharging; }
public void setIsCharging(Boolean isCharging) { this.isCharging = isCharging; }
}
```*MainActivity.java*
```java
package com.example.opticonnect_sdk_example_java;import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import androidx.activity.ComponentActivity;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;import com.opticon.opticonnect.sdk.api.OptiConnect;
import com.opticon.opticonnect.sdk.api.entities.BarcodeData;
import com.opticon.opticonnect.sdk.api.entities.BatteryLevelStatus;
import com.opticon.opticonnect.sdk.api.entities.BleDiscoveredDevice;
import com.opticon.opticonnect.sdk.api.enums.BleDeviceConnectionState;
import com.opticon.opticonnect.sdk.api.interfaces.Callback;import kotlin.Unit;
public class MainActivity extends ComponentActivity {
// Device state to hold the connection, battery, and barcode data information
private DeviceState deviceState = new DeviceState();// UI components to display device status, battery, barcode, and charging info
private TextView connectionStatusText, barcodeDataText, batteryPercentageText, chargingStatusText;// Launcher to request Bluetooth permissions as needed
private final ActivityResultLauncher requestPermissionsLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), this::onPermissionsResult);@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);// Bind UI elements
connectionStatusText = findViewById(R.id.connectionStatusText);
barcodeDataText = findViewById(R.id.barcodeDataText);
batteryPercentageText = findViewById(R.id.batteryPercentageText);
chargingStatusText = findViewById(R.id.chargingStatusText);
Button disconnectButton = findViewById(R.id.disconnectButton);// Handle disconnect button click
disconnectButton.setOnClickListener(view -> disconnectDevice());// Check Bluetooth permissions before initializing SDK
checkBluetoothPermissions();
}/**
* Checks if Bluetooth permissions are granted and requests them if necessary.
* For Android 12+ (API level 31), BLUETOOTH_SCAN and BLUETOOTH_CONNECT are required.
* For older versions, ACCESS_FINE_LOCATION is sufficient.
*/
private void checkBluetoothPermissions() {
String[] permissions = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT}
: new String[]{Manifest.permission.ACCESS_FINE_LOCATION};boolean permissionsGranted = true;
for (String permission : permissions) {
permissionsGranted &= ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
}if (permissionsGranted) {
initializeOptiConnectAndStartDiscovery();
} else {
// Launch permission request if not all permissions are granted
requestPermissionsLauncher.launch(permissions);
}
}/**
* Callback for the result of the permission request.
* Initializes SDK and starts Bluetooth discovery if permissions are granted.
*/
private void onPermissionsResult(@NonNull java.util.Map permissions) {
if (permissions.containsValue(Boolean.FALSE)) {
Toast.makeText(this, "Bluetooth permissions are required.", Toast.LENGTH_LONG).show();
} else {
initializeOptiConnectAndStartDiscovery();
}
}/**
* Initializes the OptiConnect SDK and starts device discovery.
* Sets up a listener for discovered devices to initiate connection upon detection.
*/
private void initializeOptiConnectAndStartDiscovery() {
// Initialize SDK
OptiConnect.INSTANCE.initialize(this);// Start device discovery
OptiConnect.INSTANCE.getBluetoothManager().startDiscovery();// Listen for discovered devices
OptiConnect.INSTANCE.getBluetoothManager().listenToDiscoveredDevices(new Callback<>() {
@Override
public void onSuccess(BleDiscoveredDevice device) {
if (deviceState.getConnectionState() == BleDeviceConnectionState.DISCONNECTED) {
deviceState.setConnectedDeviceId(device.getDeviceId());
deviceState.setConnectionState(BleDeviceConnectionState.CONNECTING);
updateUI();
connectToDevice(device.getDeviceId());
}
}@Override
public void onError(@NonNull Throwable error) {
Log.e("OptiConnect", "Error discovering devices: " + error.getMessage());
}
});
}/**
* Initiates connection to a BLE device by device ID and sets up data listeners.
*/
private void connectToDevice(String deviceId) {
OptiConnect.INSTANCE.getBluetoothManager().connect(deviceId, new Callback<>() {
@Override
public void onSuccess(Unit result) {
deviceState.setConnectionState(BleDeviceConnectionState.CONNECTED);
updateUI();
startListeningToDeviceData(deviceId);
listenToConnectionState(deviceId);
}@Override
public void onError(@NonNull Throwable error) {
Toast.makeText(MainActivity.this, "Failed to connect: " + error.getMessage(), Toast.LENGTH_SHORT).show();
deviceState = new DeviceState();
}
});
}/**
* Starts listening to device data streams (barcode, battery, and charging status).
*/
private void startListeningToDeviceData(String deviceId) {
OptiConnect.INSTANCE.getBluetoothManager().listenToBarcodeData(deviceId, new Callback<>() {
@Override
public void onSuccess(BarcodeData barcode) {
deviceState.setBarcodeData(barcode.getData());
updateUI();
}@Override
public void onError(@NonNull Throwable error) {
Log.e("OptiConnect", "Error receiving barcode data: " + error.getMessage());
}
});OptiConnect.INSTANCE.getBluetoothManager().listenToBatteryPercentage(deviceId, new Callback<>() {
@Override
public void onSuccess(Integer batteryPercentage) {
deviceState.setBatteryPercentage(batteryPercentage);
updateUI();
}@Override
public void onError(@NonNull Throwable error) {
Log.e("OptiConnect", "Error receiving battery percentage: " + error.getMessage());
}
});OptiConnect.INSTANCE.getBluetoothManager().listenToBatteryStatus(deviceId, new Callback<>() {
@Override
public void onSuccess(BatteryLevelStatus status) {
deviceState.setIsCharging(status.isCharging());
updateUI();
}@Override
public void onError(@NonNull Throwable error) {
Log.e("OptiConnect", "Error receiving battery status: " + error.getMessage());
}
});
}/**
* Listens to the device's connection state and updates the UI accordingly.
*/
private void listenToConnectionState(String deviceId) {
OptiConnect.INSTANCE.getBluetoothManager().listenToConnectionState(deviceId, new Callback<>() {
@Override
public void onSuccess(BleDeviceConnectionState state) {
deviceState.setConnectionState(state);
updateUI();
}@Override
public void onError(@NonNull Throwable error) {
Log.e("OptiConnect", "Error receiving connection state: " + error.getMessage());
}
});
}/**
* Disconnects the device and resets the device state.
*/
private void disconnectDevice() {
// Reset DeviceState and update UI
deviceState = new DeviceState();
updateUI();
}/**
* Updates the UI based on the current device state, including connection status, battery, and barcode data.
*/
private void updateUI() {
connectionStatusText.setText("Status: " + deviceState.getConnectionState().name());// Set color based on connection state
switch (deviceState.getConnectionState()) {
case CONNECTING:
connectionStatusText.setTextColor(ContextCompat.getColor(this, R.color.connecting_color));
break;
case CONNECTED:
connectionStatusText.setTextColor(ContextCompat.getColor(this, R.color.connected_color));
break;
case DISCONNECTED:
connectionStatusText.setTextColor(ContextCompat.getColor(this, R.color.disconnected_color));
break;
default:
connectionStatusText.setTextColor(ContextCompat.getColor(this, R.color.text_primary));
}// Display barcode, battery percentage, and charging status
barcodeDataText.setText("Barcode Data: " + (deviceState.getBarcodeData() != null ? deviceState.getBarcodeData() : "None"));
batteryPercentageText.setText("Battery: " + (deviceState.getBatteryPercentage() != null ? deviceState.getBatteryPercentage() + "%" : "N/A"));
chargingStatusText.setText("Charging: " + (deviceState.getIsCharging() != null ? (deviceState.getIsCharging() ? "Yes" : "No") : "Unknown"));
}
}
```*res/values/colors.xml*
```xml
#FFFFFF
#000000
#666666
#6200EE
#FFA500
#00FF00
#FF0000```
*res/layout/activity_main.xml*
```xml
```