How to get Bluetooth Headset battery level? - android

I'm trying to build an app which gets battery level of currently connected Bluetooth headset. This app can be used on phones which don't have this functionality built-in.
While searching on stackoverflow, I found How to get Bluetooth Headset battery status in android this question. I got the currently connected Bluetooth headset using BluetoothProfile.HEADSET profile.
But in the device object of type BluetoothDevice I don't see any method or property to get battery level of Bluetooth Headset.
I can get the device name and isAudioConnected.

If question is about Bluetooth HFP feature: HF indicators feature is optional for the both sides. If the both sides support it, BluetoothHeadset will broadcast BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED with BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID equal 2 (Battery Level) and BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE with scope 0..100. Do not remember Android version were it was implemented, you should check it.
Also battery level can be implemented in device using vendor specific HFP AT commands (especially for old handsfree devices) and maybe BLE.

I found a solution, but it only works on android 8 and above
I took this code from here
Kotlin
fun getBatteryLevel(pairedDevice: BluetoothDevice?): Int {
return pairedDevice?.let { bluetoothDevice ->
(bluetoothDevice.javaClass.getMethod("getBatteryLevel"))
.invoke(pairedDevice) as Int
} ?: -1
}

The first thing to register BroadcastReciver by "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"
and you can receive this action by the broadcast receiver then get extra data by "android.bluetooth.device.extra.BATTERY_LEVEL"
and if you want to trigger this action, you need to reconnect your Bluetooth device or Bluetooth device battery level happened to change.
Good luck for you.

Connected AirPods Pro to OnePlus 5T with Android 9.
None of those registered events happen:
"android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"
"android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"

I am Able to achieve the handset battery Level in Java
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice("Connected device ID");
java.lang.reflect.Method method;
method = device.getClass().getMethod("getBatteryLevel");
int value = (int) method.invoke(device);
result.success(value);
} catch (Exception ex) {
result.error("invalid_argument", "'deviceId' argument is required to be string", null);
break;
}

This is #Kirill Martyuk answer as an Extension variable
val BluetoothDevice.batteryLevel
get() = this.let { device ->
val method = device.javaClass.getMethod("getBatteryLevel")
method.invoke(device) as Int?
} ?: -1
Usage would be something like
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
val adapter = manager?.adapter
val devices = adapter?.bondedDevices.orEmpty()
devices.forEach { device ->
Log.d("DEVICE_NAME", device.name)
Log.d("CHARGE_LEVEL", device.batteryLevel.toString())
}

Related

How to get battery percentage or low battery information(to be specific) of the RCU in Android TV

How to get the information about current battery level or low battery level information of the RCU (remote control unit) for my Android TV.
I need to show customised message when the battery level is low i.e., "Time to change the battery....." or similar.
I've seen that the system detects the low battery and shows a system notification, but not sure how can i my application (which is a system application) can get that information.
I've already tried the below approaches, doesn't seems to work
How to get the battery level after connect to the BLE device?
How to get Bluetooth Headset battery level?
and this as well
val btManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
val mBluetoothAdapter = btManager.adapter
val pairedDevices = mBluetoothAdapter.bondedDevices
val build = StringBuilder()
for (bt in pairedDevices){
val method: Method = bt.javaClass.getMethod("getBatteryLevel")
val value = method.invoke(bt) as Int
build.append(" (Battery : $value)")
}
val tvDetails = findViewById<TextView>(R.id.tv_details);
tvDetails.text = build.toString()
None of the above method has worked so far, Also i couldn't find anything specific in Official documentation.
Please let me know if anyone know or have found the solution for this. Thanks in advance!

Weird bluetooth deviceClass is being returned by bluetooth event: 240404

I am using a broadcast receiver to try to find when a bluetooth headphone is connect/disconnected to an android device.
val filter = IntentFilter()
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
And I handle the event in this way:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val major = device?.bluetoothClass?.majorDeviceClass;
val bluetoothClass = device?.bluetoothClass ?: return
What I get is
major as 1024 (that is BluetoothClass.Device.Major.AUDIO_VIDEO which is correct)
bluetoothClass.deviceClass as 240404 but it is not listed as any of the recognized Bluetooth Devices.
Am I using the wrong property to compare it?
So silly me, I was comparing the wrong thing. What I have to compare is the deviceClass property inside bluetoothClass

How to connect to Bluetooth SPP using dot42?

