Detect Beacon (Eddystone) with Android Nearby API - java

I'm trying to detect beacons around me, but with the Nearby API, i can't seem to find them.
I'm using this method to detect nearby devices :
public void startDiscovery(String serviceId, final OnDiscoveryListener l) {
Nearby.Connections.startDiscovery(googleApiClient, serviceId, Connections.DURATION_INDEFINITE, new Connections.EndpointDiscoveryListener() {
#Override
public void onEndpointFound(String endpointId, String deviceId, String serviceId, String endpointName) {
if (l != null) {
l.onEndpointFound(endpointId, deviceId, serviceId, endpointName);
}
}
#Override
public void onEndpointLost(String s) {
if (l != null) {
l.onEndpointLost(s);
}
}
})
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (l != null) {
l.onResult(status);
}
}
});
}
The listener looks like this :
public interface OnDiscoveryListener {
public void onResult(Status status);
public void onEndpointFound(String endpointId, String deviceId, String serviceId, String endpointName);
public void onEndpointLost(String s);
}
But i can't detect anything

I work on the Nearby API at Google. The code snippet above uses the Nearby Connections API--which is actually geared towards different use-cases. To work with Eddystone, use the Nearby Messages API. Here's an example of using Nearby Messages to subscribe to the presence of beacons
Note that you need to first associate a Message payload with the beacon using the Proximity Beacon API.

Even though Google advertised the usage of the Nearby API to detect beacons around us :
The Nearby API for Android and iOS makes it easier for apps to find and communicate with nearby devices and beacons
Source : http://android-developers.blogspot.be/2015/07/lighting-way-with-ble-beacons.html
I wasn't able to use the Nearby API to detect Eddystone beacons. I will describe bellow all the information I have gathered during my research hoping this might save some people some time.
I had setup about the same code as you to detect Estimote beacons but I wasn't able to find the Service ID to provide to Nearby.Connections.startDiscovery() to make it detect my beacon (I tried to use the beacon UUID and some variants without success).
I found on Estimote's website that you need to update the beacon firmware to be able to configure it as an Eddystone beacon : http://developer.estimote.com/eddystone/#configure-estimote-beacons-to-broadcast-eddystone. Mine wasn't so I did it.
Nevertheless, that didn't solve my problem and I was still unable to detect the beacon using the Nearby API. I tried setting it as a Eddystone-UID and a Eddystone-URL and tried several combination (based on the information provided by the Estimote app) as Service ID unsuccessfully.
A more deeper look into the Nearby API's documentation shows that Google doesn't mention anything about beacons nor Eddystone in their Nearby API documentation (https://developers.google.com/nearby/connections/overview) and Google's sample about beacons is not using the Nearby API at all : https://github.com/google/beacon-platform/tree/master/samples/android
Yet, they do mention that Nearby Messages will allow rich interaction such as "collaborative editing, forming a group, voting, or broadcasting a resource" and is coming soon :
Coming Soon: The Nearby Messages API will be available in Google Play service 7.8. This site will be updated with the complete API documentation when the new version is available.
Source : https://developers.google.com/nearby/
My understanding is the support for beacons will be available with the Google Play Services 7.8 since the beacons are broadcasting resources.
In the meantime, if you still wish to be able to detect Eddystone beacons around you, you can use Estimote's Android SDK : https://github.com/estimote/android-sdk#quick-start-for-eddystone
or implement the same code as Google's Beacon Proximity sample :
https://github.com/google/beacon-platform/tree/master/samples/android

This may sound like an odd thing - but try turning you wifi on and off. A lot of cheaper devices are really bad with bluetooth and fail to connect, but for some reason turning wifi off and on fixes it for a while.
I was having a similar problem - everything in my code was correct but not detecting beacons. I even tried with the manufacturers app, and theirs also couldn't detect the beacon (but their iPhone version did). I turned my wifi off, then back on again and the manufacturers app worked and so did mine.

Yes, you can discover Eddystone beacons using the Nearby API. But to detect beacons you have to use the Nearby Messages API not the Nearby Connections API, as #Andrew Bunner also mentions.
To be able to detect the beacons using NearbyMessagesAPI you will have to also register the beacons with Google first, and than configure them to have a message payload associated with them.
You can find all the steps in my blog post or this other answer I posted.

Related

Java Custom Google Analytics 4 Server-Side Event User-IP

In my current Java project, it's easy to track server-side user events in the "old" Google Analytics Universal Project with simple REST calls to Google Analytics. So that location tracking was working, i could override the server ip with the user ip, according to the parameter "&uip=1.2.3.4" (https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters?hl=de#uip).
As upgrading to GA4 is recommended, I was able to change all the REST parameters in my project and show my events in the new dashboard, except for the user location. I can't find any information about such a parameter. I tried using still "uip" but now all my requests are located to the country of my server.
Unfortunately it's not possible to track the event client side, because my project is a simple REST API, returning only JSON data.
Does anyone have an idea, if there's such a parameter like "uip" for ga4 or if this isn't possible anymore?
In the following way I setup my parameters:
private String getQueryParameters(MeasurementEvent event) {
StringBuilder body = new StringBuilder();
body.append("?v=").append(version);
body.append("&tid=").append(trackingId);
body.append("&cid=").append(event.getClientId());
body.append("&en=").append(eventName);
body.append("&aip=1");
if (StringUtils.hasText(event.getAction())) {
body.append("&ep.useraction=").append(event.getAction());
}
if (StringUtils.hasText(event.getCategory())) {
body.append("&ep.awsregion=").append(event.getCategory());
}
if (StringUtils.hasText(event.getLabel())) {
body.append("&ep.softwarename=").append(event.getLabel());
}
if (StringUtils.hasText(event.getRemoteAddress())) {
body.append("&uip=").append(event.getRemoteAddress());
}
if (StringUtils.hasText(event.getUrl())) {
body.append("&dl=").append(event.getUrl());
}
return body.toString();
}

How to switching audio between Bluetooth audio devices on android using programmatically

It's let me confused for a long time.
I have two bt audio devices connected on android phone, and I want to switch specific bt audio device using programmatically.
I search keyword about a2dp, media router and audioManager than seems can't do this thing...
I only way found the function is:
BT devices=> A and B connected on an Android phone.
And right now the media play to A and I want to switch to B
Step1: A, B unpair on Android phone.
Step2: A pair on Android phone.
Step3: B pair on Android phone.
The media play output is B,
seems lastest pair Bluetooth device is media play output.
Can anyone give me some advice or direction?
Thanks, guys
Documentation says it can only be connected to a single A2DP device at a time, so you should be able to switch between devices by connecting to device you want to use.
You can connect to device by using BluetoothSockets.
EDIT:
I found other solutions, one which I find reliable. Both of them rely on method reflections.
First one is from this post.
1. Get BluetoothA2dp proxy from serviceListener
bluetoothManager.adapter.getProfileProxy(this, serviceListener, BluetoothProfile.A2DP)
private val serviceListener = object : BluetoothProfile.ServiceListener {
override fun onServiceDisconnected(profile: Int) {
if (profile == BluetoothProfile.A2DP) {
bluetoothA2dp = null
}
}
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
if (profile == BluetoothProfile.A2DP) {
handler.post(btSearchRunnable)
bluetoothA2dp = proxy as BluetoothA2dp
}
}
}
Get "connect" method from reflection.
private val connect = BluetoothA2dp::class.java.getDeclaredMethod("connect",BluetoothDevice::class.java)
Invoke method with proxy as first argument and device as second.
connect.invoke(bluetoothA2dp,device)
Other method used reflection to get createRfcommSocket to circumvent issue with one parameter which you can't normally access. I can post link to it, but problem I had with it is that always lagged main thread, always threw me an error and connected unpredictably.

