Uh oh, low battery - battery information in a UWP

The low battery logo puts fear in most - that and no WiFi. It's the moment where humans do whatever they can to ensure their beloved mobile device does not die.

The normal thing to do, is to just find a power outlet and plug in, but there is never one around when you need it. One way to make sure that your device does not die is to carry a battery charging pack, you know, those unattractive, bulky, heavy brick things you attach to your beautiful, thin device that weighs just a couple hundred grams. 

This was never really a big issue before, so how have we got here? Well, one of the reasons for this, is that mobile devices are being used more than ever, and, we expect them to be thinner, have bigger and brighter displays with large mega pixel cameras to take photos, followed by fast performing on-the-fly image manipulation before we send them on their way to our favorite social media channels. All this processing and hardware features rely on a great battery, however, although battery capacity has slowly increased over time it's not linear in comparison to the usage of our devices. 

This is an example of usage, blue is battery capacity, grey is usage of battery required

This is an example of usage, blue is battery capacity, grey is usage of battery required

This post is going to show how you can help users with just a few lines of code.  It'll help you be a good citizen on your users devices and ensure that you respect their battery life, otherwise, you could face the dreaded uninstall and poor store review.  

Using a Custom Trigger I'm going to create a simple check on the battery percentage, if it's below a certain threshold then I'll ask the user to plug in and charge the device before I do my battery intensive process.

Here is what the application looks like:

The XAML is pretty simple, using the VisualStateManager and a Custom State Trigger I change the user interface.

For more on VisualStateManager and Custom State Triggers review my previous post.

<Grid Background="DarkTurquoise">
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
      <VisualState x:Name="PlentyOfBattery">
        <VisualState.StateTriggers>
          <local:BatteryTrigger Charging="False" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
          <Setter Target="StatusTextBlock.Text" 
                  Value="Good to go!" />
        </VisualState.Setters>
      </VisualState>
      <VisualState x:Name="NeedsCharging">
        <VisualState.StateTriggers>
          <local:BatteryTrigger Charging="True" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
          <Setter Target="StatusTextBlock.Text" 
                  Value="Please plug in! Battery intensive process coming up" />
        </VisualState.Setters>
      </VisualState>
    </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>
  <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <TextBlock x:Name="BatteryPercentageTextBlock" 
               HorizontalAlignment="Center" 
               FontSize="64" 
               FontWeight="Normal" /> 
    <TextBlock x:Name="StatusTextBlock" FontSize="32" />
  </StackPanel>
</Grid>

The BatteryTrigger class is where all the magic happens.

In the namespace Windows.Devices.Power there is a Battery class. If your device has multiple batteries you could reference a battery by ID or, in this case just take the sum of all batteries using the AggregateBattery property.

Now I have the AggregateBattery, I have access to a report on the battery using the method GetReport(). This provides information like, whether the battery is charging, discharging, fully charged, the rate of charge and many more features. As I'm interested in changes to the report, I wire up an event called ReportUpdated in the constructor. The event won't be fired instantly - as there won't be a change instantly - so I explicitly call my helper method called UpdateStatus().

public BatteryTrigger()
{
    Windows.Devices.Power.Battery.AggregateBattery.ReportUpdated 
    += async (sender, args) => await UpdateStatus();

    UpdateStatus();
}

All of the logic happens in the UpdateStatus method, let's take a look.

private async Task UpdateStatus()
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        var batteryReport =  Windows.Devices.Power.Battery.AggregateBattery.GetReport();

        var percentage = (batteryReport.RemainingCapacityInMilliwattHours.Value /
(double)batteryReport.FullChargeCapacityInMilliwattHours.Value);

      if (percentage < 0.85)
      {
          switch (batteryReport.Status)
          {
              case BatteryStatus.Charging:
                SetActive(!this.Charging);
                break;
              case BatteryStatus.Discharging:
                SetActive(this.Charging);
                break;
          }
      }
      else
      {
          SetActive(!this.Charging);
      }
    });
}

First,  I get the get the battery report by calling the GetReport() method. Then I calculate the battery percentage, if it's < 85% then I want to determine if the device is being charged. If the device is being charged then everything is fine, if not, then I prompt the user to plug in a charger as I don't want to drain the users battery on a battery intensive process.

That's all there is to it!

Find the source at https://github.com/shenchauhan/blog/tree/master/Battery

Channel 9 video: https://channel9.msdn.com/Shows/Inside-Windows-Platform/Detecting-and-Responding-to-Battery-Status-in-UWP-apps

Happy coding!