I want to run a BTLE scan within a ViewModel. I'm not sure if this is the best approach but it is a good learning experiment for me. I did manage to successfully list my BT devices when running the scan in a more simple setup.
Permissions BLUETOOTH
BLUETOOTH_ADMIN
and ACCESS_COARSE_LOCATION
are set in the Manifest
.
I have a checkPermissions
method in my main settings activity that checks for permissions, and requests permissions if needed. I also have an override for onRequestPermissionsResult
. The location permissions shows in my app's permissions. Those methods are more or less a copy/paste of open-source projects (see this).
So well, my scan doesn't work. The log in my callback never shows in my logs. There is no error, the only suspicious thing I'm seeing in the logs is this: D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=12 mScannerId=0
.
My ViewModel looks like this:
interface ScanningStatus {
void setCurrentlyScanning(boolean updatedStatus);
boolean getCurrentlyScanning();
}
public class SettingsScanningViewModel extends AndroidViewModel {
private MutableLiveData<ArrayList<BluetoothDevice>> mDeviceList;
private ArrayList<BluetoothDevice> mInternalDeviceList;
private MutableLiveData<Boolean> mIsScanning;
private static final String TAG = "viewmodel";
private BluetoothManager mBtManager;
private BluetoothAdapter mBtAdapter;
private BluetoothLeScanner mBleScanner;
private static final long SCAN_PERIOD = 5000;
public SettingsScanningViewModel(Application mApplication) {
super(mApplication);
mDeviceList = new MutableLiveData<>();
mInternalDeviceList = new ArrayList<>();
mIsScanning = new MutableLiveData<>();
mBtManager = (BluetoothManager) mApplication.getSystemService(Context.BLUETOOTH_SERVICE);
mBtAdapter = mBtManager.getAdapter();
mBleScanner = mBtAdapter.getBluetoothLeScanner();
}
MutableLiveData<ArrayList<BluetoothDevice>> getBtDevices() {
return mDeviceList;
}
MutableLiveData<Boolean> getScanningStatus() {
return mIsScanning;
}
private void flushList() {
mInternalDeviceList.clear();
mDeviceList.setValue(mInternalDeviceList);
}
private void injectIntoList(BluetoothDevice btDevice) {
mInternalDeviceList.add(btDevice);
mDeviceList.setValue(mInternalDeviceList);
}
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d(TAG, "in callback");
BluetoothDevice mDevice = result.getDevice();
if (!mInternalDeviceList.contains(mDevice) && mDevice.getName() != null) {
injectIntoList(mDevice);
}
}
@Override
public void onScanFailed(int errorCode) {
Log.d(TAG, "onScanFailed: " + errorCode);
}
};
private ScanningStatus scanningStatus = new ScanningStatus() {
@Override
public void setCurrentlyScanning(boolean status) {
mIsScanning.setValue(status);
}
public boolean getCurrentlyScanning() {
return mIsScanning.getValue();
}
};
void doBtScan() {
Log.d(TAG, "flush list first");
flushList();
Log.d(TAG, "starting scan with scanCallback " + scanCallback);
scanningStatus.setCurrentlyScanning(true);
mBleScanner.startScan(scanCallback);
Log.d(TAG, "scan should be running for " + SCAN_PERIOD);
Handler handler = new Handler();
handler.postDelayed(() -> {
mBleScanner.stopScan(scanCallback);
scanningStatus.setCurrentlyScanning(false);
}, SCAN_PERIOD);
}
}
I am really not sure what is wrong. Is it because I'm creating new BluetoothManager
, BluetoothAdapter
and BluetoothLeScanner
in my ViewModel compared with the main activity where I initiate and request permissions? If so, how could I re-use these same objects?
Thank you.