Using kura framework, how can I specify the address type to "random static" when connecting to a BLE device

I'm trying to create an OSGi bundle that'd be installed on a eurotech gateway (reliagate 10 05).
This bundle would essentially connect the gateway to a BLE device.
To do so, I use a framework provided by eurotech called Everyware™ Software Framework (ESF) that adds up an extra layer on top of the kura v1.2.0 framework.
The catch is, the BLE device only accepts random static address type.
I managed to connect the gateway manually to the BLE device using the following commands in console:
hcitool -i hci0 lecc --random <BD_ADDR>
then
gatttool -i hci0 -b <BD_ADDR> --interactive
This works fine. The hard part is when I try to do the same thing in code using the ESF/kura framework.
Here's a snippet from a sample I use that I found on this page
public boolean connect(String adapterName) {
this.bluetoothGatt = this.device.getBluetoothGatt();
boolean connected = false;
try {
connected = this.bluetoothGatt.connect(adapterName);
} catch (KuraException e) {
logger.error(e.toString());
}
if (connected) {
this.bluetoothGatt.setBluetoothLeNotificationListener(this);
this.isConnected = true;
return true;
} else {
// If connect command is not executed, close gatttool
this.bluetoothGatt.disconnect();
this.isConnected = false;
return false;
}
}
Here is a list of some objects that the sample uses to scan and establish a connection:
org.eclipse.kura.bluetooth.BluetoothAdapter;
org.eclipse.kura.bluetooth.BluetoothDevice;
org.eclipse.kura.bluetooth.BluetoothGattSecurityLevel;
org.eclipse.kura.bluetooth.BluetoothGattService;
org.eclipse.kura.bluetooth.BluetoothLeScanListener;
org.eclipse.kura.bluetooth.BluetoothService;
org.eclipse.kura.bluetooth.BluetoothDevice;
org.eclipse.kura.bluetooth.BluetoothGatt;
org.eclipse.kura.bluetooth.BluetoothGattCharacteristic;
org.eclipse.kura.bluetooth.BluetoothLeNotificationListener;
So I searched through the api doc but didn't find anything.
Though, one interesting SO post mentions a command code to send to the device.
I found a method in kura framework that might help.
Here's the signature:
void ExecuteCmd(java.lang.String ogf, java.lang.String ocf, java.lang.String parameter)
but I couldn't figure out the OpCode Group Field (ogf) associated to the OpCode Command Field(ocf) in any documentation (I skimmed the ~2300 pages of the Bluetooth 4.0 core spec). If anyone knows where to search... :)
In the end, the question is: is there a way to set the address type to random (as with the hcitool command) with the kura framework ?
Or am I totally misleaded ? :/
Anyway, I'm really new to the kura and ble ecosystems so, sorry if it looks like an obvious thing to do but I feel like I'm running out of inspiration and could totally use a hand!
PS: Congrats if you made it to the end!
Haha lol. Kura seems to just start a gatttool process, send commands in text, and parse the output as its interface...
Here is where it is stated, using the address as parameter: https://github.com/eclipse/kura/blob/0339ac787f90debdfc270c1dee0c16de16ea6f7e/kura/org.eclipse.kura.linux.bluetooth/src/main/java/org/eclipse/kura/linux/bluetooth/util/BluetoothUtil.java#L319. Unfortunately the Kura developers seem to have missed that there is something called Random Address in the BLE standard and I don't see how that could be worked around using the current API.
Okay so for those who find themselves in my position in the future, I just received an answer from the Eurotech support team.
Dear Mr. Carneiro,
[...]
Regarding the random BD_ADDR, this is a configuration of the BLE device.
So, your BLE device is advertising an address of type random, not public, and you should specify the address type on the connection string, as you already did.
Unfortunately, current Kura Bluetooth API doesn't provide a way to specify the type of address into the connection string. We are developing a new set of APIs for BLE that will be available on preview on the next Kura/ESF release, but the Reliagate 10-05 will not support these yet.

