mirror of
https://github.com/google/pebble.git
synced 2025-03-22 03:32:20 +00:00
349 lines
11 KiB
Markdown
349 lines
11 KiB
Markdown
|
---
|
||
|
# 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: App Configuration
|
||
|
description: |
|
||
|
How to allow users to customize an app with a configuration page.
|
||
|
guide_group: user-interfaces
|
||
|
order: 0
|
||
|
platform_choice: true
|
||
|
related_examples:
|
||
|
- title: Clay Example
|
||
|
url: https://github.com/pebble-examples/clay-example
|
||
|
---
|
||
|
|
||
|
Many watchfaces and watchapps in the Pebble appstore include the ability to
|
||
|
customize their behavior or appearance through the use of a configuration page.
|
||
|
|
||
|
[Clay for Pebble](https://github.com/pebble/clay) is the recommended approach
|
||
|
for creating configuration pages, and is what will be covered in this guide.
|
||
|
If you need to host your own configuration pages, please follow our
|
||
|
{% guide_link user-interfaces/app-configuration-static "Manual Setup" %} guide.
|
||
|
|
||
|

|
||
|
|
||
|
Clay for Pebble dramatically simplifies the process of creating a configuration
|
||
|
page, by allowing developers to define their application settings using a
|
||
|
simple [JSON](https://en.wikipedia.org/wiki/JSON) file. Clay processes the
|
||
|
JSON file and then dynamically generates a configuration page which matches the
|
||
|
existing style of the Pebble mobile application, and it even works without an
|
||
|
Internet connection.
|
||
|
|
||
|
|
||
|
## Enabling Configuration
|
||
|
|
||
|
^LC^ For an app to be configurable, it must include the 'configurable' item in
|
||
|
`package.json`.
|
||
|
|
||
|
<div class="platform-specific" data-sdk-platform="local">
|
||
|
{% markdown %}
|
||
|
```js
|
||
|
"capabilities": [ "configurable" ]
|
||
|
```
|
||
|
{% endmarkdown %}
|
||
|
</div>
|
||
|
|
||
|
^CP^ For an app to be configurable, it must include the 'configurable' item in
|
||
|
'Settings'.
|
||
|
|
||
|
The presence of this value tells the mobile app to display the gear icon that
|
||
|
is associated with the ability to launch the config page next to the app itself.
|
||
|
|
||
|
## Installing Clay
|
||
|
|
||
|
Clay is available as a {% guide_link pebble-packages "Pebble Package" %}, so it
|
||
|
takes minimal effort to install.
|
||
|
|
||
|
^LC^ Within your project folder, just type:
|
||
|
|
||
|
<div class="platform-specific" data-sdk-platform="local">
|
||
|
{% markdown %}
|
||
|
```nc|text
|
||
|
$ pebble package install pebble-clay
|
||
|
```
|
||
|
{% endmarkdown %}
|
||
|
</div>
|
||
|
|
||
|
^CP^ Go to the 'Dependencies' tab, type 'pebble-clay' into the search box and
|
||
|
press 'Enter' to add the dependency.
|
||
|
|
||
|
|
||
|
## Choosing messageKeys
|
||
|
|
||
|
When passing data between the configuration page and the watch application, we
|
||
|
define `messageKeys` to help us easily identify the different values.
|
||
|
|
||
|
In this example, we're going to allow users to control the background color,
|
||
|
foreground color, whether the watchface ticks on seconds and whether any
|
||
|
animations are displayed.
|
||
|
|
||
|
^LC^ We define `messageKeys` in the `package.json` file for each configuration
|
||
|
setting in our application:
|
||
|
|
||
|
<div class="platform-specific" data-sdk-platform="local">
|
||
|
{% markdown %}
|
||
|
```js
|
||
|
"messageKeys": [
|
||
|
"BackgroundColor",
|
||
|
"ForegroundColor",
|
||
|
"SecondTick",
|
||
|
"Animations"
|
||
|
]
|
||
|
```
|
||
|
{% endmarkdown %}
|
||
|
</div>
|
||
|
|
||
|
^CP^ We define `messageKeys` in the 'Settings' tab for each configuration
|
||
|
setting in our application. The 'Message Key Assignment Kind' should be set to
|
||
|
'Automatic Assignment', then just enter each key name:
|
||
|
|
||
|
<div class="platform-specific" data-sdk-platform="cloudpebble">
|
||
|
{% markdown %}
|
||
|

|
||
|
{% endmarkdown %}
|
||
|
</div>
|
||
|
|
||
|
## Creating the Clay Configuration
|
||
|
|
||
|
^LC^ The Clay configuration file (`config.js`) should be created in your
|
||
|
`src/pkjs/` folder. It allows the easy definition of each type of HTML form
|
||
|
entity that is required. These types include:
|
||
|
|
||
|
^CP^ The Clay configuration file (`config.js`) needs to be added to your project
|
||
|
by adding a new 'Javascript' source file. It allows the easy definition of
|
||
|
each type of HTML form entity that is required. These types include:
|
||
|
|
||
|
* [Section](https://github.com/pebble/clay#section)
|
||
|
* [Heading](https://github.com/pebble/clay#heading)
|
||
|
* [Text](https://github.com/pebble/clay#text)
|
||
|
* [Input](https://github.com/pebble/clay#input)
|
||
|
* [Toggle](https://github.com/pebble/clay#toggle)
|
||
|
* [Select](https://github.com/pebble/clay#select)
|
||
|
* [Color Picker](https://github.com/pebble/clay#color-picker)
|
||
|
* [Radio Group](https://github.com/pebble/clay#radio-group)
|
||
|
* [Checkbox Group](https://github.com/pebble/clay#checkbox-group)
|
||
|
* [Generic Button](https://github.com/pebble/clay#generic-button)
|
||
|
* [Range Slider](https://github.com/pebble/clay#range-slider)
|
||
|
* [Submit Button](https://github.com/pebble/clay#submit)
|
||
|
|
||
|
In our example configuration page, we will add some introductory text, and group
|
||
|
our fields into two sections. All configuration pages must have a submit button
|
||
|
at the end, which is used to send the JSON data back to the watch.
|
||
|
|
||
|

|
||
|
|
||
|
Now start populating the configuration file with the sections you require, then
|
||
|
add the required elements to each section. Be sure to assign the correct
|
||
|
`messageKey` to each field.
|
||
|
|
||
|
```js
|
||
|
module.exports = [
|
||
|
{
|
||
|
"type": "heading",
|
||
|
"defaultValue": "App Configuration"
|
||
|
},
|
||
|
{
|
||
|
"type": "text",
|
||
|
"defaultValue": "Here is some introductory text."
|
||
|
},
|
||
|
{
|
||
|
"type": "section",
|
||
|
"items": [
|
||
|
{
|
||
|
"type": "heading",
|
||
|
"defaultValue": "Colors"
|
||
|
},
|
||
|
{
|
||
|
"type": "color",
|
||
|
"messageKey": "BackgroundColor",
|
||
|
"defaultValue": "0x000000",
|
||
|
"label": "Background Color"
|
||
|
},
|
||
|
{
|
||
|
"type": "color",
|
||
|
"messageKey": "ForegroundColor",
|
||
|
"defaultValue": "0xFFFFFF",
|
||
|
"label": "Foreground Color"
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"type": "section",
|
||
|
"items": [
|
||
|
{
|
||
|
"type": "heading",
|
||
|
"defaultValue": "More Settings"
|
||
|
},
|
||
|
{
|
||
|
"type": "toggle",
|
||
|
"messageKey": "SecondTick",
|
||
|
"label": "Enable Seconds",
|
||
|
"defaultValue": false
|
||
|
},
|
||
|
{
|
||
|
"type": "toggle",
|
||
|
"messageKey": "Animations",
|
||
|
"label": "Enable Animations",
|
||
|
"defaultValue": false
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"type": "submit",
|
||
|
"defaultValue": "Save Settings"
|
||
|
}
|
||
|
];
|
||
|
```
|
||
|
|
||
|
## Initializing Clay
|
||
|
|
||
|
To initialize Clay, all you need to do is add the following JavaScript into
|
||
|
your `index.js` file.
|
||
|
|
||
|
```js
|
||
|
// Import the Clay package
|
||
|
var Clay = require('pebble-clay');
|
||
|
// Load our Clay configuration file
|
||
|
var clayConfig = require('./config');
|
||
|
// Initialize Clay
|
||
|
var clay = new Clay(clayConfig);
|
||
|
```
|
||
|
|
||
|
<div class="platform-specific" data-sdk-platform="local">
|
||
|
{% markdown %}
|
||
|
> When using the local SDK, it is possible to use a pure JSON
|
||
|
> configuration file (`config.json`). If this is the case, you must not include
|
||
|
> the `module.exports = []` in your configuration file, and you need to
|
||
|
> `var clayConfig = require('./config.json');`
|
||
|
{% endmarkdown %}
|
||
|
</div>
|
||
|
|
||
|
## Receiving Config Data
|
||
|
|
||
|
Within our watchapp we need to open a connection with ``AppMessage`` to begin
|
||
|
listening for data from Clay, and also provide a handler to process the data
|
||
|
once it has been received.
|
||
|
|
||
|
```c
|
||
|
void prv_init(void) {
|
||
|
// ...
|
||
|
|
||
|
// Open AppMessage connection
|
||
|
app_message_register_inbox_received(prv_inbox_received_handler);
|
||
|
app_message_open(128, 128);
|
||
|
|
||
|
// ...
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Once triggered, our handler will receive a ``DictionaryIterator`` containing
|
||
|
``Tuple`` objects for each `messageKey`. Note that the key names need to be
|
||
|
prefixed with `MESSAGE_KEY_`.
|
||
|
|
||
|
```c
|
||
|
static void prv_inbox_received_handler(DictionaryIterator *iter, void *context) {
|
||
|
// Read color preferences
|
||
|
Tuple *bg_color_t = dict_find(iter, MESSAGE_KEY_BackgroundColor);
|
||
|
if(bg_color_t) {
|
||
|
GColor bg_color = GColorFromHEX(bg_color_t->value->int32);
|
||
|
}
|
||
|
|
||
|
Tuple *fg_color_t = dict_find(iter, MESSAGE_KEY_ForegroundColor);
|
||
|
if(fg_color_t) {
|
||
|
GColor fg_color = GColorFromHEX(fg_color_t->value->int32);
|
||
|
}
|
||
|
|
||
|
// Read boolean preferences
|
||
|
Tuple *second_tick_t = dict_find(iter, MESSAGE_KEY_SecondTick);
|
||
|
if(second_tick_t) {
|
||
|
bool second_ticks = second_tick_t->value->int32 == 1;
|
||
|
}
|
||
|
|
||
|
Tuple *animations_t = dict_find(iter, MESSAGE_KEY_Animations);
|
||
|
if(animations_t) {
|
||
|
bool animations = animations_t->value->int32 == 1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Persisting Settings
|
||
|
|
||
|
By default, Clay will persist your settings in localStorage within the
|
||
|
mobile application. It is common practice to also save settings within the
|
||
|
persistent storage on the watch. This creates a seemless experience for users
|
||
|
launching your application, as their settings can be applied on startup. This
|
||
|
means there isn't an initial delay while the settings are loaded from the phone.
|
||
|
|
||
|
You could save each individual value within the persistent storage, or you could
|
||
|
create a struct to hold all of your settings, and save that entire object. This
|
||
|
has the benefit of simplicity, and because writing to persistent storage is
|
||
|
slow, it also provides improved performance.
|
||
|
|
||
|
```c
|
||
|
// Persistent storage key
|
||
|
#define SETTINGS_KEY 1
|
||
|
|
||
|
// Define our settings struct
|
||
|
typedef struct ClaySettings {
|
||
|
GColor BackgroundColor;
|
||
|
GColor ForegroundColor;
|
||
|
bool SecondTick;
|
||
|
bool Animations;
|
||
|
} ClaySettings;
|
||
|
|
||
|
// An instance of the struct
|
||
|
static ClaySettings settings;
|
||
|
|
||
|
// AppMessage receive handler
|
||
|
static void prv_inbox_received_handler(DictionaryIterator *iter, void *context) {
|
||
|
// Assign the values to our struct
|
||
|
Tuple *bg_color_t = dict_find(iter, MESSAGE_KEY_BackgroundColor);
|
||
|
if (bg_color_t) {
|
||
|
settings.BackgroundColor = GColorFromHEX(bg_color_t->value->int32);
|
||
|
}
|
||
|
// ...
|
||
|
prv_save_settings();
|
||
|
}
|
||
|
|
||
|
// Save the settings to persistent storage
|
||
|
static void prv_save_settings() {
|
||
|
persist_write_data(SETTINGS_KEY, &settings, sizeof(settings));
|
||
|
}
|
||
|
```
|
||
|
|
||
|
You can see a complete implementation of persisting a settings struct in the
|
||
|
[Pebble Clay Example]({{ site.links.examples_org }}/clay-example).
|
||
|
|
||
|
## What's Next
|
||
|
|
||
|
If you're thinking that Clay won't be as flexible as hand crafting your own
|
||
|
configuration pages, you're mistaken.
|
||
|
|
||
|
Developers can extend the functionality of Clay in a number of ways:
|
||
|
|
||
|
* Define a
|
||
|
[custom function](https://github.com/pebble/clay#custom-function) to enhance the
|
||
|
interactivity of the page.
|
||
|
* [Override events](https://github.com/pebble/clay#handling-the-showconfiguration-and-webviewclosed-events-manually)
|
||
|
and transform the format of the data before it's transferred to the watch.
|
||
|
* Create and share your own
|
||
|
[custom components](https://github.com/pebble/clay#custom-components).
|
||
|
|
||
|
Why not find out more about [Clay for Pebble](https://github.com/pebble/clay)
|
||
|
and perhaps even
|
||
|
[contribute](https://github.com/pebble/clay/blob/master/CONTRIBUTING.md) to the
|
||
|
project, it's open source!
|