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!
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
Background
I've noticed that in WifiManager class there is a function called addNetwork, that might be useful if I want to restore or save networks information (network name AKA SSID, together with the password and the type), so that I could also connect to it.
The problem
I can't find much information about how to do such a thing. I've seen various examples on StackOverflow, and if I target Android API 28 (or below), I indeed succeed to make it add a network and even connect to it.
When targeting Android 29 (Android Q), however, it fails to add the network.
What I've found
Since I'm trying on Pixel 2 with Android Q beta 4, I think that maybe it's because addNetwork is deprecated, so the docs even say so, and that if I target Android Q, it won't work, and indeed it doesn't work:
Compatibility Note: For applications targeting Build.VERSION_CODES.Q
or above, this API will always return -1.
The way it seems it should work up till Android Q (excluding), is by preparing WifiConfiguration and adding it. Later I can also connect to it if I wish. On Android Q, it seems it was replaced by WifiNetworkSuggestion, but it doesn't seem like it's about adding a network at all:
The Network Suggestion object is used to provide a Wi-Fi network for
consideration when auto-connecting to networks. Apps cannot directly
create this object, they must use
WifiNetworkSuggestion.Builder#build() to obtain an instance of this
object.
Apps can provide a list of such networks to the platform using
WifiManager#addNetworkSuggestions(List).
Here's my current code, for pre-Android-Q
#WorkerThread
fun addNetwork(context: Context, networkName: String, networkPassword: String? = null, keyMgmt: Int = WifiConfiguration.KeyMgmt.NONE) {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val conf = WifiConfiguration()
conf.SSID = "\"$networkName\""
conf.preSharedKey = if (networkPassword.isNullOrEmpty()) "" else "\"$networkPassword\""
conf.allowedKeyManagement.set(keyMgmt)
when (keyMgmt) {
WifiConfiguration.KeyMgmt.WPA_PSK -> {
//WPA/WPA2
}
WifiConfiguration.KeyMgmt.IEEE8021X -> {
}
WifiConfiguration.KeyMgmt.WPA_EAP -> {
}
WifiConfiguration.KeyMgmt.NONE -> {
if (networkPassword.isNullOrEmpty()) {
//open network
conf.wepKeys[0] = "\"\""
} else {
//wep
conf.wepKeys[0] = "\"" + networkPassword + "\""
conf.wepTxKeyIndex = 0
conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40)
}
}
}
if (networkPassword.isNullOrEmpty()) {
//open network
conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
} else {
}
wifiManager.isWifiEnabled = true
while (!wifiManager.pingSupplicant()) {
Log.d("AppLog", "waiting to be able to add network")
}
val networkId = wifiManager.addNetwork(conf)
if (networkId == -1)
Log.d("AppLog", "failed to add network")
else {
wifiManager.enableNetwork(networkId, false)
Log.d("AppLog", "success to add network")
}
}
Seems it requires only these permissions:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
But in any case, this works as long as you don't target Android Q (API 29) and above. When you target it, I indeed always get "-1" as a result, meaning it fails.
I've also found an issue on the issue tracker (here and I wrote about it here), telling about someone that needs the API back, but I'm not sure it's about adding a network.
Looking at WifiNetworkSuggestion, I don't see that it has as many things to set as WifiConfiguration via its builder, so this is another reason for why I suspect it's not about adding a network.
But I tried anyway. Here's the code I've tried, for example, to add a normal WPA network:
#WorkerThread
fun addNetworkAndroidQ(context: Context, networkName: String, networkPassword: String? = null) {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val list = ArrayList<WifiNetworkSuggestion>()
val builder = WifiNetworkSuggestion.Builder().setSsid(networkName)
if (!networkPassword.isNullOrEmpty())
builder.setWpa2Passphrase(networkPassword)
list.add(builder.build())
val result = wifiManager.addNetworkSuggestions(list)
if (result == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS)
Log.d("AppLog", "success")
else Log.d("AppLog", "failed")
}
When running (I gave it my Wifi network details, after making the OS forget about it), it says it succeeded, but nothing occurred on the OS's Wifi settings. The network doesn't exist there with the password I've added. So I really don't get what it did...
After a few long seconds, I've noticed a notification asking me if it's ok to connect to the suggested networks made by the app:
But still when I chose that I accept, it didn't do anything, as before.
I tried to make another POC, thinking I might have done it incorrectly, but then it didn't even show the notification. Since I think this whole behavior is a bug, I've reported about it here.
Not only that, but I've found out that if indeed it is supposed to add a network one way or another, it still has some serious restrictions, such as max added networks (here) and being removed upon uninstall of the app (here)
The questions
How should Android Q be handled exactly? Is there really no API anymore to add a network?
If WifiNetworkSuggestion is not about adding a network, what is it really used for exactly?
Since I'm not familiar enough with the tidbits of adding a network, is my code correct about all possible ways to add a network? I ask this because someone wrote here that people should enable Wifi and make sure pingSupplicant returns true. Is it true? Or would it be enough to just call addNetwork ?
If it's now impossible to add a network using the normal API, is there maybe a solution by using a rooted device instead? Maybe some adb command?
EDIT: Not sure how to do it officially, but using adb, you might be able to add Wifi-networks on Android 11 . Need to check adb shell cmd wifi help .
I stuck with same issue, but somehow I reached a reproducible state for connecting a desired network and I want to share my findings it may helps.
As a summary:
You have to disable all auto connection before applying WifiNetworkSuggestion logic
For more details, Please read the following:
I used the following code (Similar to what you use):
private fun connectUsingNetworkSuggestion(ssid: String, password: String) {
val wifiNetworkSuggestion = WifiNetworkSuggestion.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build()
// Optional (Wait for post connection broadcast to one of your suggestions)
val intentFilter =
IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
return
}
showToast("Connection Suggestion Succeeded")
// do post connect processing here
}
}
registerReceiver(broadcastReceiver, intentFilter)
lastSuggestedNetwork?.let {
val status = wifiManager.removeNetworkSuggestions(listOf(it))
Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
}
val suggestionsList = listOf(wifiNetworkSuggestion)
var status = wifiManager.addNetworkSuggestions(suggestionsList)
Log.i("WifiNetworkSuggestion", "Adding Network suggestions status is $status")
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
showToast("Suggestion Update Needed")
status = wifiManager.removeNetworkSuggestions(suggestionsList)
Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
status = wifiManager.addNetworkSuggestions(suggestionsList)
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
lastSuggestedNetwork = wifiNetworkSuggestion
lastSuggestedNetworkSSID = ssid
showToast("Suggestion Added")
}
}
So here are the steps:
Install fresh version / Or remove all suggestion you added before
Make sure that you forgot all surrounding networks so your device won't auto-connect
Add wifi network suggestions list
Go to Wifi Settings to scan networks Or wait until next scan is running
A notification prompt will appear :
6. When you Press "Yes" the system will auto-connect with it via your app and internet will work normally. See the following:
Please note the following:
If you disconnect the network from Wifi Settings (i.e press disconnect bin icon in the following image) your network will be blocked for 24 hours from auto-connect even if you removed the suggested network using wifiManager.removeNetworkSuggestions(listOf(it)) and add it again. And even if you uninstall and install your app again
Unfortunately, this is limitation added by Android System as described here:
If the user uses the Wi-Fi picker to explicitly disconnect from one of the network suggestions when connected to it, then that network is blacklisted for 24 hours. During the blacklist period, that network will not be considered for auto-connection, even if the app removes and re-adds the network suggestion corresponding to the network.
If you uninstall the application while connected to suggested WiFi, the system will close the connection automatically.
In case you have multiple suggestion you can priorities them by using WifiNetworkSuggestion.Builder().setPriority(<Priority Integer>) as mentioned here:
Specify the priority of this network among other network suggestions provided by the same app (priorities have no impact on suggestions by different apps). The higher the number, the higher the priority (i.e value of 0 = lowest priority).
In case you pressed "No" in notification prompt, you can change it from (Settings > Apps & notifications > Special App access > Wi-Fi Control > App name) as described here:
A user declining the network suggestion notification removes the CHANGE_WIFI_STATE permission from the app. The user can grant this approval later by going into the Wi-Fi control menu (Settings > Apps & notifications > Special App access > Wi-Fi Control > App name).
I wish I had answers to all of your questions because I'm currently struggling with similar issues.
After many hours I was finally able to connect to the desired network using this approach:
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(passphrase)
.setBssid(mac)
.build()
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build()
val connectivityManager = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
connectivityManager?.requestNetwork(networkRequest, ConnectivityManager.NetworkCallback())
You can receive a whole host of events through the ConnectivityManager.NetworkCallback().
Looks like they've added support in Android 11(API 30) for adding network configuration that persists outside of the application scope and is saved as a system network configuration just like it was done with the deprecated WiFiManager method addNetwork. All you need to do is to use ACTION_WIFI_ADD_NETWORKS to show a system dialog that asks a user if he wants to proceed with adding a new Wifi suggestion to the system. This is how we start that dialog:
// used imports
import android.provider.Settings.ACTION_WIFI_ADD_NETWORKS
import android.provider.Settings.EXTRA_WIFI_NETWORK_LIST
import android.app.Activity
import android.content.Intent
import android.net.wifi.WifiNetworkSuggestion
// show system dialog for adding new network configuration
val wifiSuggestionBuilder = WifiNetworkSuggestion.Builder()
.setSsid("network SSID")
.build()
val suggestionsList = arraylistOf(wifiSuggestionBuilder)
val intent = new Intent(ACTION_WIFI_ADD_NETWORKS)
intent.putParcelableArrayListExtra(EXTRA_WIFI_NETWORK_LIST, suggestionsList);
activity.startActivityForResult(intent, 1000)
The dialog looks like this:
And then we just need to handle a result in onActivityResult method like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 1000) {
if (resultCode == Activity.RESULT_OK) {
// network succesfully added - User pressed Save
} else if (resultCode == Activity.RESULT_CANCELED) {
// failed attempt of adding network to system - User pressed Cancel
}
}
}
But as I've tested this code on Android devices that have older Android versions(lower then API30) installed I've got a crash every time I want it to show that dialog for adding a new network configuration. This is the crash:
java.lang.RuntimeException: Unable to start activity: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.settings.WIFI_ADD_NETWORKS (has extras) }
Looks like the new way is not back-supported out of the box. So, for API30 we can use a new Intent action, for API 28 and below we can still use the old way of adding Networks, but for API29 we have some kind of gray area where I was not able to find a good solution yet. If anyone has an idea what else to do please share it with me. ;)
#Sebastian Helzer's answer works for me. I use java in my application. This may help java users...
WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build();
NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build();
ConnectivityManager connectivityManager = (ConnectivityManager)this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
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())
}
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.