Java desktop bluetooth client can not find services

I am new to bluetooth programming, so please keep that in mind.
I am trying to connect to a pulse sensor from a desktop pc, specifically the Zephyr HxM BT. I am not using any device specific drivers, the Java bluetooth library I'm using is Bluecove, and my bluetooth stack is BlueSoleil.
From my understanding, the way I proceed is
1) Scan for BT devices
public void startSearch() throws BluetoothStateException{
System.out.println("Inquiry started");
localdevice.getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, this);
}
This works fine, it discovers my pulse belt and calls
public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1)
2) Search a device for services
Once a device has been discovered, it should be added to the DiscoveryAgent's list of cached devices, this is my first problem as the cache (and preknown devices) is always empty even though I've discovered my belt.
So the way that I do this now is to either keep my own list of devices, or simply start a service search directly from deviceDiscovered.
I am still a bit unsure if I'm using the correct parameters, but from reading the BT device manual and the javax.bluetooth documentation on DiscoveryAgent.searchServices:
public int searchServices(int[] attrSet,
UUID[] uuidSet,
RemoteDevice btDev,
DiscoveryListener discListener)
throws BluetoothStateException
My code:
public void searchServices(RemoteDevice device){
UUID[] uuidSet = new UUID[1];
uuidSet[0]=new UUID("1101",false); //Serial Port
System.out.println("Searching for services on " + device.getBluetoothAddress() );
try{
agent.searchServices(null,uuidSet, device,this);}
catch (BluetoothStateException e){
System.out.println("BluetoothStateException caught.");
}
}
I've set the attributes parameter to null, because the documentation states that this will have it search for services with the default attributes, however I've also tried to use only ServiceID (0x0003) as attribute with no luck.
This is where I'm stuck, I pass the correct BT device into the function, and it starts searching but never give me any results, it just searches forever for all I know, no exception, no calls to
public void servicesDiscovered(int arg0, ServiceRecord[] arg1)
or
public void serviceSearchCompleted(int arg0, int arg1)
So I guess my questions are:
- Am I doing something wrong? Any suggestions?
- What are the other approaches to connecting to a BT device, and what
information do I have to know about the device to do that?
Here is some information that I think is relevant from the device manual:
The following steps have to be undertaken to connect to a HxM device.
1) Activate the Bluetooth service of the device/computer wanting to connect to the HxM
2) Scan for Bluetooth devices in range
3) Pair with the HxM device found in range
4) Discover Services of Paired HxM
5) Connect to serial port of HxM device
The diagram above shows that the Bluetooth HxM typically communicates with a mobile device over the Bluetooth
link. The HxM only supports one link at a time and uses the Bluetooth SPP (Serial Port Profile) to communicate
with other devices with the following low-level protocol:
• 115,200 baud
• 8 data bits
• 1 stop bit
• No parity
Any suggestions are very much appreciated
edit: I just want to add that I'm testing the code with a console input loop, so the program is not immediately terminated after calling searchServices, it should have time to complete unless I'm misunderstanding async tasks
I just wanted to update this and say that I found the problem, it seems that I had to use a short UUID instead of a long. I should have tried both of these options before I deemed myself stuck, but I didn't think it would make any difference.

