Browsing the Android API difference report in the wake of new platform versions is always interesting, as you may find additions, changes and removals that are not (yet) heavily promoted by Google. In what will likely become api level 31 and can currently be used with
defaultConfig {
targetSdkVersion("S")
minSdkVersion("S")
we see a new class android.hardware.Battery
.
The docs currently say:
The
Battery
class is a representation of a single battery on a device.
We get five constants: STATUS_CHARGING
, STATUS_DISCHARGING
, STATUS_FULL
, STATUS_NOT_CHARGING
and STATUS_UNKNOWN
.
There are three query methods:
getCapacity()
Get remaining battery capacity as float percentage [0.0f, 1.0f]
of total capacity Returns -1 when battery capacity can't
be read.
getStatus()
returns, well, the battery status. One of the previously mentioned constants is used. What, by the way, always leaves me puzzled if wht Google doesn't useenum
s. Usingint
may be a safe bet if the range might change, but this should not be the case here, right?hasBattery()
Check whether the hardware has a battery.
Now, this left me puzzled, because... if I have an instance of this class we obviously have a battery, right? So, shouldn't this query method be somewhere else?
Besides... haven't we been able to access the battery status for quite a while?
Let's do a brief recap.
Since api level 21 we can write
getSystemService(BatteryManager::class.java)?.run {
// Remaining battery capacity as an integer percentage
// of total capacity
println(getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))
println(
when (getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS)) {
BatteryManager.BATTERY_STATUS_CHARGING -> "charging"
BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "not charging"
BatteryManager.BATTERY_STATUS_FULL -> "full"
BatteryManager.BATTERY_STATUS_DISCHARGING -> "discharging"
else -> "unknwon"
}
)
}
So we get an instance of android.os.BatteryManager
which we can then query using getIntProperty()
.
Alternatively we can utilize a BroadcastReceiver
. As Android's BatteryManager
uses a sticky Intent
this looks ike this:
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(null, filter)?.run {
val level = getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
println(level)
println(
when (getIntExtra(
BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN
)) {
BatteryManager.BATTERY_STATUS_CHARGING -> "charging"
BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "not charging"
BatteryManager.BATTERY_STATUS_FULL -> "full"
BatteryManager.BATTERY_STATUS_DISCHARGING -> "discharging"
else -> "unknwon"
}
)
}
So the information we get from the new Battery
class can be obtained old school, too.
Now please recall the screenshot I provided a little earlier. Was there something that caught your attention? Yes, the class is abstract
. So what do we really get?
Or, to rephrase... How do we actually get instances of (derived classes of) Battery
? BatteryManager
so far has no additions or changes regarding this.
There's another class dealing with power...
android.os.PowerManager
in fact has two additions:
getBatteryDischargePrediction()
Returns the current battery life remaining estimate.
The docs continue:
The estimated battery life remaining as a
Duration
.
Will benull
if the device is powered, charging, or an error
was encountered.
isBatteryDischargePredictionPersonalized()
Returns whether the current battery life remaining estimate
is personalized based on device usage history or not.
This value does not take a device's powered or charging
state into account.
So nothing related to Battery
.
What now? Should we shrug the shoulders and give up?
No.
Let's use the commandline. Java has a jdeps
tool which can list dependencies. We will pass android.jar which is located here:
So, it's android.view.InputDevice
.
Let's take a closer look:
Gets the battery object associated with the device, if there is
one. Even if the device does not have a battery, the result is
nevernull
. UseBattery#hasBattery
to determine whether a
battery is present.
Remember what I said earlier?
Now, this left me puzzled, because... if I have an instance
of this class we obviously have a battery, right?
So, shouldn't this query method be somewhere else?
Apparently not. 🤣
So now we solved the puzzle, right?
One more thing.
If android.hardware.Battery
is abstract, what's the actual type at runtime?
val m = getSystemService(InputManager::class.java)
for (i in m.inputDeviceIds) {
m.getInputDevice(i)?.run {
println(name)
println(battery.javaClass)
}
}
Running this on the Android Emulator gives me tons of android.hardware.input.InputDeviceBattery
.
That's it for today. I hope you liked this little treasure hunt. Please share your thoughts in the comments.
Top comments (2)
Plenty of devices running Android don't have batteries: "smart" TVs, set-top boxes, etc. Power source: yes, battery: maybe. Perhaps they're trying hard to avoid
null
.Anyway, it is interesting to see how to inspect the API.
I agree. Regarding trying hard to avoid null: maybe the
InputDevice
should have thehasBattery()
method instead. Then the api flow would be:getBattery()
might always be non-null, nonetheless. Just feel that query method should not be there :-)