Over the last week, there have been have been four version of Dashboard released (9, 10, 11 and now 12) following the inclusion of the Wakeup API. I used this shiny new firmware feature to let users of Dashboard schedule daily wakeups to issue an on/off command to the Dashboard Android app, at times they would use the toggles anyway.

pebble-screenshot_2014-11-08_21-37-47    pebble-screenshot_2014-11-08_21-38-04

pebble-screenshot_2014-11-08_21-38-11    pebble-screenshot_2014-11-08_21-38-21

A new ‘HOLD’ icon prompts access to the scheduling feature, including list of existing events and UI to create new ones.

A personal example of this is that every night at about midnight I turn off WiFi on my phone to save power through the night using Tasker. Now, I can remove the Tasker icon from the status bar and use Dashboard to carry out the action instead. Of course, Tasker didn’t require me to keep my Bluetooth on overnight, but it’s a small price to pay for automated control of Android radios!

When adding such a complex feature (Dashboard itself went from two main code files to eight and ~600 to ~1300 lines of code), bugs will occur. Some will be code-based, such as not handling setting two wakeups for the same time (which the system will not allow) and warning the user, and some are behavioral.

At the moment, the Pebble appstore will not always update the released watchapp when a new version is uploaded. This can make co-ordinating a release with Google Play Store very difficult. Users were prompted to ‘update watchapp from Android app’. What I intended was for them to use the ‘Install Watchapp’ button in the Dashboard Android app to get the bundled compatible version, but in reality they were unloading and reloading the watchapp from the Pebble app locker, which ended up with them still having the old version. And so the loop continued until some concerned users emailed me about it. In all cases I clarified the correct procedure and every case was fixed. So now that process is hopefully a bit more explicit!

For a couple of my existing watchapps and watchfaces I have implemented a smooth animation using an AppTimer. This involves something like the snippet below:

static void some_layer_update_proc(Layer *layer, GContext *ctx) {
  // Graphics calls

}

static void timer_handler(void *context) {
  // Update frame
  layer_mark_dirty(some_layer);

  // Finally schedule next frame
  app_timer_register(34, timer_handler, NULL);
}

...

static void start_animation() {
  // Schedule first frame to start loop
  layer_set_update_proc(some_layer, some_layer_update_proc);
  app_timer_register(34, timer_handler, NULL);
}

As you can see, after the first frame is scheduled with an AppTimer, the timer’s handler schedules the next, and so an infinite loop is born. After implementing this multiple times, it occurred to me that I could make this process easier to set up, even if it was just for myself.

The result of this is a new library called pge, which creates an object that handles this looping of game logic and rendering per-frame for you, similar to STL. It also handles button clicks with an easier abstraction for the developer. Here’s a quick example of usage, from the GitHub README file:

#include "pge.h"

static PGE *s_game;

void loop() {

}

void draw(GContext *ctx) {

}

void click(int button_id) {

}

...

s_game = pge_begin(s_window, loop, draw, click);

This will start a 30 FPS loop that calls the developer’s implementation of draw() and loop() every frame, and click() when a button is clicked. The developer can then check the button ID as usual using the Pebble SDK constants, such as BUTTON_ID_UP. The loop will end and the PGE can be destroyed as part of a normal Window‘s lifecycle:

static void main_window_unload(Window *window) {
  // Destroy all game resources
  pge_finish(s_game);
}

The GitHub repo also includes a sample app where I implemented a simple ‘game’ of controlling a ‘robot’, using select to start/stop the robot and the up and down buttons to rotate its direction of travel.

Robot game

I’m currently working on implementing an Entity object that can be added to a list for automatic looping and rendering by the PGE. This will eventually also allow collision, as well as couple of other useful features.

If you are thinking of creating such a game, this library can hopefully help get you started. Let me know when you end up creating! The repo can be found here.

A while ago I created a simple Alert Library, which provides functions to show and hide a set of layers to show an ‘alert window’ for a time that tells the user of an event without changing Window.

screenshot

Continuing this theme, today I created another similar library called the ToastLayer, which has two advantages over the Alert Library:

1. The toast notification animates up from the bottom, instead of covering the majority of the Window.

2. It is designed as an object, like any of the other Layers in the Pebble SDK. This means there can be more than one!

Feel free to use it to show notifications without changing Window! I will probably look into including this in some of my existing apps.

Not a lot has been happening on this blog for the last few weeks, and the reason for that is that I have been busy beginning my internship at Pebble! It’s been a great experience so far (We released SDK 2.5 today!) and I can’t wait to work on more awesome things to help make Pebble even more useful smartwatch.

That being said, I’ve been able to find time to maintain and update my public Pebble apps at weekends, and hope to continue this pattern whenever I can, because I have some ideas I want to implement even just for myself.

