Android 12 gets a... Battery
Tags: android
Published: 2021-04-08 11:54:49

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

```groovy
defaultConfig {
  targetSdkVersion("S")
  minSdkVersion("S")
```

we see a new class `android.hardware.Battery`.

![Screenshot of the Android 12 Developer Preview documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qv5ek53jo3eiwl6j53ao.png)

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 `enum`s. 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

```kotlin
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:

```kotlin
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 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hro4cdtjfe92s9alyrf.png)

![Screenshot showing the Windows command prompt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yme94lylry6psri4wc85.png)

So, it's `android.view.InputDevice`.

![Screenshot of the Android 12 Preview documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nglehzg5w6rr761yqh1x.png)

Let's take a closer look:
 
![Screenshot of the Android 12 Preview documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x337uk4rhecan1ogv91e.png)
 
> 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?

```kotlin
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.

---

[Source](https://github.com/tkuenneth/android-demos/tree/master/s/BatteryDemo)