After reading the dot42 comments and trolling Java examples I managed to setup a Bluetooth connection but fail to open the connection. I cannot determine the problem. I followed the docs step by step.
My target device is a HTC Explorer running on 2.3 Gingerbread. Here is my code.
//Target 2.3 (Gingerbread)
[assembly: Application("dot42Application1")]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH)]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH_ADMIN)]
namespace dot42Application1
{
[Activity]
public class MainActivity : Activity
{
private TextView txStatus;
protected override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
SetContentView(R.Layouts.MainLayout);
// Find UI controls
txStatus = FindViewById<TextView>(R.Ids.txStatus);
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
var bt = BluetoothAdapter.GetDefaultAdapter();
if (bt != null) //If device has not Bluetooth this will be null
{
if (bt.IsEnabled()) //Is Bluetooth device enabled?
{
var BT_My_Addr = bt.Address; //Get the devices MAC
var BT_Bonded = bt.GetBondedDevices().ToList(); //Get a list of bonded devices- I bonded to a BT2TTL Board earlier.
txStatus.Text = BT_My_Addr + System.Environment.NewLine; //Shows my MAC on screen.
string BT_Remote_Address = string.Empty;
foreach (var BTDevice in BT_Bonded) //Just searchging for string in bonded list
{
if (BTDevice.Name.Contains("linvor"))
{
BT_Remote_Address = BTDevice.Address;
}
}
//Gets remote device
var BT_Remote_Device = bt.GetRemoteDevice(BT_Remote_Address);
//Create a RFCOMM Socket to remote device using popular UUID ofr BT Serial boards
var BTsocket = BT_Remote_Device.CreateInsecureRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"));
//Call anyway to make sure there is no discvoerry in the backgorund. It slows stuff down.
bt.CancelDiscovery();
//Exception here? Dont know why :(
BTsocket.Connect();
//Suppsoed to dump 0 to 99999 to my listening serial device but I never get this far.
var BT_Out = BTsocket.GetOutputStream();
for (int i = 0; i < 99999; i++)
{
BT_Out.Write(Encoding.ASCII.GetBytes(i.ToString()));
}
}
else
{
txStatus.Text = "Bluetooth is disabled :(";
}
}
}
}
And this is what it shows after the socket creation
and the error...
What am I doing wrong? :(
I seem to have solved the problem by analysing various code snippets on the internet. I think the problem was trying to do everything in the OnCreate method. The steps I followed are the following:
Created a button on the main view (MainActivity.xml) and attached a onClick method.
Moved all the code OUT of the OnCreate method. (I think this allows the application to fully initialise.) Created an event handler for the button with two methods.
The two methods are the same as the code I posted in my original question. Just they are separated out and called when the user clicks the button.
findBT() Gets the default adapter. Checks if Bluetooth is enabled if not does the intent filter. Or if it is it will cycle through the bonded list and match a device name and store the BluetoohDevice in a variable. This is another thing that is different from my code. I do not use GetRemoteDevice I just assign the device from the BondedList to my global variable.
openBT() creates the RFCOMM socket (this did not work with unsecure - it threw an exception but using the secure method worked!)
You have to pair to the remote device using the Androids Bluetooth control panel. This code will not scan or connect to devices that are not paired. It will just throw null exceptions.
Also I left the target SDK 2.3.x but I am using the 4.x API.
-Disclosure. I am not a seasoned Android developer and just learning about the life cycle of Java applications in the Android context. I hope this can help other C# developers trying to do the same.

Can I turn on WiFi-Direct from code? on API-16 (Android 4.2.2)

I am developing an application with NFC and wifi direct. I get the MAC address using NFC and the Wifi Direct to transfer data. I call discoverpeers() and could get success. But there is no callback WIFI_P2P_PEERS_CHANGED_ACTION, the callback comes only when I go to settings and the select wifidirect.
This was discussed in the other question
Can I turn on WiFi-Direct from code? on Android API-14 (ICS)
"I'd like to add that WiFi direct on JB and above (at least on AOSP) is not active all the time - it only appears to be. If you look at listeners for WiFi direct, it turns itself off after some time. It turns itself back on if you open the wifi direct menu, however. You might have to have the host do a peer search or initialize itself in order to be able to be found. Likely a battery saving trick. I have also found that it's blocking, since as it accepts a connection, the entire system will lock up and fail to connect sometimes. (The system invitation) – Mgamerz "
Can anyone suggest the solution for the problem WIFI_P2P_PEERS_CHANGED_ACTION callback is not got and can get only when manually go to settings->wifi->tap on wifidirect
I used two devices Samsung galaxy nexus and nexus 7 both running on 4.2.2
There is no available API to enable wifiP2P but you can invoke method "enableP2p" from android settings 4.0.1
WifiP2pManager manager = (WifiP2pManager) getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(getActivity(), getActivity().getMainLooper(), null);
try {
Method method1 = manager.getClass().getMethod("enableP2p", Channel.class);
method1.invoke(manager, channel);
//Toast.makeText(getActivity(), "method found",
// Toast.LENGTH_SHORT).show();
} catch (Exception e) {
//Toast.makeText(getActivity(), "method did not found",
// Toast.LENGTH_SHORT).show();
}
To disable wifiP2P use this method
Method method1 = manager.getClass().getMethod("disableP2p", Channel.class);
Not from code. The user has to. That's why the demo has the link to wifi settings in the action bar.
When you call manager.discoverPeers(channel, new WifiP2pManager.ActionListener()
define onFailure and look at the reasonCode. If it's 0, then either the Wifi or WiFi direct is off.
If you look at the WiFi Direct demo app, the WifiDirectBroadcast Reciever, this piece of code looks at whether p2p is enabled specifically
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
// UI update to indicate wifi p2p status.
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wifi Direct mode is enabled
activity.setIsWifiP2pEnabled(true);
} else {
activity.setIsWifiP2pEnabled(false);
activity.resetData();
}
Then when discover peers is called it looks at the variable set by setIsWifiP2pEnabled
thanks user3093354. to continue with your solution, in order to disable the p2p you have to invoke:
Method method1 = manager.getClass().getMethod("disableP2p", Channel.class);
//Try this it may be help you
WifiManager wifiManager = (WifiManager)this.getSystemService(this.WIFI_SERVICE);
wifiManager.setWifiEnabled(true); //True - to enable WIFI connectivity .
//False -disable WIFI connectivity.
//add this permissions in Manifest file :
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
You can load the wifi driver from a command prompt with the desired concurrency level if you are rooted:
/system/bin/insmod /system/lib/modules/wlan.ko con_mode=3
These are the values:
typedef enum
{
VOS_STA_MODE=0,
VOS_STA_SAP_MODE=1,
VOS_P2P_CLIENT_MODE,
VOS_P2P_GO_MODE,
VOS_MONITOR_MODE,
VOS_FTM_MODE = 5,
VOS_IBSS_MODE,
VOS_P2P_DEVICE_MODE,
VOS_MAX_NO_OF_MODE
} tVOS_CON_MODE;
This is for an Atheros card.

How do I get the UUID of a bluetooth device?

I need to know UUID on API 8 (2.2) or possibly 2.3.3.
As I understand the documentation, this should be allowed:
phoneDevice = blueAdapter.getRemoteDevice(phoneAddress);
ParcelUuid[] phoneUuids = phoneDevice.getUuids(); // Won't compile
Eclipse gives me:
"The method getUuids() is undefined for the type BluetoothDevice."
But see:
http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getUuids()
Also, I would like to know how the UUIDs are "parceled" inside the ParcelUuid[]. In case I ever manage to get there, how do I retrieve a UUID from a parcelUuid[]? Documentation for Android bluetooth seems to be very poor, in my opinion.
What a joke!
Now I try to get it from the intent, but this too gives: *"EXTRA_UUID cannot be resolved or is not a field"*:
intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID);
You have to use reflection to use the getUuids() and fetchUuidsWithSdp() on android version < 3. So, try the code:
Method method = phoneDevice.getClass().getMethod("getUuids", null);
ParcelUuid[] phoneUuids = (ParcelUuid[]) method.invoke(phoneDevice, null);
//this will support from API level 15 and above.
Broadcast Action: This intent is used to broadcast the UUID wrapped as a ParcelUuid of the remote device after it has been fetched. This intent is sent only when the UUIDs of the remote device are requested to be fetched using Service Discovery Protocol
Always contains the extra field EXTRA_DEVICE
Always contains the extra field EXTRA_UUID
Requires BLUETOOTH to receive.
Constant Value: "android.bluetooth.device.action.UUID"
//no way to degrade its hardware related. there is no supporting jar also. http://developer.android.com/sdk/compatibility-library.html
Unfortunately, I don't think there is any good way to get the UUID's supported by a BluetoothDevice with API level < 15. I guess that's why they added the new functions in API 15.
Note, from the docs for BluetoothClass
BluetoothClass is useful as a hint to roughly describe a device (for
example to show an icon in the UI), but does not reliably describe
which Bluetooth profiles or services are actually supported by a
device. Accurate service discovery is done through SDP requests, which
are automatically performed when creating an RFCOMM socket with
createRfcommSocketToServiceRecord(UUID) and
listenUsingRfcommWithServiceRecord(String, UUID).
So, perhaps the device class could be used as a hint as to what services will be available until you perform one of the listed functions. Certainly it doesn't hurt to check the class since this won't require any additional bluetooth operations.
Note that the service class is also available (it is part of the device class) but this is just a general class, not a listing of specific services (like from SDP).
try BluetoothAdapter class
any question, read: http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
In case you can not get UUID from getUuids() method. Please try the other way.
After scanned successfully, you should receive byte[] (scanRecord), so from this result, if you can recognize UUID format you can split step by step to get correct UUID as these codes.
P/s : Important thing, you should know UUID format to get from index correctly.
// Put item into hash map
// UUID from index 10 to 24 : 12233445566778899aabbccddeeff0
StringBuilder mSbUUID = new StringBuilder();
for (int i = 0; i < scanRecord.length; i++) {
// UUID
if (i >= 10 & i <= 24) {
if (Integer.toHexString(
scanRecord[i]).contains("ffffff")) {
mSbUUID.append(Integer.toHexString(scanRecord[i]).replace("ffffff", "") + "-");
} else {
mSbUUID.append(Integer.toHexString(scanRecord[i]) + "-");
}
}
}

Categories

Resources