A result of this is a new application of my Spark Core driven LCD project in collaboration with a new colleague to display social media trends from WhatTheTrend on animated cards on Pebble, and as an added bonus show the same information on the LCD display.

After some teething issues, it was eventually presentable and works pretty well – but for some reason only on Wi-Fi. Here’s a photo of the whole thing in action. (Eduardo Sasha is imprinted on my brain now from all the testing…)

image

Stay tuned for coming updates to Dashboard and Wristponder, as well as bringing Watch Trigger up to date as well!

Just released Dashboard 1.3!

The major new feature in this version is the ability to dynamically re-order the toggles to suit your preference. The way this works involves selecting each position in an Android Spinner in the ‘Configure’ tab:

Screenshot_2014-09-07-19-46-17

 

Each time a user makes a selection in one of the positions, the rest of the Spinner array is checked to look for a duplicate of the toggle the user has just chosen, and switches the two around. For example, if the user changes the Wi-Fi toggle to Autosync, the first toggle becomes Autosync and then the existing Autosync Spinner duplicate is changed to the only other missing toggle type – Wi-Fi!

This means that the toggles can be any order possible, such as the examples below:

toggle-config

That’s a total of P(8,8) = 40320!

 

A not insignificant amount of time after starting work on this update, it is finally here!

New features include:

- All data to be synchronised is now done in a streaming manner after each Window appears, meaning no more waiting for sync!
– Favourite contacts are now chosen using the Android Contact Picker, and not from five (potentially very large) spinners.
– The debug log can now be read and reported from the Settings screen. This makes reporting bugs to me much simpler.

Here’s a before and after shot of the Favourites selection Activity:

favourite-selection

There is only really one major comment to make that arose during development for this version, and that’s on the subject of an Android Activity in KitKat. I came across a a strange behaviour when launching the Pebble Android app to install the watchapp. It appears that in the event that the user chooses ‘Install Watchapp’ from Wristponder Settings, completes the installation and returns to the Settings Activity, they will not be able to launch any new ones such as Favourites or Import for anywhere between 10 to 45 seconds. On top of this any spamming of buttons done by a bemused user results in just as many launches when the Activitys requested are eventually delivered.

The only evidence of this in a single logcat output something along the lines of “Waited long enough for ServiceRecord” (unable to reproduce it at the time of writing, which is a good thing!) and this is the only link I can find on the subject. Apparently its to do with serializing launches from background services, although none of my launches are Services.

Anyway, that is the only problem with this release, and I feel that Wristponder is now a pretty solid and smooth piece of software. For this I am proud!

Download
Get it on Google Play

Edit: 100th post!

Quick post to share  a ‘new’ Layer type I created for an upcoming project: ColorLayer. It’s supposed to be a convenience for adding a simple layer of colour. Problem is, unless I’m missing something very obvious, the two options are to declare a standard Layer and assign it a basic _fill_rect() LayerUpdateProc, or use a TextLayer and modifying the background colours.

I normally choose the latter, so for the umpteenth time of doing so I decided to wrap it up to make it a bit simpler to use. Here’s the result!

ColorLayer.h

/**
 * Layer on top of TextLayer used just for coloring areas without using LayerUpdateProc
 * Author: Chris Lewis (@Chris_DL)
 * Version 1.0.0
 */
#include <pebble.h>

#ifndef COLOR_LAYER_H
#define COLOR_LAYER_H

typedef struct {
	TextLayer *layer;
} ColorLayer;

ColorLayer* color_layer_create(GRect bounds, GColor fill_color);
void color_layer_destroy(ColorLayer *this);
void color_layer_set_color(ColorLayer *this, GColor fill_color);
void color_layer_set_frame(ColorLayer *this, GRect bounds);
Layer* color_layer_get_layer(ColorLayer *this);

#endif

ColorLayer.c

#include "color_layer.h"

ColorLayer* color_layer_create(GRect bounds, GColor fill_color)
{
	ColorLayer *this = malloc(sizeof(ColorLayer));
	this->layer = text_layer_create(bounds);
	text_layer_set_background_color(this->layer, fill_color);

	return this;
}

void color_layer_destroy(ColorLayer *this)
{
	text_layer_destroy(this->layer);
	free(this);
}

void color_layer_set_color(ColorLayer *this, GColor fill_color)
{
	text_layer_set_background_color(this->layer, fill_color);
}

void color_layer_set_frame(ColorLayer *this, GRect bounds)
{
	layer_set_frame(text_layer_get_layer(this->layer), bounds);
}

Layer* color_layer_get_layer(ColorLayer *this)
{
	return text_layer_get_layer(this->layer);
}

It could be argued that it’s such a thin layer you may as well not bother, but I find it to be sufficiently easier to setup and read (as well as avoiding confusion with TextLayers that actually show text), so once again I’m glad coding allows a degree of personal preference and style!

Follow

Get every new post delivered to your Inbox.

Join 179 other followers