---
# 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.

title: Core Experience Design
description: |
  How design guidelines shape the core Pebble app experience.
guide_group: design-and-interaction
menu: true
permalink: /guides/design-and-interaction/core-experience/
generate_toc: true
order: 1
---

The core Pebble experience includes several built-in system apps that use
repeatable design and interaction concepts in their implementation. These
are:

* Apps are designed with a single purpose in mind, and they do it well.

* Fast animations are used to draw attention to changing or updating
  information.

* Larger, bolder fonts to highlight important data.

* A preference for displaying multiple data items in a paginated format rather
  than many 'menus within menus'. This is called the 'card' pattern and is
  detail in
  {% guide_link design-and-interaction/recommended#display-data-sets-using-cards "Display Data Sets Using Cards" %}.

* Colors are used to enhance the app's look and feel, and are small in number.
  Colors are also sometimes used to indicate state, such as the temperature in a
  weather app, or to differentiate between different areas of a layout.

## System Experience Design

The core system design and navigation model is a simple metaphor for time on the
user's wrist - a linear representation of the past, present, and future. The
first two are presented using the timeline design, with a press of the Up button
displaying past events (also known as pins), and a press of the Down button
displaying future events. As was the case for previous versions of the system
experience, pressing the Select button opens the app menu. This contains the
system apps, such as Music and Notifications, as well as all the 3rd party apps
the user has installed in their locker.

![system-navigation](/images/guides/design-and-interaction/system-navigation.png)

Evidence of the concepts outlined above in action can be seen within the core
system apps in firmware 3.x. These are described in detail in the sections
below.

### Music

![music](/images/guides/design-and-interaction/music.png)

The Music app is designed with a singular purpose in mind - display the current
song and allow control over playback. To achieve this, the majority of the
screen space is devoted to the most important data such as the song title and
artist. The remainder of the space is largely used to display the most immediate
controls for ease of interaction in an action bar UI element.

The 'previous track' and 'next track' icons on the action bar are ones with
pre-existing affordances which do not require specific instruction for new users
thanks to their universal usaging other media applications. The use of the '...'
icon is used as an additional commonly understood action to indicate more
functionality is available. By single-pressing this action, the available
actions change from preview/next to volume up/volume down, reverting on a
timeout. This is preferable to a long-press, which is typically harder to
discover without an additional prompt included in the UI.

A press of the Back button returns the user to the appface menu, where the Music
appface displays the name and artist of the currently playing track, in a
scrolling 'marquee' manner. If no music is playing, no information is shown
here.

### Notifications

![notifications](/images/guides/design-and-interaction/notifications.png)

The system Notifications app allows a user to access all their past received
notifications in one place. Due to the fact that the number of notifications
received can be either small or large, the main view of the app is implemented
as a menu, with each item showing each notification's icon, title and the first
line of the body content. In this way it is easy for a user to quickly scroll
down the list and identify the notification they are looking for based on these
first hints.

The first item in the menu is a 'Clear All' option, which when selected prompts
the user to confirm this action using a dialog. This dialog uses the action bar
component to give the user the opportunity to confirm this action with the
Select button, or to cancel it with the Back button.

Once the desired item has been found, a press of the Select button opens a more
detailed view, where the complete notification content can be read, scrolling
down if needed. The fact that there is more content available to view is hinted
at using the arrow marker and overlapping region at the bottom of the layout.

### Alarms

![alarms](/images/guides/design-and-interaction/alarms.png)

The Alarms app is the most complex of the system apps, with multiple screens
dedicated to input collection from the user. Like the Watchfaces and Music apps,
the appface in the system menu shows the time of the next upcoming scheduled
alarm, if any. Also in keeping with other system apps, the main screen is
presented using a menu, with each item representing a scheduled alarm. Each
alarm is treated as a separate item, containing different settings and values.

A press of the Select button on an existing item will open the action menu
containing a list of possible actions, such as 'Delete' or 'Disable'. Pressing
Select on the top item ('+') will add a new item to the list, using multiple
subsequent screens to collect data about the alarm the user wishes to schedule.
Using multiple screens avoids the need for one screen to contain a lot of input
components and clutter up the display. In the time selection screen, the current
selection is marked using a green highlight. The Up and Down buttons are used to
increase and decrease the currently selected field respectively.

Once a time and recurring frequency has been chosen by the user, the new alarm
is added to the main menu list. The default state is enabled, marked by the word
'ON' to the right hand side, but can be disabled in which case 'OFF' is
displayed instead.

### Watchfaces

![watchfaces](/images/guides/design-and-interaction/watchfaces.png)

The Watchfaces system app is similar to the Notifications app, in that it uses a
menu as its primary means of navigation. Each watchface available in the user's
locker is shown as a menu item, with a menu icon if one has been included by the
watchface developer. The currently active watchface is indicated by the presence
of 'Active' as that item's subtitle.

Once the user has selected a new watchface, they are shown a confirmation dialog
to let them know their choice was successful. If the watchface is not currently
loaded on the watch, a progress bar is shown briefly while the data is loaded.
Once this is done the newly chosen watchface is displayed.

### Settings

![settings](/images/guides/design-and-interaction/settings.png)

The Settings app uses the system appface to display the date, the battery charge
level, and the Bluetooth connection without the need to open the app proper. If
the user does open the app, they are greeted with a menu allowing a choice of
settings category. This approach saves the need for a single long list of
settings that would require a lot of scrolling.

Once a category has been chosen, the app displays another menu filled with
interactive menu rows that change various settings. Each item shows the name of
the setting in bold as the item title, with the current state of the setting
shown as the subtitle.

When the user presses Select, the state of the currently selected setting is
changed, usually in a binary rotation of On -> Off states. If the setting does
not operate with a binary state (two states), or has more than two options, an
action menu window is displayed with the available actions, allowing the user to
select one with the Select button.

A press of the Back button from a category screen returns the user to the
category list, where they can make another selection, or press Back again to
return to the app menu.

### Sports API

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/sports.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

The Sports API app is designed around displaying the most immediate relevant
data of a particular sporting activity, such as running or cycling. A suitable
Android or iOS companion app pushes data to this app using the
{% guide_link communication/using-the-sports-api "PebbleKit Sports API" %}
at regular intervals. This API enables third-party sports app developers to
easily add support for Pebble without needing to create and maintain their own
watchapp.

The high contrast choice of colors makes the information easy to read at a
glance in a wide variety of lighting conditions, ideal for use in outdoor
activities. The action bar is also used to present the main action available to
the user - the easily recognizable 'pause' action to suspend the current
activity for a break. This is replaced by the equally recognizable 'play' icon,
the action now used to resume the activity.

This API also contains a separate Golf app for PebbleKit-compatible apps to
utilize in tracking the user's golf game.

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/golf.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

The Golf app uses a similar design style with larger fonts for important numbers
in the center of the layout, with the action bar reserved for additional input,
such as moving between holes. Being an app that is intended to be in use for
long periods of time, the status bar is used to display the current time for
quick reference without needing to exit back to the watchface.

## Timeline Experience Design

Evidence of guided design can also be found in other aspects of the system, most
noticeably the timeline view. With pins containing possibly a large variety of
types of information, it is important to display a view which caters for as many
types as possible. As detailed in
{% guide_link pebble-timeline/pin-structure "Creating Pins" %},
pins can be shown in two different ways; the event time, icon and title on two
lines or a single line, depending on how the user is currently navigating the
timeline. When a pin is highlighted, as much information is shown to the user as
possible.

![timeline](/images/guides/design-and-interaction/timeline.png)

Users access the timeline view using Up and Down buttons from the watchface to
go into the past and future respectively. The navigation of the timeline also
uses animations for moving elements that gives life to the user interface, and
elements such as moving the pin icon between windows add cohesion between the
different screens. A press of the Select button opens the pin to display all the
information it contains, and is only one click away.

A further press of the Select button opens the pin's action menu, containing a
list of all the actions a user may take. These actions are directly related to
the pin, and can be specified when it is created. The system provides two
default actions: 'Remove' to remove the pin from the user's timeline, and 'Mute
[Name]' to mute all future pins from that source. This gives the user control
over which pins they see in their personal timeline. Mute actions can be
reversed later in the mobile app's 'Apps/Timeline' screen.

## What's Next?

Read {% guide_link design-and-interaction/recommended %} for tips on creating an
intuitive app experience.


---
# 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.

title: Example Implementations
description: |
  Resources and code samples to implement common design and UI patterns.
guide_group: design-and-interaction
menu: true
permalink: /guides/design-and-interaction/implementation/
generate_toc: true
order: 4
---

This guide contains resources and links to code examples that may help
developers implement UI designs and interaction patterns recommended in the
other guides in this section.

## UI Components and Patterns

Developers can make use of the many UI components available in SDK 3.x in
combination with the
{% guide_link design-and-interaction/recommended#common-design-styles "Common Design Styles" %}
to ensure the user experience is consistent and intuitive. The following
components and patterns are used in the Pebble experience, and listed in the
table below. Some are components available for developers to use in the SDK, or
are example implementations designed for adaptation and re-use.

| Pattern | Screenshot | Description |
|---------|------------|-------------|
| [`Menu Layer`](``MenuLayer``) | ![](/images/guides/design-and-interaction/menulayer.png) | Show many items in a list, allow scrolling between them, and choose an option. |
| [`Status Bar`](``StatusBarLayer``) | ![](/images/guides/design-and-interaction/alarm-list~basalt.png) | Display the time at the top of the Window, optionally extended with additional data. |
| [`Radio Button List`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/radio_button_window.c) | ![](/images/guides/design-and-interaction/radio-button.png) | Allow the user to specify one choice out of a list. |
| [`Checkbox List`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/checkbox_window.c) | ![](/images/guides/design-and-interaction/checkbox-list.png) | Allow the user to choose multiple different options from a list. |
| [`List Message`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/list_message_window.c) | ![](/images/guides/design-and-interaction/list-message.png) | Provide a hint to help the user choose from a list of options. |
| [`Message Dialog`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/dialog_message_window.c) | ![](/images/guides/design-and-interaction/dialog-message.gif) | Show an important message using a bold fullscreen alert. |
| [`Choice Dialog`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/dialog_choice_window.c) | ![](/images/guides/design-and-interaction/dialog-choice-patterns.png) | Present the user with an important choice, using the action bar and icons to speed up decision making. |
| [`PIN Entry`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/pin_window.c) | ![](/images/guides/design-and-interaction/pin.png) | Enable the user to input integer data. |
| [`Text Animation`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/text_animation_window.c) | ![](/images/guides/design-and-interaction/text-change-anim.gif) | Example animation to highlight a change in a text field. |
| [`Progress Bar`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/progress_bar_window.c) | ![](/images/guides/design-and-interaction/progress-bar.gif) | Example progress bar implementation on top of a ``StatusBarLayer``. |
| [`Progress Layer`]({{site.links.examples_org}}/ui-patterns/blob/master/src/windows/progress_layer_window.c) | ![](/images/guides/design-and-interaction/progresslayer.gif) | Example implementation of the system progress bar layer. |

## Example Apps

Developers can look at existing apps to begin to design (or improve) their user
interface and interaction design. Many of these apps can be found on the
appstore with links to their source code, and can be used as inspiration.

### Cards Example (Weather)

The weather [`cards-example`]({{site.links.examples_org}}/cards-example)
embodies the 'card' design pattern. Consisting of a single layout, it displays
all the crucial weather-related data in summary without the need for further
layers of navigation. Instead, the buttons are reserved for scrolling between
whole sets of data pertaining to different cities. The number of 'cards' is
shown in the top-right hand corner to let the user know that there is more data
present to be scrolled through, using the pre-existing Up and Down button action
affordances the user has already learned. This helps avoid implementing a novel
navigation pattern, which saves time for both the user and the developer.

![weather >{pebble-screenshot,pebble-screenshot--time-red}](/images/guides/design-and-interaction/weather.gif)

When the user presses the appropriate buttons to scroll through sets of data,
the changing information is animated with fast, snappy, and highly visible
animations to reinforce the idea of old data moving out of the layout and being
physically replaced by new data.

This is the latest version of the internal repository from Pebble Technology providing the software to run on Pebble watches. Proprietary source code has been removed from this repository and it will not compile as-is. This is for information only.


---
# 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.

title: Round App Design
description: |
  Tips and advice for designing apps that take advantage of the Pebble Time
  Round display
guide_group: design-and-interaction
menu: true
permalink: /guides/design-and-interaction/in-the-round/
generate_toc: true
order: 3
---

> This guide is about designing round apps. For advice on implementing a round
> design in code, read {% guide_link user-interfaces/round-app-ui %}.

With the release of the Chalk [platform](/faqs#pebble-sdk), developers must take
new features and limitations into account when designing their apps. New and
existing apps that successfully adapt their layout and colors for both Aplite
and Basalt should also endeavor to do so for the Chalk platform.

## Minor Margins

The Pebble Time Round display requires a small two pixel border on each edge, to
compensate for the bezel design. To this end, it is highly encouraged to allow
for this in an app's design. This may involve stretching a background color to
all outer edges, or making sure that readable information cannot be displayed in
this margin, or else it may not be visible.

Avoid thin rings around the edge of the display, even after accounting for the
two pixel margin as manufacturing variations may cause them to be visibly
off-center. Instead use thick rings, or inset them significantly from the edge
of the screen.

## Center of Attention

With the round Chalk display, apps no longer have the traditional constant
amount of horizontal space available. This particularly affects the use of the
``MenuLayer``. To compensate for this, menus are now always centered on the
highlighted item. Use this to display additional information in the cell with
the most space available, while showing reduced content previews in the
unhighlighted cells.

![centered >{pebble-screenshot,pebble-screenshot--time-round-silver-20}](/images/guides/design-and-interaction/center-layout~chalk.png)

Menus built using the standard cell drawing functions will automatically adopt
this behavior. If performing custom cell drawing, new APIs are available to
help implement this behavior. For more information, look at the ``Graphics``
documentation, as well as the ``menu_layer_set_center_focused()`` and
``menu_layer_is_index_selected()`` to help with conditional drawing.

## Pagination

Another key concept to bear in mind when designing for a round display is text
flow. In traditional Pebble apps, text in ``ScrollLayer`` or ``TextLayer``
elements could be freely moved and scrolled with per-pixel increments without
issue. However, with a round display each row of text can have a different
width, depending on its vertical position. If such text was reflowed while
moving smoothly down the window, the layout would reflow so often the text would
be very difficult to read.

![center-layout >{pebble-screenshot,pebble-screenshot--time-round-silver-20}](/images/guides/design-and-interaction/scrolling-with-text-flow.gif)

The solution to this problem is to scroll through text in pages, a technique
known as pagination. By moving through the text in discrete sections, the text
is only reflowed once per 'page', and remains easily readable as the user is
navigating through it. The ``ScrollLayer`` has been updated to implement this
on Chalk.

To inform the user that more content is available, the Chalk platform allows use
of the ``ContentIndicator`` UI component. This facilitates the display of two
arrows at the top and bottom of the display, similar to those seen in the
system UI.

![content-indicator >{pebble-screenshot,pebble-screenshot--time-round-silver-20}](/images/guides/design-and-interaction/content-indicator.png)

A ``ContentIndicator`` can be created from scratch and manually managed to
determine when the arrows should be shown, or a built-in instance can be
obtained from a ``ScrollLayer``.

## Platform-Specific Designs

Sometimes a design that made sense on a rectangular display does not make sense
on a circular one, or could be improved. Be open to creating a new UI for the
Chalk platform, and selecting which to use based on the display shape.

For example, in the screenshot below the linear track display was incompatible
with the round display and center-focused menus, leading to a completely
different design on Chalk that shows the same information.

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/caltrain-stops.png",
  "platforms": [
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

## What's Next?

Read {% guide_link design-and-interaction/implementation %} to learn how to use
and implement the UI components and patterns encouraged in SDK 3.x apps.


---
# 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.

title: Design and Interaction
description: |
  How to design apps to maximise engagement, satisfaction, efficiency and
  overall user experience.
guide_group: design-and-interaction
menu: false
permalink: /guides/design-and-interaction/
generate_toc: false
hide_comments: true
---

Interaction guides are intended to help developers design their
apps to maximize user experience through effective, consistent visual design and
user interactions on the Pebble platform. Readers can be non-programmers and
programmers alike: All material is explained conceptually and no code must be
understood. For code examples, see
{% guide_link design-and-interaction/implementation %}.

By designing apps using a commonly understood and easy to understand visual
language users can get the best experience with the minimum amount of effort
expended - learning how they work, how to operate them or what other behavior
is required. This can help boost how efficiently any given app is used as well
as help reinforce the underlying patterns for similar apps. For example, the
layout design should make it immediately obvious which part of the UI contains
the vital information the user should glance at first.

In addition to consistent visual design, implementing a common interaction
pattern helps an app respond to users as they would expect. This allows them to
correctly predict how an app will respond to their input without having to
experiment to find out.

To get a feel for how to approach good UI design for smaller devices, read other
examples of developer design guidelines such as Google's
[Material Design](http://www.google.com/design/spec/material-design/introduction.html)
page or Apple's
[iOS Human Interface Guidelines](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/).

## Contents

{% include guides/contents-group.md group=page.group_data %}


---
# 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.

title: One Click Actions
description: |
  Details about how to create One Click Action watchapps
guide_group: design-and-interaction
menu: true
order: 2
related_docs:
  - AppExitReason
  - AppGlanceSlice
  - AppMessage
related_examples:
  - title: One Click Action Example
    url: https://github.com/pebble-examples/one-click-action-example
---

One click actions are set to revolutionize the way users interact with their
Pebble by providing instant access to their favorite one click watchapps,
directly from the new system launcher. Want to unlock your front door?
Call an Uber? Or perhaps take an instant voice note? With one click actions,
the user is able to instantly perform a single action by launching an app, and
taking no further action.

![Lockitron >{pebble-screenshot,pebble-screenshot--time-black}](/images/guides/design-and-interaction/lockitron.png)

### The One Click Flow

It’s important to develop your one click application with a simple and elegant
flow. You need to simplify the process of your application by essentially
creating an application which serves a single purpose.

The typical flow for a one click application would be as follows:

1. Application is launched
2. Application performs action
3. Application displays status to user
4. Application automatically exits to watchface if the action was successful,
or displays status message and does not exit if the action failed

If we were creating an instant voice note watchapp, the flow could be as
follows:

1. Application launched
2. Application performs action (take a voice note)
  1. Start listening for dictation
  2. Accept dictation response
3. Application displays a success message
4. Exit to watchface

In the case of a one click application for something like Uber, we would need to
track the state of any existing booking to prevent ordering a second car. We
would also want to update the ``App Glance``
as the status of the booking changes.

1. Application launched
2. If a booking exists:
  1. Refresh booking status
  2. Update ``App Glance`` with new status
  3. Exit to watchface
3. Application performs action (create a booking)
  1. Update AppGlance: “Your Uber is on it’s way”
  2. Application displays a success message
  3. Exit to watchface

### Building a One Click Application

For this example, we’re going to build a one click watchapp which will lock or
unlock the front door of our virtual house. We’re going to use a virtual
[Lockitron](https://lockitron.com/), or a real one if you’re lucky enough to
have one.

Our flow will be incredibly simple:

1. Launch the application
2. Take an action (toggle the state of the lock)
3. Update the ``App Glance`` to indicate the new lock state
4. Display a success message
5. Exit to watchface

For the sake of simplicity in our example, we will not know if someone else has
locked or unlocked the door using a different application. You can investigate
the [Lockitron API](http://api.lockitron.com) if you want to develop this idea
further.

In order to control our Lockitron, we need the UUID of the lock and an access
key. You can generate your own virtual lockitron UUID and access code on the
[Lockitron website](https://api.lockitron.com/v1/getting_started/virtual_locks).

```c
#define LOCKITRON_LOCK_UUID "95c22a11-4c9e-4420-adf0-11f1b36575f2"
#define LOCKITRON_ACCESS_TOKEN "99e75a775fe737bb716caf88f161460bb623d283c3561c833480f0834335668b"
```

> Never publish your actual Lockitron access token in the appstore, unless you
want strangers unlocking your door! Ideally you would make these fields
configurable using [Clay for Pebble](https://github.com/pebble/clay).

We’re going to need a simple enum for the state of our lock, where 0 is
unlocked, 1 is locked and anything else is unknown.

```c
typedef enum {
  LOCKITRON_UNLOCKED,
  LOCKITRON_LOCKED,
  LOCKITRON_UNKNOWN
} LockitronLockState;
```

We’re also going to use a static variable to keep track of the state of our
lock.

```c
static LockitronLockState s_lockitron_state;
```

When our application launches, we’re going to initialize ``AppMessage`` and
then wait for PebbleKit JS to tell us it’s ready.

```c
static void prv_init(void) {
  app_message_register_inbox_received(prv_inbox_received_handler);
  app_message_open(256, 256);
  s_window = window_create();
  window_stack_push(s_window, false);
}

static void prv_inbox_received_handler(DictionaryIterator *iter, void *context) {
  Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_APP_READY);
  if (ready_tuple) {
    // PebbleKit JS is ready, toggle the Lockitron!
    prv_lockitron_toggle_state();
    return;
  }
  // ...
}
```

In order to toggle the state of the Lockitron, we’re going to send an
``AppMessage`` to PebbleKit JS, containing our UUID and our access key.

```c
static void prv_lockitron_toggle_state() {
  DictionaryIterator *out;
  AppMessageResult result = app_message_outbox_begin(&out);
  dict_write_cstring(out, MESSAGE_KEY_LOCK_UUID, LOCKITRON_LOCK_UUID);
  dict_write_cstring(out, MESSAGE_KEY_ACCESS_TOKEN, LOCKITRON_ACCESS_TOKEN);
  result = app_message_outbox_send();
}
```

PebbleKit JS will handle this request and make the relevant ajax request to the
Lockitron API. It will then return the current state of the lock and tell our
application to exit back to the default watchface using
``AppExitReason``. See the
[full example](https://github.com/pebble-examples/one-click-action-example) for
the actual Javascript implementation.

```c
static void prv_inbox_received_handler(DictionaryIterator *iter, void *context) {
  // ...
  Tuple *lock_state_tuple = dict_find(iter, MESSAGE_KEY_LOCK_STATE);
  if (lock_state_tuple) {
    // Lockitron state has changed
    s_lockitron_state = (LockitronLockState)lock_state_tuple->value->int32;
    // App will exit to default watchface
    app_exit_reason_set(APP_EXIT_ACTION_PERFORMED_SUCCESSFULLY);
    // Exit the application by unloading the only window
    window_stack_remove(s_window, false);
  }
}
```

Before our application terminates, we need to update the
``App Glance`` with the current state
of our lock. We do this by passing our current lock state into the
``app_glance_reload`` method.

```c
static void prv_deinit(void) {
  window_destroy(s_window);
  // Before the application terminates, setup the AppGlance
  app_glance_reload(prv_update_app_glance, &s_lockitron_state);
}
```

We only need a single ``AppGlanceSlice`` for our ``App Glance``, but it’s worth
noting you can have multiple slices with varying expiration times.

```c
static void prv_update_app_glance(AppGlanceReloadSession *session, size_t limit, void *context) {
  // Check we haven't exceeded system limit of AppGlances
  if (limit < 1) return;

  // Retrieve the current Lockitron state from context
  LockitronLockState *lockitron_state = context;

  // Generate a friendly message for the current Lockitron state
  char *str = prv_lockitron_status_message(lockitron_state);
  APP_LOG(APP_LOG_LEVEL_INFO, "STATE: %s", str);

  // Create the AppGlanceSlice (no icon, no expiry)
  const AppGlanceSlice entry = (AppGlanceSlice) {
    .layout = {
      .template_string = str
    },
    .expiration_time = time(NULL)+3600
  };

  // Add the slice, and check the result
  const AppGlanceResult result = app_glance_add_slice(session, entry);
  if (result != APP_GLANCE_RESULT_SUCCESS) {
    APP_LOG(APP_LOG_LEVEL_ERROR, "AppGlance Error: %d", result);
  }
}
```

### Handling Launch Reasons

In the example above, we successfully created an application that will
automatically execute our One Click Action when the application is launched.
But we also need to be aware of some additional launch reasons where it would
not be appropriate to perform the action.

By using the ``launch_reason()`` method, we can detect why our application was
started and prevent the One Click Action from firing unnecessarily.

A common example, would be to detect if the application was actually started by
the user, from either the launcher, or quick launch.

```c
  if(launch_reason() == APP_LAUNCH_USER || launch_reason() == APP_LAUNCH_QUICK_LAUNCH) {
    // Perform One Click
  } else {
    // Display a message
  }
```

### Conclusion

As you can see, it’s a relatively small amount of code to create one click
watchapps and we hope this inspires you to build your own!

We recommend that you check out the complete
[Lockitron sample](https://github.com/pebble-examples/one-click-action-example)
application and also the ``App Glance`` and ``AppExitReason`` guides for further
information.


---
# 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.

title: Recommended Guidelines and Patterns
description: |
  Pebble's recommended guidelines for creating awesome app experiences.
guide_group: design-and-interaction
menu: true
permalink: /guides/design-and-interaction/recommended/
generate_toc: true
order: 2
---

This page contains recommendations for things to consider when designing an
app's visual styles and interaction patterns. The aim here is to encourage
efficiency through optional conformity to common ideas and concepts, and to
breed a consistent experience for users across apps. Developers can also find
suggested interface styles to use when building the navigation of apps to best
display the information contained within.

## Tips for UI Design

To achieve an effective, clear, and intuitive design developers should:

* Keep layouts **simple**, with only as much information displayed as is
  **immediately required**. This encourages quick usage of the app, distracting
  the user for the minimal amount of time necessary for the app to achieve its
  goals.

* Give priority to the **most commonly used/main purpose** functionality of the
  app in the UI. Make it easy to use the biggest features, but still easy to
  find additional functionality that would otherwise be hidden.

* Use **larger fonts** to highlight the **most important data** to be read at a
  glance. Consider font size 28 for larger items, and a minimum of 18 for
  smaller ones.

* Take advantage of colors to convey additional **information without any text**
  if they are already associated as such, such as green for a task complete.

* Try to avoid any colors used in places they may have a **pre-conceived
  meaning** which does not apply, such as red text when there are no errors.

* Use animations to embody the layout with **character** and flourish, as well
  as to **draw the eye** to updated or changing information.

* Ensure the UI gives **direct feedback** to the user's input, or else they may
  think their button presses are having no effect.

## Tips for UI Interaction

* Avoid using the Pebble buttons for actions **not** already associated with
  them, unless clearly marked in the UI using an ``ActionBarLayer`` or similar
  method. When using the Up and Down buttons for 'previous item' and 'next item'
  respectively, there may be no need for a visual cue.

* Use iconography with **pre-existing visual associations** (such as a 'stop'
  icon) to take advantage of the inherent behavior the user will have when
  seeing it. This can avoid the need for explicit instructions to train the user
  for an app's special case.

* Ensure the navigation between app ``Window``s is **logical** with the input
  given and the information displayed. For example, an app showing bus
  timetables should use higher level screens for quickly navigating to a
  particular area/station, with lower level views reserved for scrolling through
  the actual timetable data.

* If possible, **preserve the state of the app** and/or previous navigation if
  the app is commonly used for a repetitive task. In the bus timetable example,
  the user may only look up two stations for a to-from work journey. Learn this
  behavior and store it with the [Persistent Storage API](``Storage``) to
  intelligently adapt the UI on subsequent launches to show the relevant
  information. This helps the user avoid navigating through multiple menus every
  time they launch the app.

## Common Design Styles

The following are common design styles that have been successfully used in
system and 3rd party apps, and are recommended for use in the correct manner.

### Display Data Sets Using Cards

![card >{pebble-screenshot,pebble-screenshot--time-red}](/images/guides/design-and-interaction/card.gif)

The 'card' style aims to reduce the number of menu levels needed to access as
much relevant information as possible. Instead of a menu to select a data set
leading to a menu to explore each item in that set, a single ``Window`` is
designed that displays an entire data set. This view then uses the Pebble Up and
Down buttons to scroll through complete data sets in an array of many sets.

An example of this is the
{% guide_link design-and-interaction/implementation#cards-example-weather "cards-example" %}
example app, which displays all weather data in a single view and pages through
sets of data for separate locations with the Up and Down buttons. This style of
UI design allows access to lots of information without navigating through
several menus to view it.

### List Options with a Menu

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/list.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

The style is one of the most basic, tried and true styles. Using the
``MenuLayer`` UI component, the user may choose between multiple app functions
by scrolling with the Up and Down buttons, an interaction pattern afforded to
the developer by the core system experience. Using a menu, a user can navigate
straight to the part of the app or specific action they want.

### Execute Actions with an ActionBarLayer

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/actionbar.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

The ``ActionBarLayer`` allows easy association of app functionality with the
Pebble buttons. By setting icons to each of the three positions, a user can see
which actions they can perform at a glance and execture them with a single
button press. When pressed, the icon is animated to provide immediate visual
feedback.

An example of this is the system Music app, that uses the ``ActionBarLayer`` to
inform the user that the Up and Down buttons skip tracks. In this case, the
Select button is displayed with elipses, indicating further actions are
available. A press of this button changes the set of actions on the Up and Down
buttons, enabling them to modify the playback volume instead.

A collection of icons for common actions is available for use by developers, and
can be found in the {% guide_link app-resources/app-assets %} guide.

### Allow Extended Options with an Action Menu

![actionmenu](/images/guides/design-and-interaction/actionmenu.png)

If an app screen demands a larger range of available actions than the
``ActionBarLayer`` will allow, present these as a list that slides into
view with a press of the Select button using an action menu. This menu contains
all the available options, and can contain multiple sub-menus, providing levels.
The user can keep track of which level they are currently looking at using the
breadcrumb dots on the left-hand side of the screen when the action menu is
displayed.

Once an action has been chosen, the user should be informed of the success or
failure of their choice using a new alert dialog window. In the system action
menus, these screens use an eye-catching animation and bold label to convey the
result of the action. This feedback is important to prevent the user from
getting frustrated if they perceive their input has no result, as well as to
reassure them that their action has succeeded without a problem.

### Get User Input with a Form

![list](/images/guides/design-and-interaction/alarm-list-config.png)

Apps such as the system Alarm app make use of a list of configurable items, with
each active alarm treated as a menu item with properties. The status of each
item is displayed in a menu, with the Select button initiating configuration of
that item.

When an item is being configured, the data requried to create the item should be
obtained from the user through the use of a form, with manipulable elements. In
the Alarms example, each integer required to schedule an alarm is obtained with
a number field that can have its value incrememted or decremented using the
intuitive Up and Down buttons. The current form element is highlighted with
color, and advancing to the next element is done with the Select button,
doubling as a 'Confirm' action when the end of the form is reached.

### Prompting User Action on the Phone

In some applications, user input is required in the app's configuration page (as
detailed in {% guide_link user-interfaces/app-configuration %}) before the app
can perform its task. An example of this is a feed reader app, that will need
the user to input the URL of their preferred news feed before it can fetch the
feed content. In this case, the watchapp should display a prominent (full-
screen if possible) dialog telling the user that input to the phone app for
configuration is required.

![action-required >{pebble-screenshot,pebble-screenshot--time-red}](/images/guides/design-and-interaction/action-required.png)

Once the user has performed the required action on the phone, the
[`webviewclosed`](/guides/communication/using-pebblekit-js/)
event should signify that the app can proceed, and that the required data is now
available.

It should not be the case that this action is required every time the app is
started. In most cases, the input data from the user can be stored with
[Peristent Storage](``Storage``) on the watch, or
[`localStorage`](/guides/communication/using-pebblekit-js/)
on the phone. If the app must get input on every launch (such as a mode
selection), this should be done through a form or menu on the watch, so as to
avoid needing to use the phone.

### Show Time and Other Data with the Status Bar

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/alarm-list.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

Under SDK 2.x, the status bar was displayed to users in all menus and watchapps
except watchfaces, or where the developer had explicitly disabled it. This was
useful for showing the time and battery level, but arguably not essential all
the time.

In SDK 3.x, only apps that are designed to be running for extended periods of
time (such as Music and the Sports API app) show the time, using the
``StatusBarLayer`` UI component. The battery level can easily be seen from the
Settings appface, and so it not necessary to be always visible. Another instance
where the status bar is neccessary is in the Alarms app (shown above), where the
user may need to compare with the current time when setting an alarm.

If a constant, minimalistic display of app data is required, the
``StatusBarLayer`` can be used to perform this task. It provides a choice of
separator mode and foreground/background colors, and can also be made
transparent. Since is it just another ``Layer``, it can be easily extended with
additional text, icons, or other data.

For example, the
[`cards-example`]({{site.links.examples_org}}/cards-example) app uses an
extention of the status bar to display the currently selected 'card' (a set of
displayed data). Another example is the progress bar component example from the
[`ui-patterns`]({{site.links.examples_org}}/ui-patterns) app, which
builds upon the dotted separator mode to become a thin progress bar.

When used in conjunction with the ``ActionBarLayer`` (for example, in the Music
system app), the width of the underlying layer should be adjusted such that the
time displayed is shown in the new center of the app area (excluding that taken
up by the action bar itself).

### Show Alerts and Get Decisions with Modal Windows

![dialog-message >{pebble-screenshot,pebble-screenshot--time-red}](/images/guides/design-and-interaction/dialog-message.gif)

When a significant event occurs while using an app, it should be made visible to
the user through the use of a full-screen model dialog. In a similar way that
notifications and reminders alert the user to events, these layouts consist of
only the important information and an associated icon telling the user the
source of the alert, or the reason for its occurrence. This pattern should also
be used to quickly and efficently tell the user that an app-related error has
occured, including steps on how to fix any potential problems.

These alerts can also take the form of requests for important decisions to be
made by the user, such as to remember a choice as the default preference:

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/dialog-choice-window.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

In this way the decision can be passed to the user with an immediately obvious
and actionable set of choices. One the choice has been made, the modal window is
dismissed, and a confirmation of the choice displayed. The user should then be
returned to the previous window to resume their use of the app where they left
off.

### Using Vibrations and Haptic Feedback

The Pebble SDK allows the use of the vibration motor to deliver haptic feedback
to the user. This can take the form of short, long, double pulses or more
detailed vibration sequences, allowing a lot of customization as well as
variation between apps.

To encourage a consistent experience for users, the ``Vibes`` API should be used
with the following points in mind:

* A short pulse should be used to alert the user to the end of a long-running
  in-app event, such as a download completing, preferably when they are not
  looking at the watch.

* A long pulse should be used to alert the user to a failure or error that
  requires attention and some interaction.

* Custom vibration patterns should be used to allow the user to customize haptic
  feedback for different events inside the app.

When the app is open and being actively interacted with no vibration or haptic
feedback should be neccessary on top of the existing visual feedback. However,
some exceptions may occur, such as for visually-impaired users. In these cases
haptic feedback may be very useful in boosting app accessibility.

### Handling Connection Problems

When a watchapp is running, there is no guarantee that the phone connection will
be available at any one time. Most apps will function without this connection,
but if PebbleKit JS, Android, or iOS is required, the user must be informed of
the reason for failure so that they can correct the problem. This check can be
performed at any time using ``connection_service_peek_pebble_app_connection()``.

An example alert layout is shown below.

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/no-bt-connection.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

A similar situation arises if an app that requires information or responses from
a remote web service attempts to do so, but the phone has no Internet
connection. This may be because the user has opted to disable their data
connections, or they may be out of range.

Another example alert layout is shown below for this situation.

{% screenshot_viewer %}
{
  "image": "/images/guides/design-and-interaction/no-inet-connection.png",
  "platforms": [
    {"hw": "aplite", "wrapper": "steel-black"},
    {"hw": "basalt", "wrapper": "time-red"},
    {"hw": "chalk", "wrapper": "time-round-rosegold-14"}
  ]
}
{% endscreenshot_viewer %}

If these kinds of situations can cause problems for the operation of the app,
consider using the [`Persistent Storage`](``Storage``) API to cache the most
recently loaded data (such as weather, sports scores or news items) from the
last successful launch, and display this to the user (while making them aware of
the data's age) until new data can be obtained.

### Hiding Timeline-only Apps

Watchapps that consist only of a {% guide_link pebble-timeline "Pebble timeline" %}
experience will only need to be launched when configured by the user to select
topic subscriptions. In these cases, developers should hide their app from the
launcher menu to prevent the user needlessly launching it.

To find out how to do this using the `hiddenApp` property, see
{% guide_link tools-and-resources/app-metadata %}.

## Consistent App Configuration

Watchapps and watchfaces that include user configuration normally include a web
page hosted by the app developer, allowing the user to choose from a set of
options and apply them to the app. Such options include aesthetic options such
as color schemes, larger font sizes, replacement images, data source choices,
and others. Traditionally the design of these pages has been left entirely to
the developer on a per-app basis, and this is reflected in the resulting design
consistency.

Read {% guide_link user-interfaces/app-configuration %} to learn more about
configuration page design and implementation.

## What's Next?

Read {% guide_link design-and-interaction/in-the-round %} to read tips and
guidance on designing apps that work well on a round display.


---
# 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.

title: Accelerometer
description: |
  How to use data and simple tap gestures from the onboard accelerometer.
guide_group: events-and-services
order: 0
related_docs:
  - AccelerometerService
related_examples:
  - title: Feature Accel Discs
    url: https://github.com/pebble-examples/feature-accel-discs
---

The acceleromter sensor is included in every Pebble watch, and allows collection
of acceleration and orientation data in watchapps and watchfaces. Data is
available in two ways, each suitable to different types of watchapp:

* Taps events - Fires an event whenever a significant tap or shake of the watch
  occurs. Useful to 'shake to view' features.

* Data batches - Allows collection of data in batches at specific intervals.
  Useful for general accelerometer data colleciton.

As a significant source of regular callbacks, the accelerometer should be used
as sparingly as possible to allow the watch to sleep and conserve power. For
example, receiving data in batches once per second is more power efficient than
receiving a single sample 25 times per second.

## About the Pebble Accelerometer

The Pebble accelerometer is oriented according to the diagram below, showing the
direction of each of the x, y, and z axes.

![accel-axes](/images/guides/pebble-apps/sensors/accel.png)

In the API, each axis value contained in an ``AccelData`` sample is measured in
milli-Gs. The accelerometer is calibrated to measure a maximum acceleration of
±4G. Therefore, the range of possible values for each axis is -4000 to +4000.

The ``AccelData`` sample object also contains a `did_vibrate` field, set to
`true` if the vibration motor was active during the sample collection. This
could possibly contaminate those samples due to onboard vibration, so they
should be discarded. Lastly, the `timestamp` field allows tracking of obtained
accelerometer data over time.

## Using Taps

Adding a subscription to tap events allows a developer to react to any time the
watch is tapped or experiences a shake along one of three axes. Tap events are
received by registering an ``AccelTapHandler`` function, such as the one below:

```c
static void accel_tap_handler(AccelAxisType axis, int32_t direction) {
  // A tap event occured

}
```

The `axis` parameter describes which axis the tap was detected along. The
`direction` parameter is set to `1` for the positive direction, and `-1` for the
negative direction.

A subscription can be added or removed at any time. While subscribed,
`accel_tap_handler` will be called whenever a tap event is fired by the
accelerometer. Adding a subscription is simple:

```c
// Subscribe to tap events
accel_tap_service_subscribe(accel_tap_handler);
```

```c
// Unsubscribe from tap events
accel_tap_service_unsubscribe();
```

## Using Data Batches

Accelerometer data can be received in batches at a chosen sampling rate by
subscribing to the Accelerometer Data Service at any time:

```c
uint32_t num_samples = 3;  // Number of samples per batch/callback

// Subscribe to batched data events
accel_data_service_subscribe(num_samples, accel_data_handler);
```

The ``AccelDataHandler`` function (called `accel_data_handler` in the example
above) is called when a new batch of data is ready for consumption by the
watchapp. The rate at which these occur is dictated by two things:

* The sampling rate - The number of samples the accelerometer device measures
  per second. One value chosen from the ``AccelSamplingRate`` `enum`.

* The number of samples per batch.

Some simple math will determine how often the callback will occur. For example,
at the ``ACCEL_SAMPLING_50HZ`` sampling rate, and specifying 10 samples per
batch will result in five calls per second.

When an event occurs, the acceleromater data can be read from the ``AccelData``
pointer provided in the callback. An example reading the first set of values is
shown below:

```c
static void accel_data_handler(AccelData *data, uint32_t num_samples) {
  // Read sample 0's x, y, and z values
  int16_t x = data[0].x;
  int16_t y = data[0].y;
  int16_t z = data[0].z;

  // Determine if the sample occured during vibration, and when it occured
  bool did_vibrate = data[0].did_vibrate;
  uint64_t timestamp = data[0].timestamp;

  if(!did_vibrate) {
    // Print it out
    APP_LOG(APP_LOG_LEVEL_INFO, "t: %llu, x: %d, y: %d, z: %d",
                                                          timestamp, x, y, z);
  } else {
    // Discard with a warning
    APP_LOG(APP_LOG_LEVEL_WARNING, "Vibration occured during collection");
  }
}
```

The code above will output the first sample in each batch to app logs, which
will look similar to the following:

```nc|text
[15:33:18] -data-service.c:21> t: 1449012797098, x: -111, y: -280, z: -1153
[15:33:18] -data-service.c:21> t: 1449012797305, x: -77, y: 40, z: -1014
[15:33:18] -data-service.c:21> t: 1449012797507, x: -60, y: 4, z: -1080
[15:33:19] -data-service.c:21> t: 1449012797710, x: -119, y: -55, z: -921
[15:33:19] -data-service.c:21> t: 1449012797914, x: 628, y: 64, z: -506
```


---
# 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.

title: Background Worker
description: |
  Using the Background Worker to do work in the background, such as activity
  tracking.
guide_group: events-and-services
order: 1
related_docs:
  - Worker
related_examples:
  - title: Background Counter
    url: https://github.com/pebble-examples/feature-background-counter
  - title: Background Worker Communication
    url: https://github.com/pebble-examples/feature-worker-message
platform_choice: true
---

In addition to the main foreground task that every Pebble app implements, a
second background worker task can also be created. This worker is capable of
running even when the foreground task is closed, and is useful for tasks that
must continue for long periods of time. For example, apps that log sensor data.

There are several important points to note about the capabilities of this
worker when compared to those of the foreground task:

* The worker is constrained to 10.5 kB of memory.

* Some APIs are not available to the worker. See the
  [*Available APIs*](#available-apis) section below for more information.

* There can only be one background worker active at a time. In the event that a
  second one attempts to launch from another watchapp, the user will be asked to
  choose whether the new worker can replace the existing one.

* The user can determine which app's worker is running by checking the
  'Background App' section of the Settings menu. Workers can also be launched
  from there.

* The worker can launch the foreground app using ``worker_launch_app()``. This
  means that the foreground app should be prepared to be launched at any time
  that the worker is running.

> Note: This API should not be used to build background timers; use the
> ``Wakeup`` API instead.

## Adding a Worker

^CP^ The background worker's behavior is determined by code written in a
separate C file to the foreground app. Add a new source file and set the
'Target' field to 'Background Worker'.

^LC^ The background worker's behavior is determined by code written in a
separate C file to the foreground app, created in the `/worker_src` project
directory.

<div class="platform-specific" data-sdk-platform="local">
{% markdown %}
This project structure can also be generated using the
[`pebble` tool](/guides/tools-and-resources/pebble-tool/) with the `--worker`
flag as shown below:

```bash
$ pebble new-project --worker project_name
```
{% endmarkdown %}
</div>

The worker C file itself has a basic structure similar to a regular Pebble app,
but with a couple of minor changes, as shown below:

```c
#include <pebble_worker.h>

static void prv_init() {
  // Initialize the worker here
}

static void prv_deinit() {
  // Deinitialize the worker here
}

int main(void) {
  prv_init();
  worker_event_loop();
  prv_deinit();
}
```

## Launching the Worker

To launch the worker from the foreground app, use ``app_worker_launch()``:

```c
// Launch the background worker
AppWorkerResult result = app_worker_launch();
```

The ``AppWorkerResult`` returned will indicate any errors encountered as a
result of attempting to launch the worker. Possible result values include:

| Result | Value | Description |
|--------|-------|:------------|
| ``APP_WORKER_RESULT_SUCCESS`` | `0` | The worker launch was successful, but may not start running immediately. Use ``app_worker_is_running()`` to determine when the worker has started running. |
| ``APP_WORKER_RESULT_NO_WORKER`` | `1` | No worker found for the current app. |
| ``APP_WORKER_RESULT_ALREADY_RUNNING`` | `4` | The worker is already running. |
| ``APP_WORKER_RESULT_ASKING_CONFIRMATION`` | `5` | The user will be asked for confirmation. To determine whether the worker was given permission to launch, use ``app_worker_is_running()`` for a short period after receiving this result. |

## Communicating Between Tasks

There are three methods of passing data between the foreground and background
worker tasks:

* Save the data using the ``Storage`` API, then read it in the other task.

* Send the data to a companion phone app using the ``DataLogging`` API. Details
  on how to do this are available in {% guide_link communication/datalogging %}.

* Pass the data directly while the other task is running, using an
  ``AppWorkerMessage``. These messages can be sent bi-directionally by creating
  an `AppWorkerMessageHandler` in each task. The handler will fire in both the
  foreground and the background tasks, so you must identify the source
  of the message using the `type` parameter.

    ```c
    // Used to identify the source of a message
    #define SOURCE_FOREGROUND 0
    #define SOURCE_BACKGROUND 1
    ```

    **Foreground App**

    ```c
    static int s_some_value = 1;
    static int s_another_value = 2;

    static void worker_message_handler(uint16_t type,
                                        AppWorkerMessage *message) {
      if(type == SOURCE_BACKGROUND) {
        // Get the data, only if it was sent from the background
        s_some_value = message->data0;
        s_another_value = message->data1;
      }
    }

    // Subscribe to get AppWorkerMessages
    app_worker_message_subscribe(worker_message_handler);

    // Construct a message to send
    AppWorkerMessage message = {
      .data0 = s_some_value,
      .data1 = s_another_value
    };

    // Send the data to the background app
    app_worker_send_message(SOURCE_FOREGROUND, &message);

    ```

    **Worker**

    ```c
    static int s_some_value = 3;
    static int s_another_value = 4;

    // Construct a message to send
    AppWorkerMessage message = {
      .data0 = s_some_value,
      .data1 = s_another_value
    };

    static void worker_message_handler(uint16_t type,
                                        AppWorkerMessage *message) {
      if(type == SOURCE_FOREGROUND) {
        // Get the data, if it was sent from the foreground
        s_some_value = message->data0;
        s_another_value = message->data1;
      }
    }

    // Subscribe to get AppWorkerMessages
    app_worker_message_subscribe(worker_message_handler);

    // Send the data to the foreground app
    app_worker_send_message(SOURCE_BACKGROUND, &message);
    ```

## Managing the Worker

The current running state of the background worker can be determined using the
``app_worker_is_running()`` function:

```c
// Check to see if the worker is currently active
bool running = app_worker_is_running();
```

The user can tell whether the worker is running by checking the system
'Background App' settings. Any installed workers with be listed there.

The worker can be stopped using ``app_worker_kill()``:

```c
// Stop the background worker
AppWorkerResult result = app_worker_kill();
```

Possible `result` values when attempting to kill the worker are as follows:

| Result | Value | Description |
|--------|-------|:------------|
| ``APP_WORKER_RESULT_SUCCESS`` | `0` | The worker launch was killed successfully. |
| ``APP_WORKER_RESULT_DIFFERENT_APP`` | `2` | A worker from a different app is running, and cannot be killed by this app. |
| ``APP_WORKER_RESULT_NOT_RUNNING`` | `3` | The worker is not currently running. |

## Available APIs

Background workers do not have access to the UI APIs. They also cannot use the
``AppMessage`` API or load resources. Most other APIs are available including
(but not limited to) ``AccelerometerService``, ``CompassService``,
``DataLogging``, ``HealthService``, ``ConnectionService``,
``BatteryStateService``, ``TickTimerService`` and ``Storage``.

^LC^ The compiler will throw an error if the developer attempts to use an API
unsupported by the worker. For a definitive list of available APIs, check
`pebble_worker.h` in the SDK bundle for the presence of the desired API.

^CP^ CloudPebble users will be notified by the editor and compiler if they
attempt to use an unavailable API.


---
# 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.

title: Buttons
description: |
  How to react to button presses in your app.
guide_group: events-and-services
order: 2
related_docs:
  - Clicks
  - ClickHandler
related_examples:
  - title: App Font Browser
    url: https://github.com/pebble-examples/app-font-browser/blob/master/src/app_font_browser.c#L168
---

Button [`Clicks`](``Clicks``) are the primary input method on Pebble. All Pebble
watches come with the same buttons available, shown in the diagram below for
Pebble Time:

![button-layout](/images/guides/sensors-and-input/button-layout.png)

These buttons are used in a logical fashion throughout the system:

* Back - Navigates back one ``Window`` until the watchface is reached.

* Up - Navigates to the previous item in a list, or opens the past timeline when
  pressed from the watchface.

* Select - Opens the app launcher from the watchface, accepts a selected option
  or list item, or launches the next ``Window``.

* Down - Navigates to the next item in a list, or opens the future timeline when
  pressed from the watchface.

Developers are highly encouraged to follow these patterns when using button
clicks in their watchapps, since users will already have an idea of what each
button will do to a reasonable degree, thus avoiding the need for lengthy usage
instructions for each app. Watchapps that wish to use each button for a specific
action should use the ``ActionBarLayer`` or ``ActionMenu`` to give hints about
what each button will do.

## Listening for Button Clicks

Button clicks are received via a subscription to one of the types of button
click events listed below. Each ``Window`` that wishes to receive button click
events must provide a ``ClickConfigProvider`` that performs these subscriptions.

The first step is to create the ``ClickConfigProvider`` function:

```c
static void click_config_provider(void *context) {
  // Subcribe to button click events here

}
```

The second step is to register the ``ClickConfigProvider`` with the current
``Window``, typically after ``window_create()``:

```c
// Use this provider to add button click subscriptions
window_set_click_config_provider(window, click_config_provider);
```

The final step is to write a ``ClickHandler`` function for each different type
of event subscription required by the watchapp. An example for a single click
event is shown below:

```c
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
  // A single click has just occured

}
```

## Types of Click Events

There are five types of button click events that apps subscribe to, enabling
virtually any combination of up/down/click events to be utilized in a watchapp.
The usage of each of these is explained below:

### Single Clicks

Most apps will use this type of click event, which occurs whenever the button
specified is pressed and then immediately released. Use
``window_single_click_subscribe()`` from a ``ClickConfigProvider`` function,
supplying the ``ButtonId`` value for the chosen button and the name of the
``ClickHandler`` that will receive the events:

```c
static void click_config_provider(void *context) {
  ButtonId id = BUTTON_ID_SELECT;  // The Select button

  window_single_click_subscribe(id, select_click_handler);
}
```

### Single Repeating Clicks

Similar to the single click event, the single repeating click event allows
repeating events to be received at a specific interval if the chosen button
is held down for a longer period of time. This makes the task of scrolling
through many list items or incrementing a value significantly easier for the
user, and uses fewer button clicks.

```c
static void click_config_provider(void *context) {
  ButtonId id = BUTTON_ID_DOWN;       // The Down button
  uint16_t repeat_interval_ms = 200;  // Fire every 200 ms while held down

  window_single_repeating_click_subscribe(id, repeat_interval_ms,
                                                down_repeating_click_handler);
}
```

After an initial press (but not release) of the button `id` subscribed to, the
callback will be called repeatedly with an interval of `repeat_interval_ms`
until it is then released.

Developers can determine if the button is still held down after the first
callback by using ``click_recognizer_is_repeating()``, as well as get the number
of callbacks counted so far with ``click_number_of_clicks_counted()``:

```c
static void down_repeating_click_handler(ClickRecognizerRef recognizer,
                                                              void *context) {
  // Is the button still held down?
  bool is_repeating = click_recognizer_is_repeating(recognizer);

  // How many callbacks have been recorded so far?
  uint8_t click_count = click_number_of_clicks_counted(recognizer);
}
```

> Single click and single repeating click subscriptions conflict, and cannot be
> registered for the same button.

### Multiple Clicks

A multi click event will call the ``ClickHandler`` after a specified number of
single clicks has been recorded. A good example of usage is to detect a double
or triple click gesture:

```c
static void click_config_provider(void *context) {
  ButtonId id = BUTTON_ID_SELECT;  // The Select button
  uint8_t min_clicks = 2;          // Fire after at least two clicks
  uint8_t max_clicks = 3;          // Don't fire after three clicks
  uint16_t timeout = 300;          // Wait 300ms before firing
  bool last_click_only = true;     // Fire only after the last click

  window_multi_click_subscribe(id, min_clicks, max_clicks, timeout,
                                 last_click_only, multi_select_click_handler);
}
```

Similar to the single repeating click event, the ``ClickRecognizerRef`` can be
used to determine how many clicks triggered this multi click event using
``click_number_of_clicks_counted()``.

### Long Clicks

A long click event is fired after a button is held down for the specified amount
of time. The event also allows two ``ClickHandler``s to be registered - one for
when the button is pressed, and another for when the button is released. Only
one of these is required.

```c
static void click_config_provider(void *context) {
  ButtonId id = BUTTON_ID_SELECT;  // The select button
  uint16_t delay_ms = 500;         // Minimum time pressed to fire

  window_long_click_subscribe(id, delay_ms, long_down_click_handler,
                                                       long_up_click_handler);
}
```

### Raw Clicks

The last type of button click subcsription is used to track raw button click
events. Like the long click event, two ``ClickHandler``s may be supplied to
receive each of the pressed and depressed events.

```c
static void click_config_provider(void *context) {
  ButtonId id = BUTTON_ID_SELECT;  // The select button

  window_raw_click_subscribe(id, raw_down_click_handler, raw_up_click_handler,
                                                                        NULL);
}
```

> The last parameter is an optional pointer to a context object to be passed to
> the callback, and is set to `NULL` if not used.


---
# 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.

title: Compass
description: |
  How to use data from the Compass API to determine direction.
guide_group: events-and-services
order: 3
related_docs:
  - CompassService
related_examples:
  - title: Feature Compass
    url: https://github.com/pebble-examples/feature-compass
  - title: Official Compass Watchapp
    url: https://github.com/pebble-hacks/pebble-compass
---

The ``CompassService`` combines data from Pebble's accelerometer and
magnetometer to automatically calibrate the compass and produce a
``CompassHeading``, containing an angle measured relative to magnetic north.

The compass service provides magnetic north and information about its status
and accuracy through the ``CompassHeadingData`` structure.

## Calibration

The compass service requires an initial calibration before it can return
accurate results. Calibration is performed automatically by the system when
first required. The [`compass_status`](``CompassHeadingData``) field indicates
whether the compass service is calibrating. To help the calibration process, the
app should show a message to the user asking them to move their wrists in
different directions.

Refer to the [compass example]({{site.links.examples_org}}/feature-compass) for
an example of how to implement this screen.

## Magnetic North and True North

Depending on the user's location on Earth, the measured heading towards magnetic
north and true north can significantly differ. This is due to magnetic
variation, also known as 'declination'.

Pebble does not automatically correct the magnetic heading to return a true
heading, but the API is designed so that this feature can be added in the future
and the app will be able to automatically take advantage of it.

For a more precise heading, use the `magnetic_heading` field of
``CompassHeadingData`` and use a webservice to retrieve the declination at the
user's current location. Otherwise, use the `true_heading` field. This field
will contain the `magnetic_heading` if declination is not available, or the true
heading if declination is available. The field `is_declination_valid` will be
true when declination is available. Use this information to tell the user
whether the app is showing magnetic north or true north.

![Declination illustrated](/images/guides/pebble-apps/sensors/declination.gif)

> To see the true extent of declination, see how declination has
> [changed over time](http://maps.ngdc.noaa.gov/viewers/historical_declination/).

## Battery Considerations

Using the compass will turn on both Pebble's magnetometer and accelerometer.
Those two devices will have a slight impact on battery life. A much more
significant battery impact will be caused by redrawing the screen too often or
performing CPU-intensive work every time the compass heading is updated.

Use ``compass_service_subscribe()`` if the app only needs to update its UI when
new compass data is available, or else use ``compass_service_peek()`` if this
happens much less frequently.

## Defining "Up" on Pebble

Compass readings are always relative to the current orientation of Pebble. Using
the accelerometer, the compass service figures out which direction the user is
facing.

![Compass Orientation](/images/guides/pebble-apps/sensors/compass-orientation.png)

The best orientation to encourage users to adopt while using a compass-enabled
watchapp is with the top of the watch parallel to the ground. If the watch is
raised so that the screen is facing the user, the plane will now be
perpedicular to the screen, but still parallel to the ground.

## Angles and Degrees

The magnetic heading value is presented as a number between 0 and
TRIG_MAX_ANGLE (65536). This range is used to give a higher level of
precision for drawing commands, which is preferable to using only 360 degrees.

If you imagine an analogue clock face on your Pebble, the angle 0 is always at
the 12 o'clock position, and the magnetic heading angle is calculated in a
counter clockwise direction from 0.

This can be confusing to grasp at first, as it’s opposite of how direction is
measured on a compass, but it's simple to convert the values into a clockwise
direction:

```c
int clockwise_angle = TRIG_MAX_ANGLE - heading_data.magnetic_heading;
```

Once you have an angle relative to North, you can convert that to degrees using
the helper function `TRIGANGLE_TO_DEG()`:

```c
int degrees = TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE - heading_data.magnetic_heading);
```

## Subscribing to Compass Data

Compass heading events can be received in a watchapp by subscribing to the
``CompassService``:

```c
// Subscribe to compass heading updates
compass_service_subscribe(compass_heading_handler);
```

The provided ``CompassHeadingHandler`` function (called
`compass_heading_handler` above) can be used to read the state of the compass,
and the current heading if it is available. This value is given in the range of
`0` to ``TRIG_MAX_ANGLE`` to preserve precision, and so it can be converted
using the ``TRIGANGLE_TO_DEG()`` macro:

```c
static void compass_heading_handler(CompassHeadingData heading_data) {
  // Is the compass calibrated?
  switch(heading_data.compass_status) {
    case CompassStatusDataInvalid:
      APP_LOG(APP_LOG_LEVEL_INFO, "Not yet calibrated.");
      break;
    case CompassStatusCalibrating:
      APP_LOG(APP_LOG_LEVEL_INFO, "Calibration in progress. Heading is %ld",
                TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE - heading_data.magnetic_heading));
      break;
    case CompassStatusCalibrated:
      APP_LOG(APP_LOG_LEVEL_INFO, "Calibrated! Heading is %ld",
                TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE - heading_data.magnetic_heading));
      break;
  }
}
```

By default, the callback will be triggered whenever the heading changes by one
degree. To reduce the frequency of updates, change the threshold for heading
changes by setting a heading filter:

```c
// Only notify me when the heading has changed by more than 5 degrees.
compass_service_set_heading_filter(DEG_TO_TRIGANGLE(5));
```

## Unsubscribing From Compass Data

When the app is done using the compass, stop receiving callbacks by
unsubscribing:

```c
compass_service_unsubscribe();
```

## Peeking at Compass Data

To fetch a compass heading without subscribing, simply peek to get a single
sample:

```c
// Peek to get data
CompassHeadingData data;
compass_service_peek(&data);
```

> Similar to the subscription-provided data, the app should examine the peeked
> `CompassHeadingData` to determine if it is valid (i.e. the compass is
> calibrated).


---
# 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.

title: Dictation
description: |
  How to use the Dictation API to get voice-to-text input in watchapps.
guide_group: events-and-services
order: 4
platforms:
  - basalt
  - chalk
  - diorite
  - emery
related_docs:
  - Dictation
related_examples:
  - title: Simple Voice Demo
    url: https://github.com/pebble-examples/simple-voice-demo
  - title: Voice Quiz
    url: https://github.com/pebble-examples/voice-quiz
---

On hardware [platforms](/faqs/#pebble-sdk) supporting a microphone, the
``Dictation`` API can be used to gather arbitrary text input from a user.
This approach is much faster than any previous button-based text input system
(such as [tertiary text](https://github.com/vgmoose/tertiary_text)), and
includes the ability to allow users to re-attempt dictation if there are any
errors in the returned transcription.

> Note: Apps running on multiple hardware platforms that may or may not include
> a microphone should use the `PBL_MICROPHONE` compile-time define (as well as
> checking API return values) to gracefully handle when it is not available.

## How the Dictation API Works

The ``Dictation`` API invokes the same UI that is shown to the user when
responding to notifications via the system menu, with events occuring in the
following order:

* The user initiates transcription and the dictation UI is displayed.

* The user dictates the phrase they would like converted into text.

* The audio is transmitted via the Pebble phone application to a 3rd party
  service and translated into text.

* When the text is returned, the user is given the opportunity to review the
  result of the transcription. At this time they may elect to re-attempt the
  dictation by pressing the Back button and speaking clearer.

* When the user is happy with the transcription, the text is provided to the
  app by pressing the Select button.

* If an error occurs in the transcription attempt, the user is automatically
  allowed to re-attempt the dictation.

* The user can retry their dictation by rejecting a successful transcription,
  but only if confirmation dialogs are enabled.

## Beginning a Dictation Session

To get voice input from a user, an app must first create a ``DictationSession``
that contains data relating to the status of the dictation service, as well as
an allocated buffer to store the result of any transcriptions. This should be
declared in the file-global scope (as `static`), so it can be used at any time
(in button click handlers, for example).

```c
static DictationSession *s_dictation_session;
```

A callback of type ``DictationSessionStatusCallback`` is also required to notify
the developer to the status of any dictation requests and transcription results.
This is called at any time the dictation UI exits, which can be for any of the
following reasons:

* The user accepts a transcription result.

* A transcription is successful but the confirmation dialog is disabled.

* The user exits the dictation UI with the Back button.

* When any error occurs and the error dialogs are disabled.

* Too many transcription errors occur.

```c
static void dictation_session_callback(DictationSession *session, DictationSessionStatus status,
                                       char *transcription, void *context) {
  // Print the results of a transcription attempt
  APP_LOG(APP_LOG_LEVEL_INFO, "Dictation status: %d", (int)status);
}
```

At the end of this callback the `transcription` pointer becomes invalid - if the
text is required later it should be copied into a separate buffer provided by
the app. The size of this dictation buffer is chosen by the developer, and
should be large enough to accept all expected input. Any transcribed text longer
than the length of the buffer will be truncated.

```c
// Declare a buffer for the DictationSession
static char s_last_text[512];
```

Finally, create the ``DictationSession`` and supply the size of the buffer and
the ``DictationSessionStatusCallback``. This session may be used as many times
as requires for multiple transcriptions. A context pointer may also optionally
be provided.

```c
// Create new dictation session
s_dictation_session = dictation_session_create(sizeof(s_last_text),
                                               dictation_session_callback, NULL);
```

## Obtaining Dictated Text

After creating a ``DictationSession``, the developer can begin a dictation
attempt at any time, providing that one is not already in progress.

```c
// Start dictation UI
dictation_session_start(s_dictation_session);
```

The dictation UI will be displayed and the user will speak their desired input.

![listening >{pebble-screenshot,pebble-screenshot--time-red}](/images/guides/pebble-apps/sensors/listening.png)

It is recommended to provide visual guidance on the format of the expected input
before the ``dictation_session_start()`` is called. For example, if the user is
expected to speak a location that should be a city name, they should be briefed
as such before being asked to provide input.

When the user exits the dictation UI, the developer's
``DictationSessionStatusCallback`` will be called. The `status` parameter
provided will inform the developer as to whether or not the transcription was
successful using a ``DictationSessionStatus`` value. It is useful to check this
value, as there are multiple reasons why a dictation request may not yield a
successful result. These values are described below under
[*DictationSessionStatus Values*](#dictationsessionstatus-values).

If the value of `status` is equal to ``DictationSessionStatusSuccess``, the
transcription was successful. The user's input can be read from the
`transcription` parameter for evaluation and storage for later use if required.
Note that once the callback returns, `transcription` will no longer be valid.

For example, a ``TextLayer`` in the app's UI with variable name `s_output_layer`
may be used to show the status of an attempted transcription:

```c
if(status == DictationSessionStatusSuccess) {
  // Display the dictated text
  snprintf(s_last_text, sizeof(s_last_text), "Transcription:\n\n%s", transcription);
  text_layer_set_text(s_output_layer, s_last_text);
} else {
  // Display the reason for any error
  static char s_failed_buff[128];
  snprintf(s_failed_buff, sizeof(s_failed_buff), "Transcription failed.\n\nReason:\n%d",
           (int)status);
  text_layer_set_text(s_output_layer, s_failed_buff);
}
```

The confirmation mechanism allowing review of the transcription result can be
disabled if it is not needed. An example of such a scenario may be to speed up a
'yes' or 'no' decision where the two expected inputs are distinct and different.

```c
// Disable the confirmation screen
dictation_session_enable_confirmation(s_dictation_session, false);
```

It is also possible to disable the error dialogs, if so desired. This will
disable the dialogs that appear when a transcription attempt fails, as well as
disabling the ability to retry the dictation if a failure occurs.

```
// Disable error dialogs
dictation_session_enable_error_dialogs(s_dictation_session, false);
```

### DictationSessionStatus Values

These are the possible values provided by a ``DictationSessionStatusCallback``,
and should be used to handle transcription success or failure for any of the
following reasons.

| Status | Value | Description |
|--------|-------|-------------|
| ``DictationSessionStatusSuccess`` | `0` | Transcription successful, with a valid result. |
| ``DictationSessionStatusFailureTranscriptionRejected`` | `1` | User rejected transcription and dismissed the dictation UI. |
| ``DictationSessionStatusFailureTranscriptionRejectedWithError`` | `2` | User exited the dictation UI after a transcription error. |
| ``DictationSessionStatusFailureSystemAborted`` | `3` | Too many errors occurred during transcription and the dictation UI exited. |
| ``DictationSessionStatusFailureNoSpeechDetected`` | `4` | No speech was detected and the dictation UI exited. |
| ``DictationSessionStatusFailureConnectivityError`` | `5` | No Bluetooth or Internet connection available. |
| ``DictationSessionStatusFailureDisabled`` | `6` | Voice transcription disabled for this user. This can occur if the user has disabled sending 'Usage logs' in the Pebble mobile app. |
| ``DictationSessionStatusFailureInternalError`` | `7` | Voice transcription failed due to an internal error. |
| ``DictationSessionStatusFailureRecognizerError`` | `8` | Cloud recognizer failed to transcribe speech (only possible if error dialogs are disabled). |