Creating Native Companion Apps with JS and Cordova
Creating companion apps using PebbleKit Android and PebbleKit iOS is a great way to extend the functionality of your Pebble watchfaces and apps, but what if Java or Objective C just isn't your thing? What if you love JavaScript? What if you just don't want to write the same companion app twice?
Now you don't have to! Enter Cordova - an open source framework for building cross-platform mobile apps using web technologies such as HTML5 and JavaScript. What does this mean for you? One framework, and one codebase for building Pebble companion apps for iOS and Android!
Cordova allows developers to create plugins that wrap native functionality such as accessing the phone's contacts, calendar, camera, and other phone APIs. Plugins allow you to interface with these APIs through JavaScript, and use them in your Cordova projects. Needless to say, PebbleKit Android and PebbleKit iOS are now available as a singular Cordova plugin.
In this blog post, we'll look at how to use Cordova to create a companion app that allows you to retrieve the next upcoming calendar event for the current day.
Setup
The first step will be installing Cordova and creating a new project. Cordova provides these steps generically here, but if you want to follow along down to the T, here are the steps listed below.
$ npm install -g cordova
$ cordova create calendar-example com.pebble.calendarexample "Calendar Example"
$ cd calendar-example
$ cordova platform add android
$ cordova platform add ios
$ cordova plugin add cordova-plugin-calendar
$ cordova plugin add cordova-plugin-pebblekit
Note, this assumes you have node/npm already installed and avaiable on your machine. If not, head on over to nodejs.org to install node.
Doesn't seem too bad! If the cordova create
command seems a little scary to
you, don't be afraid to ask for help with cordova create --help
.
Querying Events
The next step is to use the calendar plugin we added earlier in order to fetch
the next event for the day. Before we do that though, we'll need to to setup
the Cordova PebbleKit plugin, as shown below. Fire up your favourite text
editor and open www/js/index.js
file. This is the JavaScript file that runs
when your app starts up, your application's control logic will go here.
Upon opening the file, you'll notice there's already a fair bit of code
generated by Cordova. What's important here is the onDeviceReady
function,
this is a callback you can use to be notified once the plugins for your Cordova
project have been loaded and are ready to be called. Let's create our own
function called fetchCalendarEvent
to be called once the PebbleKit plugin has
been setup. We'll also declare a variable uuid with our Pebble app's UUID,
which is required when communicating with our watchapp via AppMessage.
var uuid = "ebc92429-483e-4b91-b5f2-ead22e7e002d";
var app = {
/* ... */
onDeviceReady: function () {
app.receivedEvent('deviceready');
window.pebblekit.setup(uuid, function () {
app.fetchCalendarEvent();
});
},
fetchCalendarEvent: function () {
var startDate = new Date(); // Now
var endDate = new Date();
endDate.setHours(24, 0, 0, 0); // Nearest midnight in the future
window.plugins.calendar.findEvent(
undefined, // title
undefined, // eventLocation
undefined, // notes
startDate,
endDate,
app.calendarSuccessCallback,
app.calendarFailureCallback
);
},
calendarSuccessCallback: function (events) {
// TODO
},
calendarFailureCallback: function (err) {
console.error('Failed to fatch calendar events', err);
}
/* ... */
}
Note, the calendar plugin recommends checking that you have sufficient permissions before using its APIs. For the sake of brevity, the permission workflow has been omitted in this post.
The first 3 parameters we passed to the calendar.findEvent
function are
undefined, since we don't want to filter out events with specific titles,
locations, or notes. The last two parameters are callback functions that will
be executed when the app has succeeded or failed to fetch calendar data. Our
calendarFailureCallback will log a simple message, and our
calendarSuccessCallback will sort through the events and find the earliest
event in the list.
calendarSuccessCallback: function (events) {
if (events.length == 0) {
// TODO
return;
}
// Find the earliest returned event
var earliestEvent = events[0];
for (var i = 1; i < events.length; i++) {
var tempDate = events[i];
var date1 = Date.parse(earliestEvent.startDate);
var date2 = Date.parse(tempDate.startDate);
if (date2 < date1) {
earliestEvent = tempDate;
}
}
},
Sending An AppMessage
All we have to do now is send the relevent bits of information down to our
Pebble app. This is where the Pebble plugin is going to come in handy. At the
end of the function, we'll call sendAppMessage
which works in a similar
manner to PebbleKit JS'
sendAppMessage
function.
The title is easy to snag - it's a property on the event object, available via
event.title
. The hours and minutes will take a bit of work since they only
exist within a string property on the object (event.startDate
). Luckily the
format for the date is fixed, so we know where in the string it will be.
With this in mind, we can use the String.prototype.substring method to extract the numbers we want. The method takes two parameters, the first is the starting index (inclusive) of the substring, and the second is the the end index (exclusive). So if we want the hour, we'd pass 11 as the starting index, and 13 as the end index. This will create a substring containing the 11th and 12th characters of the string.
Also in this example, the data
object represents our AppMessage, where 10
,
11
, and 12
are the AppMessage keys.
// Format is in YYYY-MM-DD HH:mm:ss
var hourString = earliestEvent.startDate.substring(11, 13);
var hour = parseInt(hourString);
var minuteString = earliestEvent.startDate.substring(14, 16);
var minute = parseInt(minuteString);
// AppMessage keys
var data = {
'10': earliestEvent.title,
'11': hour,
'12': minute
};
window.pebblekit.sendAppMessage(uuid, data, function () {
console.log('sent AppMessage', data);
}, function (err) {
console.error('Couldn\'t send AppMessage', err);
});
The only thing left is to read the AppMessage on the C side. To quote every university professor I've ever had: "We leave this as an exercise to the reader". (Just kidding, you can see the watchface's full C code here, or consult the documentation).
While we only used two of the APIs of the PebbleKit Cordova plugin here, there is much more available to you, all with new and shiny documentation
For folks that are interested, our Cordova plugin is completely open source, and hosted on Github. We welcome bug reports, feature requests, and contributions with open arms :).
And that's all there is to it! If you've been holding back on writing a companion app for your amazing app, now's the time to try it!
Overview
Categories
- All Posts
- #makeawesomehappen
- At the Pub
- Beautiful Code
- CloudPebble
- Down the Rabbit Hole
- Freshly Baked
- Timeline