First Pebble Timeline App – BBC News

Introduction

As a side-effect of being involved with the development and testing of the new Pebble timeline experience, I’ve been eager to try my hand at exploiting this new mechanism to bring timely updates to users of BBC News (which as with most of my apps, began as an app for personal use) with timeline pins.

The Concept

As you may know from reading the Pebble Developers timeline guides, Pebble watchapps can now incorporate a purely web-based element to display data from web data sources (such as the BBC News feeds) on the timeline. This is ideal for chronological events such as news stories, as each pin is shown on the timeline according to date.

Pins can also include notifications that appear when they are first created or updated, keeping the user informed as details change. This functionality is used to show a notification each time a new news story is available. The pin itself includes only the icon (standard timeline pin icon) and the title, which is the main title of the story. In addition, timeline pins can have actions associated with them, enabling the user to launch the associated watchapp (whose API key is used to push the pins) and pass a single integer as a context argument. My idea was to use this action ability to enable the pin to open the BBC News watchapp to show the full story details, since a very long timeline pin body was a poor scrolling experience.

The Problem

The fundamental question was this; How does one tell the watchapp which story to show from the pin using a single integer? The first guess was to specify the launchCode (the argument) as the position in the array of downloaded news stories. This would work for a very recent pin only, as the order of news stories in the feed changes frequently as new stories are added and removed from the headlines. After an hour or two, the order would change and an existing pin would possess a launchCode pointing to the same array location, but no longer guaranteed to be the same story.

Implementation

The solution I ended up choosing came from Wristponder, which uses a checksum of the persisted list of responses on the Android and Pebble sides, prompting a re-download to the watch if the two do not match (i.e.: The user has modified their list of responses). Thus the procedure to open a news story from its pin is this:

1. Backend server running on DigitalOcean downloads the RSS feed from the BBC every half an hour, using a function to create a list of story objects from the XML;

var parseFeed = function(responseText) {
  var items = [];
  var longestTitle = 0;
  var longestDesc = 0;
  while(responseText.indexOf('<title>') > 0 && items.length < MAX_ITEMS) {
    //Title
    var title = responseText.substring(responseText.indexOf('<title>') + '<title>'.length);
    title = title.substring(0, title.indexOf('</title>'));
    responseText = responseText.substring(responseText.indexOf('</title>') + '</title>'.length);

    //Desc
    var desc = responseText.substring(responseText.indexOf('<description>') + '<description>'.length);
    desc = desc.substring(0, desc.indexOf('</description>'));

    // Date
    var date = responseText.substring(responseText.indexOf('<pubDate>') + '<pubDate>'.length);
    date = date.substring(0, date.indexOf('</pubDate>'));

    //Add
    var s = { 'title': title, 'description': desc, 'date': date };
    items.push(s);

    // Metrics
    Log('Story '+ items.length + ': ' + s.title + ' // ' + s.description);
    Log('(' + s.title.length + 'x' + s.description.length + ')');
    if(s.title.length > longestTitle) {
      longestTitle = s.title.length;
    }
    if(s.description.length > longestDesc) {
      longestDesc = s.description.length;
    }

    // Next
    responseText = responseText.substring(responseText.indexOf('</description>') + '</description>'.length);
  }

  Log('parseFeed(): Extracted ' + items.length + ' items.');
  Log('parseFeed(): Longest title/description: ' + longestTitle + '/' + longestDesc);
  return items;
};

2. Backend uses story titles, publish dates and checksums of the titles to push a pin for each story. The id field of each pin is a prefix followed by the Unix timestamp of the pubDate RSS field (as an easy solution of a fairly unique number from each story). The checksum is generated by simply adding all the character codes in each title, then specifying this number as the ‘Open Story’ pin action’s lanchCode;

