Reputation:
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.
Upvotes: 1
Views: 1101
Reputation: 473
These three steps worked for me.
GPS should be turned on (write some code to turn it on if not already).
both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions should be declared in manifest and asked at runtime.
and the most important is the below answer talking about ScanSettings
https://stackoverflow.com/a/53831870/10432212
Upvotes: 1
Reputation:
OKKKKKKKKKKKK another instance of trying to find out what's wrong for hours, posting, then 2mn later finding out by myself.
Looks like ACCESS_COARSE_LOCATION
is not enough and ACCESS_FINE_LOCATION
is needed. So I suppose I must be using a different SDK than previously!
Thank you.
Upvotes: 1