DEV Community

Cover image for Android 12 gets a... Battery
Thomas Künneth
Thomas Künneth

Posted on • Updated on

Android 12 gets a... Battery

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")
Enter fullscreen mode Exit fullscreen mode

we see a new class android.hardware.Battery.

Screenshot of the Android 12 Developer Preview documentation

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 use enums. Using int 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"
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

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 be null 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:

Directory containing android.jar

Screenshot showing the Windows command prompt

So, it's android.view.InputDevice.

Screenshot of the Android 12 Preview documentation

Let's take a closer look:

Screenshot of the Android 12 Preview documentation

Gets the battery object associated with the device, if there is
one. Even if the device does not have a battery, the result is
never null. Use Battery#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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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.


Source

Top comments (2)

Collapse
 
jeikabu profile image
jeikabu • Edited

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.

Collapse
 
tkuenneth profile image
Thomas Künneth

I agree. Regarding trying hard to avoid null: maybe the InputDevice should have the hasBattery() method instead. Then the api flow would be:

if (inputDevice.hasBattery()) {
  val battery = inputDevice.getBattery()
Enter fullscreen mode Exit fullscreen mode

getBattery() might always be non-null, nonetheless. Just feel that query method should not be there :-)