pebble/devsite/source/tutorials/watchface-tutorial/part4.md

158 lines
5.1 KiB
Markdown
Raw Permalink Normal View History

---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
layout: tutorials/tutorial
tutorial: watchface
tutorial_part: 4
title: Adding a Battery Bar
description: |
How to add a battery level meter to your watchface.
permalink: /tutorials/watchface-tutorial/part4/
generate_toc: true
---
Another popular feature added to a lot of watchfaces is a battery meter,
enabling users to see the state of their Pebble's battery charge level at a
glance. This is typically implemented as the classic 'battery icon' that fills
up according to the current charge level, but some watchfaces favor the more
minimal approach, which will be implemented here.
This section continues from
[*Part 3*](/tutorials/watchface-tutorial/part3/), so be sure to re-use
your code or start with that finished project.
The state of the battery is obtained using the ``BatteryStateService``. This
service offers two modes of usage - 'peeking' at the current level, or
subscribing to events that take place when the battery state changes. The latter
approach will be adopted here. The battery level percentage will be stored in an
integer at the top of the file:
```c
static int s_battery_level;
```
As with all the Event Services, to receive an event when new battery information
is available, a callback must be registered. Create this callback using the
signature of ``BatteryStateHandler``, and use the provided
``BatteryChargeState`` parameter to store the current charge percentage:
```c
static void battery_callback(BatteryChargeState state) {
// Record the new battery level
s_battery_level = state.charge_percent;
}
```
To enable this function to be called when the battery level changes, subscribe
to updates in `init()`:
```c
// Register for battery level updates
battery_state_service_subscribe(battery_callback);
```
With the subscription in place, the UI can be created. This will take the form
of a ``Layer`` with a ``LayerUpdateProc`` that uses the battery level to draw a
thin, minimalist white meter along the top of the time display.
Create the ``LayerUpdateProc`` that will be used to draw the battery meter:
```c
static void battery_update_proc(Layer *layer, GContext *ctx) {
}
```
Declare this new ``Layer`` at the top of the file:
```c
static Layer *s_battery_layer;
```
Allocate the ``Layer`` in `main_window_load()`, assign it the ``LayerUpdateProc`` that will draw it, and
add it as a child of the main ``Window`` to make it visible:
```c
// Create battery meter Layer
s_battery_layer = layer_create(GRect(14, 54, 115, 2));
layer_set_update_proc(s_battery_layer, battery_update_proc);
// Add to Window
layer_add_child(window_get_root_layer(window), s_battery_layer);
```
To ensure the battery meter is updated every time the charge level changes, mark
it 'dirty' (to ask the system to re-render it at the next opportunity) within
`battery_callback()`:
```c
// Update meter
layer_mark_dirty(s_battery_layer);
```
The final piece of the puzzle is the actual drawing of the battery meter, which
takes place within the ``LayerUpdateProc``. The background of the meter is drawn
to 'paint over' the background image, before the width of the meter's 'bar' is
calculated using the current value as a percentage of the bar's total width
(114px).
The finished version of the update procedure is shown below:
```c
static void battery_update_proc(Layer *layer, GContext *ctx) {
GRect bounds = layer_get_bounds(layer);
// Find the width of the bar (total width = 114px)
int width = (s_battery_level * 114) / 100;
// Draw the background
graphics_context_set_fill_color(ctx, GColorBlack);
graphics_fill_rect(ctx, bounds, 0, GCornerNone);
// Draw the bar
graphics_context_set_fill_color(ctx, GColorWhite);
graphics_fill_rect(ctx, GRect(0, 0, width, bounds.size.h), 0, GCornerNone);
}
```
Lastly, as with the ``TickTimerService``, the ``BatteryStateHandler`` can be
called manually in `init()` to display an inital value:
```c
// Ensure battery level is displayed from the start
battery_callback(battery_state_service_peek());
```
Don't forget to free the memory used by the new battery meter:
```c
layer_destroy(s_battery_layer);
```
With this new feature in place, the watchface will now display the watch's
battery charge level in a minimalist fashion that integrates well with the
existing design style.
![battery-level >{pebble-screenshot,pebble-screenshot--steel-black}](/images/tutorials/intermediate/battery-level.png)
## What's Next?
In the next, and final, section of this tutorial, we'll use the Connection Service
to notify the user when their Pebble smartwatch disconnects from their phone.
[Go to Part 5 → >{wide,bg-dark-red,fg-white}](/tutorials/watchface-tutorial/part5/)