pebble/devsite/source/_guides/user-interfaces/app-configuration.md
2025-02-24 18:58:29 -08:00

11 KiB

title description guide_group order platform_choice related_examples
App Configuration How to allow users to customize an app with a configuration page. user-interfaces 0 true
title url
Clay Example 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 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 Sample](/images/guides/user-interfaces/app-configuration/clay-sample.png =200)

Clay for Pebble dramatically simplifies the process of creating a configuration page, by allowing developers to define their application settings using a simple 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.

{% markdown %} ```js "capabilities": [ "configurable" ] ``` {% endmarkdown %}

^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:

{% markdown %} ```nc|text $ pebble package install pebble-clay ``` {% endmarkdown %}

^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:

{% markdown %} ```js "messageKeys": [ "BackgroundColor", "ForegroundColor", "SecondTick", "Animations" ] ``` {% endmarkdown %}

^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:

{% markdown %} ![CloudPebble Settings](/images/guides/user-interfaces/app-configuration/message-keys.png =400) {% endmarkdown %}

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:

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.

![Clay](/images/guides/user-interfaces/app-configuration/clay-actual.png =200)

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.

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.

// 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);
{% 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 %}

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.

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

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.

// 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:

Why not find out more about Clay for Pebble and perhaps even contribute to the project, it's open source!