Android GPS does not work until restart on new phones

I have posted about this issue before, and found a few other people who have had the same issue with no solutions found.
I am developing an Android app that submits a JSON query to a server with the obtained GPS coordinates and geocoded Zip Code. For brand new users that have never downloaded the app, GPS does not work whatsoever. It is not until rebooting the phone that GPS will work. After installing the app and then rebooting, the GPS will work every time without problem, even if they restart again.
There is precious little information on this issue, and the only issue I have found refers to using Google Play Location Services, with no further details. Has anyone else had this issue? My development is completely halted until this issue can be resolved.
EDIT:
Here is the link to the MainActivity.java file that calls the geopositioning functions.
Here is the link to the MyLocation.java file that contains the actual logic for multiple sources of geolocation
I have no doubt that there are much better ways of doing GPS. I'm very new to Android development, so any help on this front is very much appreciated.
EDIT 2:
I have wiped my phone with a factory reset, and started from scratch. I still cannot replicate the issue on this device, only on phones using the app for the very first time prior to a restart.
Looks like you're registering both the GPS and NETWORK providers to listen for a location for 10 seconds, and when the timer goes off after 10 seconds you try to get the most recent location from both providers.
There are a few things going on here.
First, you seem to be listening for updates in the wrong method. Your two listeners should look like:
LocationListener locationListenerGps = new LocationListener() {
// This will never be called, its not part of the LocationListener interface - http://developer.android.com/reference/android/location/LocationListener.html
/* public void onStatusChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
} */
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
public void onLocationChanged(Location location) {
// This is the correct method to receive location callbacks
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
Second, I'd use the ScheduledThreadPoolExecutor or Handler instead of Timer.
From the Timer docs:
Prefer ScheduledThreadPoolExecutor for new code...This class does not offer guarantees about the real-time nature of task scheduling.
If a reboot is required to get the app working, its likely something to do with the Timer not firing after 10 seconds. Note that this doesn't necessarily mean that GPS itself isn't working.
Handler should do the job and it's designed for Android, so I'd suggest using it. It looks like:
Handler handler = new Handler();
handler.postDelayed(getLastLocation, 10000);
...and your GetLastLocation would change to:
Runnable getLastLocation = new Runnable() {
#Override
public void run() {
...
}
}
...and your cancel() and other methods would need to reference the Handler.
Also, note that you're declaring the Location object in your MainActivity with a provider type of NETWORK_PROVIDER, and then setting the lat/long in that object.
public Location mUserCoordinates = new Location(LocationManager.NETWORK_PROVIDER);
So, the location type in MainActivity will always appear to be of NETWORK_PROVIDER, no matter the actual source.
Also, doesn't look like your MainActivity needs to implement LocationListener, as its never registered with the LocationManager.
Finally, instead of using two listeners for GPS and NETWORK, I would suggest using the Fused location provider in Google Play Services, as discussed here:
http://developer.android.com/training/location/receive-location-updates.html
You'll be limited to devices Android 2.2 and up with Google Play Services installed, but in my opinion its worth it to avoid dealing with some of the eccentricities of location in the platform and managing more than one provider. For more about Fused location provider and how it differs from listening directly to GPS and NETWORK providers, see this 2013 Google I/O presentation - Beyond the Blue Dot: New Features in Android Location
Why do you use Google Play Services Location API?
The only new feature provided by Play Services is Geofencing. From your answer i assume that you don't want to use Geofencing but just "usual" location requests.
The Android platform provides a great API for such requests which does not requires Google Play Services. I never had the problem you described when using it.
Note that although Google claims the Play Services to be better than the Android API, this is not true since API 9 (Android 2.3), as long as you use the newer LocationManager.requestLocationUpdates methodes that don't require a provider to be specified.
See: http://developer.android.com/guide/topics/location/strategies.html
I had the same issue with the regular Location API earlier. I swiched to Play Services, and it seemed to work. Lately sometimes I experience this issue again with Google Play Services. It is really strange, and based on my experience the probelem is system-wide, so when my app couldn't find location, than the Google Maps app couldn't eather. Note that I use Cyanogenmod, so it can be some bug within it.

Categories

Resources