var pin = {
  'id': 'bbcnews-story-' + pubDate.unix(),
  'time': pubDate.toDate(),
  'layout': {
    'type': 'genericPin',
    'tinyIcon': 'system://images/TIMELINE_PIN_TINY',
    'title': gStories[i].title,
    'subtitle': 'BBC News Headline'
  },
  'createNotification': {
    'layout': {
    'type': 'genericPin',
    'tinyIcon': 'system://images/TIMELINE_PIN_TINY',
    'title': gStories[i].title,
    'subtitle': 'BBC News Headline'
    }
  },
  'actions': [
    {
      'title': 'Open Story',
      'type': 'openWatchApp',
      'launchCode': checksum(gStories[i].title)
    }
  ]
};

3. These pins are filtered for duplicates and staleness by the Pebble timeline public API, then pushed through the Pebble mobile app to the user’s watch. This is demanded by a subscription to the ‘headlines’ topic that all users are subscribed to when they first launch the BBC News watchapp (this will be optional in the first release).

4. The user selects a BBC News story from their timeline and chooses the ‘Open Story’ pin action. This opens the watchapp with the launch_get_args() value set as the checksum stored in the pin when it was originally generated. If the launchCode is not zero, a pin was specified;

#ifdef PBL_PLATFORM_APLITE

// Timeline not available, give me ALL the stories!
comm_request(COMM_MODE_LIST, 0);

#elif PBL_PLATFORM_BASALT

// Is launchCode specified from a pin?
int launch_code = launch_get_args();

if(launch_code == 0) {
  // Get all stories
  comm_request(COMM_MODE_LIST, 0);

  stories_window_set_desc_text("Updating...");
} else if(launch_reason() == APP_LAUNCH_TIMELINE_ACTION) {
  // Check this checksum, JS!
  comm_request(COMM_MODE_PIN, launch_code);

  stories_window_set_desc_text("Getting pin story...");
}

#endif

5. The watchapp sends the checksum for the pin in question to PebbleKit JS, which downloads the latest feed from the BBC News site and checks the query checksum against checksums of all the stories downloaded. First, the checksum is obtained from the watch’s AppMessage;

if(hasKey(dict, 'KEY_ACTION')) {
  gLaunchCode = getValue(dict, 'KEY_ACTION');
  Log('TIMELINE PIN LAUNCH CODE: ' + gLaunchCode + '\n\n\n');

  // Download stories, and match the titles to the pin
  download(persistRead('category', 'headlines'), findPinWithHash);
}

Next, the matching story is found;

function findPinWithHash(responseText) {
  //Strip metadata
  var spool = responseText.substring(responseText.indexOf('<item>') + '<item>'.length);
  gQuantity = 30; // Get all

  var stories = parseFeed(spool);
  Log('Finding title with launchCode=' + gLaunchCode + ' in list of ' + stories.length + ' stories');

  var found = false;
  for(var i = 0; i < stories.length; i += 1) {
    var check = checksum(stories[i].title);
    if('' + check == '' + gLaunchCode) {
      Log('Found! check=' + check + ', gLaunchCode=' + gLaunchCode);

      // Send to phone
      var dict = {
        'KEY_ACTION': 0,
        'KEY_TITLE': stories[i].title,
        'KEY_DESCRIPTION': stories[i].description
      };
      Pebble.sendAppMessage(dict, function() {
        Log('Sent pin data to watch!');
        found = true;
      });
    }
  }

  // Not found?
  if(found == false) {
    var dict = {
      'KEY_FAILED': 1
    };
    Pebble.sendAppMessage(dict, function() {
      Log('Informed Pebble of failure to find story.');
    },
    function(err) {
      Log('Failed to inform of failure!');
    });
  }
}

6. If the story is still relatively recent (this can vary) and a checksum match is found, the story’s full title and body are sent to the watchapp for display to the user. The RSS feed also contains links to thumbnails already formatted to 144 pixels in width, which is ideally placed to be a possible future feature.

Results

The resulting flow looks something like the image below, which is still a work in progress:

pin

This new layout will be available soon (once bugs are worked out) for Aplite users of the existing BBC News Headlines watchapp, in color on Basalt for those with Pebble Time watches (admittedly few right now!). Stay tuned!

