I have a problem with my code:
private ScanCallback mLeScanCallback = new ScanCallback(){
//Callback when a BLE advertisement has been found.
#Override
public void onScanResult(int callbackType, final android.bluetooth.le.ScanResult result) {
super.onScanResult(callbackType, result);
new Thread(){
#Override
public void run() {
final BluetoothDevice device = result.getDevice();
runOnUiThread(new Runnable() {
#Override
public void run() {
if (device != null){
mDevices.add(device);
}
}
});
}
}.start();
}
//Callback when batch results are delivered.
#Override
public void onBatchScanResults(List<android.bluetooth.le.ScanResult> results) {
super.onBatchScanResults(results);
}
//Callback when scan could not be started.
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
currently I am using this code to get the results of my scan. This was based on: https://github.com/RedBearLab/Android/blob/master/Examples/Chat/src/com/redbear/chat/Main.java#L138
The app where is was based on had a lower API level, and my app has a higher one. So I changed it to on startScan() method.
I am honestly stuck, because when I run the app i get no errors. I checked if the mDevices array is empty and it is. Meaning that the code doesn't add the devices to the array or that there aren't any devices to be found by my app specifically.
Any help would be greatly appreciated.
Code of activating scanning
private void scanDevice(){
new Thread() {
#Override
public void run(){
BTScanner.startScan(mLeScanCallback);
try {
Thread.sleep(SCAN_PERIOD);
} catch (InterruptedException e){
e.printStackTrace();
}
BTScanner.stopScan(mLeScanCallback);
}
}.start();
}
Found the problem. It has to do with the permissions for ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION to get the scan results.
Related
I am trying to register a app with the DJI android SDK, but the call to DJISDKManager.getInstance() just hangs.
I am following the tutorial here: https://developer.dji.com/mobile-sdk/documentation/application-development-workflow/workflow-integrate.html
After the app verifies it has all the required permissions it calls startSDKRegistration:
I have the call to DJISDKManager.getInstance() on a single line for testing. It hangs on the call, and doesn't throw any errors.
private void startSDKRegistration() {
if (isRegistrationInProgress.compareAndSet(false, true)) {
Thread registrationThread = new Thread() {
#Override
public void run() {
showToast("registering, pls wait...");
try {
DJISDKManager temp = DJISDKManager.getInstance();
} catch (InterruptedException e) {
e.printStackTrace();
}
DJISDKManager.getInstance().registerApp(MainActivity.this.getApplicationContext(), new DJISDKManager.SDKManagerCallback() {
#Override
public void onRegister(DJIError djiError) {
if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
showToast("Register Success");
DJISDKManager.getInstance().startConnectionToProduct();
} else {
showToast("Register sdk fails, please check the bundle id and network connection!");
}
Log.v(TAG, djiError.getDescription());
}
#Override
public void onProductDisconnect() {
Log.d(TAG, "onProductDisconnect");
showToast("Product Disconnected");
notifyStatusChange();
}
#Override
public void onProductConnect(BaseProduct baseProduct) {
Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct));
showToast("Product Connected");
notifyStatusChange();
}
#Override
public void onProductChanged(BaseProduct baseProduct) {
// there was nothing in the tutorial for this method
}
#Override
public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent,
BaseComponent newComponent) {
if (newComponent != null) {
newComponent.setComponentListener(new BaseComponent.ComponentListener() {
#Override
public void onConnectivityChange(boolean isConnected) {
Log.d(TAG, "onComponentConnectivityChanged: " + isConnected);
notifyStatusChange();
}
});
}
Log.d(TAG,
String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s",
componentKey,
oldComponent,
newComponent));
}
#Override
public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) {
}
#Override
public void onDatabaseDownloadProgress(long l, long l1) {
}
});
}
};
GlobalParams.getInstance().getThreadPool().submit (registrationThread);
}
private void notifyStatusChange() {
mHandler.removeCallbacks(updateRunnable);
mHandler.postDelayed(updateRunnable, 500);
}
private Runnable updateRunnable = new Runnable() {
#Override
public void run() {
Intent intent = new Intent(FLAG_CONNECTION_CHANGE);
sendBroadcast(intent);
}
};
private void showToast(final String toastMsg) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show();
}
});
}
The problem occurs after Android Gradle plugin version 3.6.0+
https://developer.android.google.cn/studio/releases/gradle-plugin#3-6-0-behavior
Native libraries packaged uncompressed by default
When you build your app, the plugin now sets extractNativeLibs to "false" by default. That is, your native libraries are page aligned and packaged uncompressed. While this results in a larger upload size, your users benefit from the following:
Smaller app install size because the platform can access the native libraries directly from the installed APK, without creating a copy of the libraries.
Smaller download size because Play Store compression is typically better when you include uncompressed native libraries in your APK or Android App Bundle.
If you want the Android Gradle plugin to instead package compressed native libraries, include the following in your app's manifest:
<application
android:extractNativeLibs="true">
</application>
Question: What is the setting for minSdkVersion and targetSdkVersion?
I ask because there is a known issue with the values. I don't remember exactly but setting the following should work.
minSdkVersion 22
targetSdkVersion 29
I have a project that scan QR Codes . It works fine using Samsung mobiles but I can't scan QR Codes using OPPO CPH1923 device . I made a debug and I found that , debugger didn't get into
receiveDetections override function .I don't know why this device dose that and I'm wasting my time .
I use Google mobile vision API
Note : I don't have a sim card in the mobile
Here is my code
barcodeDetector = new BarcodeDetector.Builder(MainActivity.this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
cameraSource = new CameraSource.Builder(MainActivity.this,barcodeDetector)
.setRequestedPreviewSize(640,480)
.setAutoFocusEnabled(true)
.build();
surface.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},REQUEST_CAMERA_PERMISSION);
}
else {
cameraSource.start(holder);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
scannerDilaog.dismiss();
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodeSparseArray = detections.getDetectedItems();
if(barcodeSparseArray!=null&&barcodeSparseArray.size()>0){
if(!flag) {
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
int times =0;
//flag = true;
cameraSource.stop();
Intent intent = new Intent(MainActivity.this, HomeActivity.class);
intent.putExtra("result", barcodeSparseArray.valueAt(0).displayValue);
Log.e("result"," "+barcodeSparseArray.valueAt(0).displayValue);
if(!flag) {
times++;
Log.d("times"," "+times);
startActivity(intent);
}
flag=true;
cameraSource.stop();
}
});
}
}
}
});
Finally I found the solution at this part of this Answer
Here
Based on the Google Samples(a comment in the source):
// Note: The first time that an app using the barcode or face API is installed on a
// device, GMS will download a native libraries to the device in order to do detection.
// Usually this completes before the app is run for the first time. But if that
// download has not yet completed, then the above call will not detect any barcodes
// and/or faces.
I have faced with the issue using startScan method of BluetoothLeScanner a BLE device was found, but when I turned off BLE device my phone still shows this device as turned on !!
I have tried to use:
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("ScanCallback", String.format("onScanResult(int callbackType[%d], ScanResult result)", callbackType));
final BluetoothDevice btDevice = result.getDevice();
if (btDevice == null){
Log.e("ScanCallback", "Could not get bluetooth device");
return;
}
final String macAddress = btDevice.getAddress();
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
// NOTE: I've never got here
final BluetoothDevice outOfRangeDevice = mBtDevices.get(macAddress);
...
} else {
...
}
}
...
};
Guy, I have not found solution how to detect that BLE device is lost in other resources like (Android SDK reference, forums, stackoverflow and etc) (:
Any help will be appreciated !!
During googling and exploring the Android Documentations I have figured out how to detect if device is out of range. I would like to share my solution how I did it:
...
public void scanBLEDevices(final boolean enable) {
if(mLeScanner == null) {
Log.d(TAG, "Could not get LEScanner object");
throw new InternalError("Could not get LEScanner object");
}
if (enable) {
startLeScan();
} else {
stopLeScan(false);
}
}
private void startLeScan() {
Log.i(TAG, "startLeScan(BluetoothLeScanner mLeScanner)");
mScanning = true;
mInRangeBtDevices.clear();
if (mStartScanCallback != null) {
mHandler.removeCallbacks(mStartScanCallback);
}
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLeScanner.startScan(mScanFilters, mScanSettings, mScanCallback);
}
mStopScanCallback = new Runnable() {
#Override
public void run() {
stopLeScan(true);
}
};
mHandler.postDelayed(mStopScanCallback, SCAN_PERIOD);
}
private void stopLeScan(final boolean isContinueAfterPause) {
Log.i(TAG, "stopLeScan(BluetoothLeScanner mLeScanner)");
mScanning = false;
if (mStopScanCallback != null) {
mHandler.removeCallbacks(mStopScanCallback);
}
removeOutOfRangeDevices();
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLeScanner.stopScan(mScanCallback);
}
if (isContinueAfterPause) {
mStartScanCallback = new Runnable() {
#Override
public void run() {
startLeScan();
}
};
mHandler.postDelayed(mStartScanCallback, SCAN_PAUSE);
}
}
private void removeOutOfRangeDevices() {
final Set<String> outOfRangeDevices = new HashSet<>();
for (String btAddress : mBtDevices.keySet()) {
if (!mInRangeBtDevices.contains(btAddress)) {
outOfRangeDevices.add(btAddress);
}
}
for (String btAddress : outOfRangeDevices) {
final BluetoothDevice outOfRangeDevice = mBtDevices.get(btAddress);
mBtDevicesRSSI.remove(btAddress);
mBtDevices.remove(btAddress);
}
}
...
Explanation:
As you can see I have added on each scanning period mInRangeBtDevices collection that will keep all devices found during the current scanning.
When I stop scanning, I am also removing out of range device from previous lists that is not available anymore using one additional helper collection outOfRangeDevices
I think this example would be usefull and you will be able to integrate it in your own code
This one is looking good (JAVA):
As I understood, you need to implement startLeScan().
Find BLE devices
To find BLE devices, you use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:
As soon as you find the desired device, stop scanning.
Never scan on a loop, and set a time limit on your scan. A device that was previously available may have moved out of range, and continuing to scan drains the battery.
The following snippet shows how to start and stop a scan:
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...}
Consider checking this tutorial as well.
Also this one.
I'm developing an app that have to connect with a BLE device, in my code I want to use the new Scan and ScanCallback for BLE implemented from API 21 (Android 5) but I have to maintain the compatibility with Android 4.3 and above.
So I wrote the code, for example, in this way:
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner.startScan(filters, settings, mScanCallback);
} else {
btAdapter.startLeScan(leScanCallback);
}
And I have defined the 2 callbacks, one for API 21 and above and one for API 18 to 20:
//API 21
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(context, false, btleGattCallback);
if (Build.VERSION.SDK_INT < 21) {
btAdapter.stopLeScan(leScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
};
//API 18 to 20
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
btAdapter.stopLeScan(leScanCallback);
runOnUiThread(new Runnable() {
#Override
public void run() {
mBluetoothGatt = device.connectGatt(context, false, btleGattCallback);
}
});
}
};
I also added the annotation
#TargetApi(21)
but when I launch the App on Android 4.x it crashes immediately reporting the error that the class ScanCallback cannot be found (the one intended to be used only with Android 5 and above).
How can I solve this?
Thank you very much.
Daniele.
After reading several posts I did the following. Just in case, here is the documentation of Android about BluetoothLe
First
Create two methods one scanLeDevice21 and scanLeDevice18. On scanLeDevice21 add the annotation #RequiresApi(21) that says:
Denotes that the annotated element should only be called on the given API level or higher.
This is similar in purpose to the older #TargetApi annotation, but more clearly expresses that this is a requirement on the caller, rather than being used to "suppress" warnings within the method that exceed the minSdkVersion.
Second
Implement each method, here's my code.
#RequiresApi(21)
private void scanLeDevice21(final boolean enable) {
ScanCallback mLeScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice bluetoothDevice = result.getDevice();
if (!bluetoothDeviceList.contains(bluetoothDevice)) {
Log.d("DEVICE", bluetoothDevice.getName() + "[" + bluetoothDevice.getAddress() + "]");
bluetoothDeviceArrayAdapter.add(bluetoothDevice);
bluetoothDeviceArrayAdapter.notifyDataSetChanged();
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
};
final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(() -> {
mScanning = false;
swipeRefreshLayout.setRefreshing(false);
bluetoothLeScanner.stopScan(mLeScanCallback);
}, SCAN_PERIOD);
mScanning = true;
bluetoothLeScanner.startScan(mLeScanCallback);
} else {
mScanning = false;
bluetoothLeScanner.stopScan(mLeScanCallback);
}
}
/**
* Scan BLE devices on Android API 18 to 20
*
* #param enable Enable scan
*/
private void scanLeDevice18(boolean enable) {
BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi,
byte[] scanRecord) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
bluetoothDeviceArrayAdapter.add(bluetoothDevice);
bluetoothDeviceArrayAdapter.notifyDataSetChanged();
}
});
}
};
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(() -> {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
Third
Every time you need to scan devices you surround your code asking which version you are. For instance, I have a RefreshLayout to display the list of devices. Here's the result:
/**
* Refresh listener
*/
private void refreshScan() {
if (!hasFineLocationPermissions()) {
swipeRefreshLayout.setRefreshing(false);
//Up to marshmallow you need location permissions to scan bluetooth devices, this method is not here since is up to you to implement it and it is out of scope of this question.
requestFineLocationPermission();
} else {
swipeRefreshLayout.setRefreshing(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scanLeDevice21(true);
} else {
scanLeDevice18(true);
}
}
}
And that's it.
Forget about extending, subclassing classes you don't really need like ulusoyca answer.
Create AbstractBluetoothLe class and IBleScanCallback interface. IBleScanCallback interface is a Marker Interface. In other saying, an interface with no methods. You can also add methods to interface if you need. These methods will do the same functionality for all type of scanCallbacks i.e. getListOfFoundBleDevices(), clearListOfFoundBleDevices() etc.
Create BluetootLeLollipop, and BluetoothLeJellyBean classes which extend AbstractBluetoothLe class. Create also BluetootLeMarshmallow class which extends BluetootLeLollipopclass. AbstractBluetoothLe class has protected field mIBleScanCallback which is an IBleScanCallback object.
Create BleScanCallbackBase class which implements IBleScanCallback.
Create LollipopScanCallback class which extends ScanCallback class and implements IBleScanCallback interface. .This class has a protected field scanCallback which will be instantiated as BleScanCallbackBase object. Create also MarshmallowScanCallback class which extends LollipopScanCallback class.
Create JellyBeanScanCallback class which extends BleScanCallbackBase and implements BluetoothAdapter.LeScanCallback
In BleScanCallbackBase override the method: onScanCallback(...)
In LollipoScanCallback override onScanResult(int callbackType, ScanResult result) and inside this method call the method onScanCallback(...) of the scanCallback object.
In JellyBeanScanCallback override onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) and inside this method call onScanCallback(...)
Finally, do whatever needs to be done when a device is found in onScanCallback(...)method of BleScanCallbackBase class.
In short, read about composition over inheritance- I know this is not an answer to your question but this is a neat way of what you want to achieve in the end. Here is the class diagram:
Your code is crashing because it is creating anonymous inner class. Hence at run time it doesn't find that sCanCallback class.
Try below way and share the outcome. Before you try this, make sure you comment the callback(ScanCallback).
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner.startScan(filters, settings, new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(context, false, btleGattCallback);
if (Build.VERSION.SDK_INT < 21) {
btAdapter.stopLeScan(leScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
};
} else {
btAdapter.startLeScan(leScanCallback);
}
I would like to share my idea to overcome this crash on devices below API 21. The ScanCallback class has support from API 21. So when you write a code to implement ScanCallback in your scan code it will cause a crash on lover APIs. I have fixed it the following way:
I created one new abstract class which extending ScanCallback class as follows:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public abstract class AppScanCallback extends ScanCallback {}
Now I am using this class instance in my BluetoothService class as follow:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private AppScanCallback mScanCallback;
And using this variable as follow:
public void startBleScan() {
if (isEnabled()) {
if (Build.VERSION.SDK_INT < 21) {
_bluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner = _bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.build();
filters = new ArrayList<>();
mScanCallback = new AppScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
if (Build.VERSION.SDK_INT >= 21) {
BluetoothDevice btDevice = result.getDevice();
onLeScanResult(btDevice, result.getScanRecord().getBytes());
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
mLEScanner.startScan(filters, settings, mScanCallback);
}
}
}
and to stop scan
public void stopBLEScan() {;
if (Build.VERSION.SDK_INT < 21) {
_bluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
Hope it will help you out.
I know how to use the estimote sdk to alter the beacon's uuid, major and minor values but I was wondering if there exists an .apk that already does this to save me time.
You can change Major, Minor, Broadcasting Power, Advertising interval at official app by Estimote.
But changing of UUID is possible only by SDK.
I leave the code here, maybe someone will find it usefull, It'a pretty much the same for the three of them, only changes the writeMajor.
private void setMajorID(final int majorid,final Beacon beacon) {
mMajorsConnection = new BeaconConnection(this, beacon, new BeaconConnection.ConnectionCallback() {
#Override
public void onAuthenticated(BeaconConnection.BeaconCharacteristics chars) {
Log.d(TAG, "Authenticated to beacon: " + chars);
mMajorsConnection.writeMajor(majorid, new BeaconConnection.WriteCallback() {
#Override
public void onSuccess() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.update(beacon);
}
});
Log.d(TAG, "Successfully writted the major id!");
mMajorsConnection.close();
}
#Override
public void onError() {
Log.d(TAG, "Error while writting the major id!");
}
});
}
#Override
public void onAuthenticationError() {
Log.d(TAG, "Authentication Error");
}
#Override
public void onDisconnected() {
Log.d(TAG, "Disconnected");
}
});
mMajorsConnection.authenticate();
}