8.9 KiB
title | description | guide_group | menu | order | related_docs | related_examples | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
One Click Actions | Details about how to create One Click Action watchapps | design-and-interaction | true | 2 |
|
|
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.
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:
- Application is launched
- Application performs action
- Application displays status to user
- 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:
- Application launched
- Application performs action (take a voice note)
- Start listening for dictation
- Accept dictation response
- Application displays a success message
- 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.
- Application launched
- If a booking exists:
- Refresh booking status
- Update
App Glance
with new status - Exit to watchface
- Application performs action (create a booking)
- Update AppGlance: “Your Uber is on it’s way”
- Application displays a success message
- 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, or a real one if you’re lucky enough to have one.
Our flow will be incredibly simple:
- Launch the application
- Take an action (toggle the state of the lock)
- Update the
App Glance
to indicate the new lock state - Display a success message
- 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 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.
#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.
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.
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.
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.
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.
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 for
the actual Javascript implementation.
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.
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.
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.
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
application and also the App Glance
and AppExitReason
guides for further
information.