Advertisements
13 comments
  1. Paul said:

    Hi there. I don’t know if you remember me I contacted you about increasing the text size for your BBC app. Which you did very quickly. So thanks again

    I saw what you have been experimenting on, bringing timeline to your BBC app ready for the pebble time. So just wanted to share my feedback.

    Think the BBC colour scheme of red and white works really well. makes it very easy to read

    I was wondering how often you are seeing the timeline being updated from the BBC rss feed? As it could get information overload. At the moment I get notifications from the BBC iPhone app of big stores and the frequency feels about right (don’t know if these are tagged so that you could see them for the timeline). Then I use your pebble app to look at stories while I am on the go.

    The beauty of your timeline solution would be that I get the best of both worlds. I.e. If I see a big story notification on the timeline I can then read the story via your pebble app straight from the notification.

    Also I was wondering if it is possible to get the whole headlines in the timeline? I.e. In your example have the full text rather than concatenated after’denies’ or is there a constraint with the OS?

    I would just like to say thanks for all your handiwork. I use your app on a daily basis and I’m really impressed with what you are developing for the pebble time. Hope you don’t mind me sending my thoughts

    • bonsitm said:

      Hi Paul, thanks for the feedback! At the moment, they user will see timeline pins for all headlines stories passively, and notifications when they are first published.

      It is possible to get the title and description in a timeline pin (which was the first implementation), but this produces a lot of scrolling for something that is supposed to be a prompt for further action. Hence it opens the app to view the full description. This seems better in my eyes, but I am open to other suggestions.

      • Paul said:

        Thanks for coming back to me.
        I didn’t realise that the full text would mean scrolling. I agree thats not an ideal solution. I wonder if just two lines is the optimum level. So that the user can see if they wish to open the the link without lots of scrolling?

      • bonsitm said:

        So two lines of the story content in addition to the title?

      • Paul said:

        No sorry. when I said two lines I meant just for the title, as this should give enough words to know what it relates to without having to scroll lots of text.

        Following your question back I have re-read your original response and I notice that when you referred to lots of scrolling it was because you had shown the title AND the news article. Is my understanding correct? So what does it look like when it is just the title on timeline? Does that still mean lots of scrolling?
        Personally I find the title that you use in your existing pebble app on the main menu is normally two rows in length and contains enough information to know if I want to click on the news story to get more detail.

        Hope that helps. Thanks again

    • Carl said:

      Hey Christ,
      I was wondering: Since you fetch the RSS once every 30 minutes, how will it look if a user gets two pins at the same time? (I mean, it possibly could happen right? A living RSS feed of news would update a couple of times per hour).

      • bonsitm said:

        The createNotifications simply stack, like current notifications.

  2. Carl said:

    Ah, makes sense! Never seen anything about it in the documentation (as far as I can remember, anyway :- )

    Since you are “in the know”, how would setting a future event work on the timeline? I mean, if I just send the start time as, IE, 2015-04-23T18:54:43Z, how will the Pebble know when that is for a specific user? Or am I missing something that is right under my nose yet again? 🙂

  3. Carl said:

    Don’t tell me! I just came to the great conclusion to Google it. It seems like the “Z” stands for zero offset to UTC time. So, basically, I send the UTC time of the event and the Pebble figures out when that is for a perticular user?

    • bonsitm said:

      You are correct! Pebble knows about timezones 😉

  4. Hi, Chris

    Can you open-source this project and put it up on Github? I want to do something similar with a local tech site here in Africa (HTXT.co.za instead of BBC)

    Kind Regards.
    somejuan

    [ Btw. Major props to getting it to set the app to set News Categories. The app in general is a really great implementation of the timeline os (SDK 3) and the use of pins.]

    • bonsitm said:

      Hi Juan, I do plan to open source the app soon (when I find time), but it needs a lot of cleaning up before I do. In the meantime, there is an older version available on GitHub here, which was the app before I began updating for Pebble Time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: