Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save akshayaurora/8d7eec16acb15db31098b28ddea1ee65 to your computer and use it in GitHub Desktop.
Save akshayaurora/8d7eec16acb15db31098b28ddea1ee65 to your computer and use it in GitHub Desktop.
ScanResultCallback.java
```
package com.example.ble;
public interface ScanResultCallback {
void onKeywordFound(String data);
}
```
BleScannerBridge.java
```
package com.example.ble;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.*;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import java.nio.charset.StandardCharsets;
public class BleScannerBridge {
private final Context context;
private final BluetoothLeScanner bleScanner;
private ScanCallback scanCallback;
private Handler handler = new Handler();
private boolean scanning = false;
private ScanResultCallback callback;
private String keyword = "waverian"; // default fallback
private static final String TAG = "BleScannerBridge";
private static final long SCAN_PERIOD = 50000;
public BleScannerBridge(Context ctx) {
this.context = ctx.getApplicationContext();
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
this.bleScanner = bluetoothAdapter.getBluetoothLeScanner();
}
public void setCallback(ScanResultCallback cb) {
this.callback = cb;
}
public void startScan(String keyword) {
if (scanning || bleScanner == null) return;
this.keyword = keyword;
scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
ScanRecord scanRecord = result.getScanRecord();
if (scanRecord != null) {
String deviceName = scanRecord.getDeviceName(); // May be null
String address = result.getDevice().getAddress();
int rssi = result.getRssi();
byte[] manuData = scanRecord.getManufacturerSpecificData(0xFFFF);
String manuDataStr = (manuData != null)
? new String(manuData, StandardCharsets.UTF_8)
: "";
String payload = "Name: " + (deviceName != null ? deviceName : "null") +
", Address: " + address +
", RSSI: " + rssi +
", ManufacturerData[0xFFFF]: " + manuDataStr;
Log.d(TAG, "Scan payload: " + payload);
if (manuDataStr.contains(BleScannerBridge.this.keyword)) {
Log.d(TAG, "Keyword match found in manufacturer data: " + manuDataStr);
if (callback != null) {
callback.onKeywordFound(payload);
}
}
}
}
};
bleScanner.startScan(scanCallback);
scanning = true;
handler.postDelayed(() -> {
bleScanner.stopScan(scanCallback);
scanning = false;
}, SCAN_PERIOD);
}
public void stopScan() {
if (scanning && scanCallback != null) {
bleScanner.stopScan(scanCallback);
scanning = false;
scanCallback = null;
}
}
}
```
Pit the java files above to a directory.
In buildozer.spec use add_src
```
add_src = path/to/java/files_folder/
```
Python
```
from kivy.app import App
from jnius import autoclass, PythonJavaClass, java_method
from android import mActivity
app = App.get_running_app()
# Load Java classes
BleScannerBridge = autoclass('com.example.ble.BleScannerBridge')
Runnable = autoclass('java.lang.Runnable')
# Define the callback interface
class MyScanCallback(PythonJavaClass):
__javainterfaces__ = ['com/example/ble/ScanResultCallback']
__javacontext__ = 'app'
@java_method('(Ljava/lang/String;)V')
def onKeywordFound(self, data):
Logger.debug(f"[BLE] Keyword match found: {data}")
data = string_to_dict(data)
Clock.schedule_once(
lambda dt: app.discovered_receivers.update(
{data['Name']: [data['Address'], None]}), 1)
class StartBleScanRunnable(PythonJavaClass):
__javainterfaces__ = ['java/lang/Runnable']
__javacontext__ = 'app'
def __init__(self, keyword):
super().__init__()
self.keyword = keyword
app._ble_scan_callback = MyScanCallback()
@java_method('()V') # ← this is mandatory
def run(self):
print("[BLE] Running scan on UI thread with keyword:", self.keyword)
if not hasattr(app, '_ble_bridge'):
scanner = BleScannerBridge(mActivity)
scanner.setCallback(app._ble_scan_callback)
app._ble_bridge = scanner
scanner.startScan(self.keyword)
class StopBleScanRunnable(PythonJavaClass):
__javainterfaces__ = ['java/lang/Runnable']
__javacontext__ = 'app'
@java_method('()V')
def run(self):
print("[BLE] Running stop scan on UI thread")
app = App.get_running_app()
if hasattr(app, '_ble_bridge'):
app._ble_bridge.stopScan()
print("[BLE] Scan stopped")
del app._ble_bridge
# Start scan with a custom keyword (must be Java String)
JavaString = autoclass('java.lang.String')
# REPLACE <MyDeviceManufacturerData> with what you are searching for.
keyword = JavaString("MyDeviceManufacturerData")
# ENable Bluetooth
# Give permission to scan
from android.permissions import request_permissions, Permission
request_permissions([Permission.ACCESS_FINE_LOCATION, Permission.BLUETOOTH_SCAN])
# Start scan
mActivity.runOnUiThread(StartBleScanRunnable(keyword))
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment