Localizing Your Roku Channels

It is likely that most of the channels you’ve used on your Roku player use English in their user interfaces. However, support for other languages on Roku devices has been steadily improving. Therefore, as a channel developer, it is increasingly important that you know how to build your channels to support a variety of languages. Currently, Roku supports English of course, as well as French (as of firmware version 4.6) and Spanish and German (added in firmware version 4.8.) This article discusses the key steps needed to create multilanguage channels.

Locales on Roku

A localized channel typically contains language or culture specific text and images (such as locally relevant icons.) A localized channel may further contain audio files in the local language. BrightScript makes it easy to include any language specific assets you like as part of your channel. If packaged using the techniques described in this article, the correct assets are loaded and rendered based on the Roku device’s language setting. Furthermore, you can publish one package to the Channel Store that supports all of your languages, instead of one package per language.

Roku channels expect to find locale specific assets in the locale directory hierarchy, similar to the one shown below:

Under the locale folder, you define subfolders for each of the languages supported by your channel. In this example, the channel supports English and Spanish. Accordingly, there is an en_US folder for English and an es_ES folder for Spanish. Each of these locale folders can then contain whatever additional subfolders and files are needed to hold all of the language specific channel assets. For example, icons containing Spanish text could go in an es_ES/images subfolder, or Spanish audio files could go in a folder named es_ES/audio. When your channel runs and makes requests for localized resources, the Roku device will look in the locale folder hierarchy associated with the current language setting on the device. Thus, if the device is configured for Spanish, resource API calls will look in folders under the pkg:/locale/es_ES path. We will take a look at the various localization APIs shortly.

Your channel can also define a default locale folder under locale/default. This folder can be used just like a locale folder, except that it is used to store assets that are common to all locales supported by the channel. For example, if you have a logo or other branding that is common across all languages, those assets can be kept in the default locale folder structure.

Two other files that can be in a locale hierarchy are manifest and translations.xml. The manifest file plays the same role as the main manifest file, specifying the channel title, subtitle, focus and side logos, and the like. The difference is that the values in the locale specific manifest files are used to define these properties for the corresponding language. It is also important to keep in mind that manifest files are additive. The Roku device will parse the top level manifest file, and then parse the locale specific manifest, adding properties from the latter to those found in the top level file. Therefore manifest properties that are locale independent, such as version numbers, should be stored in the top level manifest file.

The second file, translations.xml, is an XLIFF compliant localization file that contains translations of text strings for the associated locale. As an example, this is what a German translations.xml file might look like:

Each element contains a source and a target. The source string is referenced in your BrightScript code to identify a localized string to load into your channel. The target string is the localized version of the source string. The source string is typically the default locale (such as English) version of the string in question. The source is mapped to the target using the BrightScript global function tr():

tr(String source) as String

BrightScript will look up the source string in the translations.xml file that corresponds to the Roku device’s current language settings, and return the associated target string.  For example, if the device’s language is set to German, the following code would return the string “Verlassen Video”:

REM This call will return “Verlassen Video”
str = tr(“Exit Video”)

Loading Non-String Resources in a Localized Channel

Previously we saw how localized strings are loaded into a channel using the tr() command.  But what about other resources such as images?  For this there is a BrightScript component called roLocalization.  The main method defined by this component for loading resources is GetLocalizedAsset:

GetLocalizedAsset(String dirName, String fileName) as String

This function takes two string arguments.  The first is the name of the directory in which the file specified by the second argument is located.  The directory name is relative to the corresponding locale folder.  As an example, let’s assume that the Roku device language is set to French.  Further, let’s assume that you want to load an image file called playbutton.png located in your locale/fr_CA/images folder.  Since the Roku device knows that the current language is set to French, you don’t need to specify locale/fr_CA as part of the directory name.  The device knows to look there for the resource.  So you would call GetLocalizedAsset as follows:

path_to_image = GetLocalizedAsset(“images”, “playbutton.png”)

After this call, path_to_image will contain the full path to the image, specifically:

pkg://locale/fr_CA/images/playbutton.png

Your BrightScript code can then use this return value to load the image into any UI component.  To make using GetLocalizedAsset easy, images or other assets in your channel should use the same file name for each locale version.  In the previous example, each version of the image file should be named playbutton.png.  This allows you to make one GetLocalizedAsset call to load the image independent of the Roku device language settings.

When searching for assets, GetLocalizedAsset first looks in the current locale folder structure.  If the specified file is not found, the API will search the locale/default folder.  If the file is not there, it looks next in the locale/en_US folder.  If the asset is not found there, GetLocalizedAsset returns an empty string.

To help you dig deeper into Roku channel localization, I’ve updated the Channel Diner sample application from my last post.  The diner now has three cuisines: Mexican, French, and good old classic American food.  To see the various options, set your Roku device language to English, French, or Spanish and have fun!
Sample Localization Channel

Posted in Uncategorized | 6 Comments

Using the Enhanced List Screen

Now that you’ve had some time to write some BrightScript code with the recent 4.8 software release for Roku 2, Roku LT and Roku HD players, it’s time to look at some other new features in the platform.  This time around, let’s look at what’s new in the list screen component.

With the 4.8 firmware release, the list screen now supports breadcrumb text as well as mini icons for list items.  Implementing breadcrumbs in your list screens is done exactly the same way as with other supporting components:

SetBreadcrumbText(String breadcrumb1, String breadcrumb2) As Void

This can be very handy, for example, when you want to update the breadcrumb text to reflect the current selection or focus in your lists.  The event loop in your screen code can respond to the various list item events and set the breadcrumb text accordingly:

    while (true)
        msg = wait(0, port)
        if (type(msg) = "roListScreenEvent")
            if (msg.isListItemFocused())
                screen.SetBreadcrumbText("Menu", “Index “ + Stri(msg.GetIndex()))
            endif
        endif
    end while

The next new feature is the ability to add icons to your list elements.  This lets you add a bit of graphical context to each of the items in a list.  As an example, take a look at the screen shot below from the sample application included with this article:

Each of the list items has a small graphic to the right.  To add graphics like this to a list, all you need to do is specify SDSmallIconUrl and HDSmallIconUrl attributes to the list items in the content list.

    contentList = [
        {
            Title: "Breakfast",
            ID: "1",
            SDSmallIconUrl: "pkg:/images/breakfast_small.png",
            HDSmallIconUrl: "pkg:/images/breakfast_small.png",
            ShortDescriptionLine1: "Breakfast Menu",
            ShortDescriptionLine2: "Select from our award winning offerings"
        },
        ...
    ]
    list = CreateObject("roListScreen")
    list.SetContent(contentList)

In my case, I used 56×56 icons for my list items.  In reality, the images you use can be any size you like, up to the dimensions of a list item.  Using the proper size image would let you create list items that are entirely graphical instead of text.

Another feature available to you in the list screen is the ability to set the screen background image that gets displayed when a list item is focused. That’s how the pancakes in the screenshot above got rendered, for example. Although this feature is not new to the 4.8 firmware release, it is worth highlighting here since it’s not a well known list screen capability. To specify a background image for a list item, include HDBackgroundImageUrl and SDBackgroundImageUrl values.

    contentList = [
        {
            Title: "Breakfast",
            ID: "1",
            SDSmallIconUrl: "pkg:/images/breakfast_small.png",
            HDSmallIconUrl: "pkg:/images/breakfast_small.png",
            HDBackgroundImageUrl: "pkg:/images/breakfast_large.png",
            SDBackgroundImageUrl: "pkg:/images/breakfast_large.png",
            ShortDescriptionLine1: "Breakfast Menu",
            ShortDescriptionLine2: "Select from our award winning offerings"
        },
       ...
     ]

Keep in mind that the various list item attributes can be set for all list items, or for any combination of the list items in your list screens. This gives you a lot of flexibility in how you design your screens.

If you’d like to experiment more with the new list screen features, you can download the full sample application from the link below as a starting point. Good luck!
List Screen Sample

Posted in Uncategorized | 6 Comments

JSON Support Comes to Roku

With the 4.8 software release for Roku 2, Roku LT and Roku HD players, developers now have access to native JSON parsing support from BrightScript.  Roku channel developers no longer need to integrate third party solutions or worse, roll their own parsing solutions anymore.  Developers can easily consume JSON based feeds or REST API response payloads with one simple API call.

To parse JSON from BrightScript, simply call the new ParseJSON API:

ParseJSON(jsonString As String) As Object

ParseJSON takes a single string argument that contains the JSON content that you want to parse.  This can come from any source, such as a text file installed as part of your channel or the response from an HTTP request executed with an roUrlTransfer object.  If the string contains valid, well-formed JSON, the object returned is an associative array representation of the parsed JSON.

For example, let’s assume that the string jsonAsString contains the following JSON content:

{
 "Videos" : [
  {
   "Title" : "Jeff Han Demos his Breakthrough Touchscreen",
   "Image" : "http://rokudev.roku.com/rokudev/examples/videoplayer/images/JeffHan.jpg",
   "Url" : "http://video.ted.com/talks/podcast/JeffHan_2006_480.mp4"
 },
 {
   "Title" : "David Kelley on Human-Centered Design",
   "Image" : "http://rokudev.roku.com/rokudev/examples/videoplayer/images/DavidKelley.jpg",
   "Url" : "http://video.ted.com/talks/podcast/DavidKelley_2002_480.mp4"
  },
  …
 ]
}

A call to ParseJSON would then return an object that can be used like this:

json = ParseJSON(jsonAsString)
for each video in json.Videos
  print video.Title; video.Image; video.Url
end for

To help get you started using Roku’s new JSON support, I’ve built a short sample application that demonstrates how to parse a JSON text file which populates the content in a simple poster screen style UI.  This application also provides an XML driven version of the same functionality so that you can compare JSON parsing to XML parsing in one convenient sample. The sample can be found here:

Download BrightScript JSON Parsing Sample

More details on the new JSON API can be found in the Roku SDK documentation:
More on the JSON API

Posted in Uncategorized | 7 Comments

Startup Splash Screens

Starting with the forthcoming 4.3 software release, we will support (and strongly encourage) splash screens for Roku Channels. These splash screens are shown immediately when you channel launches, before any code is executed, so the user gets immediate feedback.

To add the splash screen, add three new lines into your manifest file:

splash_screen_hd=pkg:/images/<hd splash screen image>
splash_screen_sd=pkg:/images/<sd splash screen image>
splash_color=#rrggbb

The images you specify will be centered in the screen over the background color specified by splash_color. An easy way to get going is to simply use your current homescreen icons, so these lines might look like this:

splash_screen_hd=pkg:/images/mm_icon_focus_hd.png
splash_screen_sd=pkg:/images/mm_icon_focus_sd.png
splash_color=#062365

A couple of considerations for creating and using splash screen images:

  • images must be in your channel package, they can’t be remotely hosted.
  • consider using fairly small images to keep your package size down. Larger packages may not fit on some user’s Roku players if they have a large number of channels installed, and if your package is over 750kb, it may be limited to Roku 2 units only.

While this requires version 4.3 or later, earlier software releases will ignore these new items in the manifest and function correctly, though without the splash screen.

Let us know if you have any questions

Posted in Uncategorized | 14 Comments

International Channel Stores

This week, we began shipping Roku 2 XS and Roku LT units in the UK and Republic of Ireland. To support this international launch, we’ve created separate Channel Stores for each region. What was before the only Channel Store is now shown as the US Channel Store on our developer site. We’ve also added UK, Ireland and Rest of World (RoW) Channel Stores. Users will be assigned to the appropriate channel store based on their location at the time of account setup. Developers must to opt-in to each region in which they want their channels available. All channels that were available original Channel Store have been automatically placed in the US Channel Store, so there is nothing you need to do if you want your channel to continue to appear only in the United States. If you wish your channels to be available in additional regions, you’ll need to log into your developer account and submit a new version of the channel that adds the additional regions (Instructions below).

There are some important considerations for making your channel available in the international Channel Stores:

  • Do you have rights to your content for distribution in that region? Many licenses restrict the geographies and delivery methods for the content.
  • Do you have an addressable audience in the region? You will have to pay for delivery of this content, even if it’s not your ideal audience.
  • Are you able to monetize your content in the region?
  • Is your channel geographically aware? Roku doesn’t do any geographic IP filtering except for directing users to the appropriate channel store. If you need to filter your content based on geography, this functionality will need to be added to your channel.
  • Do you have appropriate and sufficient delivery capabilities in the region? Depending on usage, you may need in-region CDNs or datacenters to serve these international users.

While we want the widest selection of content available in each region, we urge developers and publishers to consider carefully the impact to their business when deciding to submit their channels for additional regions. It should be noted that Roku reserves the right to choose which channels it publishes in which regions and may not publish a channel in each region the developer chooses.

How to Make Your Channel Available in other Regions
If your channel is an existing channel, you’ll need to log into your developer account, create a new version (by clicking “Edit Channel”), add your channel package (it can be the same package currently published), then click edit to choose the appropriate channel stores from the drop-down list. Submit the channel update to start the publication process. For categories in the new regions, please choose the most appropriate ones. We will be reviewing and revising channel categorization before launch.

If your channel is a new channel, you’ll pick the Channel Store(s) at the time of channel creation.

Let us know if you have any questions.

Posted in Uncategorized | 2 Comments

Back in the saddle

Apologies for the dearth of recent posts. As you may have heard, we have been pretty busy recently. We will be working on that. To recap recent Roku happenings:

  • We ended 2011 with more than 400 channels published in the Channel Store. This is up from 135 at the beginning of 2011.
  • We announced the forthcoming Streaming Stick just prior to CES. This product, due in the second half of this year, takes all the functionality of the Roku2 and places it in a package the size of a pack of chewing gum. The Streaming Stick will be compatible with all TV’s that implement the new MHL port, which is being added to a number of TV models this year . Our first announced partner is Best Buy’s Insignia brand. The Streaming Stick will automatically turn an MHL-enabled TV into a “Smart TV’”, and the in the future a user can upgrade their TV again just by buying a new Streaming Stick. From a developer standpoint, there should not be anything specific you will need to do to make your channel Streaming Stick compatible; if it runs on Roku2, it should run on the Streaming Stick. For more information, please see our press release.
  • We announced, and are now shipping in  the UK and Ireland. More on that in the next post.
We expect 2012 to be a busy year and look forward to seeing what developers continue to submit to the channel store.
Posted in Uncategorized | 1 Comment

Welcome Joel Braverman

I wanted to take a minute to welcome the newest member of the Roku Developer Program, Joel Braverman.  Joel will be working with developers to help get channels published, provide useful feedback and guidance for best practices and improve our sample code.

Over the past year or so, Joel has been a prolific Roku developer, publishing a number of channels including Abacus.fm, Musiclouds, and ScreenShades.  You’ll also see Joel active on our forums, lending a hand.

 

Welcome Joel.

Posted in Uncategorized | 8 Comments

Using Launch Parameters

Launch parameters are chunks of information that can be sent to your channel by devices using the Roku External Control Protocol (ECP). ECP is a RESTful API that allows other network connected devices to control a Roku. ECP is documented in the Roku SDK.

When a device using ECP launches a channel, it can also send a collection of parameters to that channel. This ability presents endless possibilities to channel developers. In this article, we’ll explore some of the simplest applications of this mechanism.

Hello World!
Launch parameters are passed to your Main() function as a parameter whose value is a roAssociativeArray. The simplest thing we can do with a launch parameter is display its value directly to the user, so let’s write a very simple channel that does exactly that. First, we need to check for the existence of a launch parameter named message. If that parameter exists, we’ll display it to the user.

sub Main(launchParameters)
  ' setup a poster screen and assign it a message port
  port = CreateObject("roMessagePort")
  screen = CreateObject("roPosterScreen")
  screen.SetMessagePort(port)
  screen.Show()
  
  ' set the default message
  message = "No message received"
  ' check to see if a message was passed in
  if launchParameters.message <> invalid
    message = launchParameters.message
  end if
  
  ' display the message
  screen.ShowMessage(message)
  
  while true
    ' wait for an event from our poster screen
    msg = Wait(0, port)
    
    if type(msg) = "roPosterScreenEvent"
      if msg.isScreenClosed()
        exit while
      end if
    end if
  end while
  
  screen.Close()
end sub

If you side-load this channel and launch it from the Roku home screen using the standard remote control, you will see “No message received” displayed on the poster screen. In order to make use of our launch parameter, we need to launch the channel using ECP. You can do this from your PC using curl and replacing the x’s below with the local IP address of your Roku. Note that the command must be sent as an HTTP POST request and the launch parameters must be sent as URL parameters. It’s also important that the parameter values be properly encoded, otherwise the ECP call will fail and the channel will not launch.

curl -d "" "http://xxx.xxx.xxx.xxx:8060/launch/dev?message=Hello%20World%21"

If this command succeeds, your side-loaded channel will launch and “Hello World!” will be displayed on your poster screen. Try it with some other values for the message parameter.

Play a Video
Displaying a message is a simple way of demonstrating how launch parameters work, but it isn’t especially useful. Suppose we wanted to pass the URL of a piece of content into a channel and have the channel play that content. The first thing we would need is a simple channel that could accept a launch parameter whose value was a URL and use that URL to build a content-meta-data structure that could then be fed into a roVideoScreen. That might look something like this.

sub Main(launchParameters)
  ' don't do anything unless we got a url
  if launchParameters.url <> invalid
    ' setup a poster screen and assign it a message port
    port = CreateObject("roMessagePort")
    screen = CreateObject("roVideoScreen")
    screen.SetMessagePort(port)

    ' build a content-meta-data using the passed in URL   
    screen.SetContent({
      stream: { url: launchParameters.url }
    })

    ' play the video
    screen.Show()
    
    while true
      ' wait for an event from our video screen
      msg = Wait(0, port)
      
      if type(msg) = "roVideoScreenEvent"
        if msg.isScreenClosed()
          exit while
        end if
      end if
    end while
    
    screen.Close()
  end if
end sub

Once you side-load this channel, you can launch it using ECP the same way we did our first channel, but this time you would need to send it a parameter called url whose value is the URL to a compatible mp4 video. You could do that with a command similar to this. Again, notice that the launch parameter must be properly URL encoded for the command to succeed.

curl -d "" "http://xxx.xxx.xxx.xxx:8060/launch/dev?url=http%3A%2F%2Fvideo.ted.com%2Ftalks%2Fpodcast%2FDavidBrooks_2011.mp4"

If the command succeeds, your side-loaded channel will launch and begin playing the video referenced by the URL.

Using this simple channel as a starting point, you could add support for additional video and audio content by adding a second launch parameter to specify the type of content that the URL refers to. Or, instead of a single video, you could pass in the URL of an MRSS feed and display all the content contained in that feed.

And if you’re working with a content provider’s API, you can use launch parameters to pass in search terms or content identifiers, or other data that can be used in conjunction with that particular API to get the user to compelling content quickly.

Posted in Uncategorized | 3 Comments

Logging Into an Online Account

In a previous post, we learned how to associate a channel with an online service using rendezvous style linking. In this post, we’ll associate a channel with an online service by logging in directly with a username and password. Any time you send sensitive information over the internet, it’s important to make sure that information is secure. Be sure you take the necessary precautions to protect your users’ data.

We’re going to ask the user to enter two chunks of information, a username and a password. We will be using the roKeyboardScreen component for both of these tasks, so the first thing we need is a reusable function that presents a roKeyboardScreen and returns whatever string the user types into it.

function ShowKeyboardScreen(prompt = "", secure = false)
  result = ""

  ' create a roKeyboardScreen and assign a message port to it
  port = CreateObject("roMessagePort")
  screen = CreateObject("roKeyboardScreen")
  screen.SetMessagePort(port)

  ' display a short string telling the user what they need to enter
  screen.SetDisplayText(prompt)

  ' add some buttons
  screen.AddButton(1, "Okay")
  screen.AddButton(2, "Cancel")

  ' if secure is true, the typed text will be obscured on the screen
  ' this is useful when the user is entering a password
  screen.SetSecureText(secure)

  ' display our keyboard screen
  screen.Show()

  while true
    ' wait for an event from the screen
    msg = wait(0, port)

    if type(msg) = "roKeyboardScreenEvent" then
      if msg.isScreenClosed() then
        exit while
      else if msg.isButtonPressed()
        if msg.GetIndex() = 1
          ' the user pressed the Okay button
          ' close the screen and return the text they entered
          result = screen.GetText()
          exit while
        else if msg.GetIndex() = 2
          ' the user pressed the Cancel button
          ' close the screen and return an empty string
          result = ""
          exit while
        end if
      end if
    end if
  end while

  screen.Close()
  return result
end function

Now we can use our keyboard screen to solicit information from the user. Notice that the second call to ShowKeyboardScreen() has two arguments. Passing a true value as the second argument will cause the text to be obscured on the screen as the user is typing their password.

username = ShowKeyboardScreen("Enter your username")
if username <> ""
  ' only prompt the user for a password if they entered a username
  password = ShowKeyboardScreen("Enter your username", true)
end if

Once you have the user’s credentials, it is safe to store them in the device registry because all data stored in the registry is encrypted, Access to registry data is tied to the developer key used to package your channel, so make sure future updates to your channel are packaged with the same key. Otherwise your channel will lose access to any data stored in the registry and your users will have to go through the login process again. It is also important to protect the key and its associated password appropriately.

Now you can use the credentials to make an API call to your cloud service and authenticate the channel to the user’s account. Because we’re sending sensitive data, we’ll use SSL to encrypt the data when making the transfer. For purposes of this example, we’ll assume that the API will return a chunk of XML containing a token that should be used when making subsequent API calls.

if username <> "" and password <> ""
  ' the user entered both a username and a password
  ' store them in the registry for logging in during future sessions
  sec = CreateObject("roRegistrySection", "mySection")
  sec.Write("username", username)
  sec.Write("password", password)
  sec.Flush()

  ' and now let's try logging in
  xfer = CreateObject("roURLTransfer")
  ' setup the transfer for SSL
  xfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
  xfer.InitClientCertificates()
  xfer.SetURL("https://api.example.com/login?username=" + xfer.Escape(username) + "&password=" + xfer.Escape(password))
  response = xfer.GetToString()

  ' parse the response and extract the apiToken
  xml = CreateObject("roXMLElement")
  if xml.Parse(response)
    apiToken = xml.token.GetText()
  end if
end if

Once the channel is associated with the user’s account, you can give them a personalized experience within your channel.

Posted in sdk | 5 Comments

Linking a Channel to an Online Account

There are several different ways of associating a channel with an online account. In this tutorial, we will explore rendezvous style registration where a channel presents a code to the user and asks them to go to a computer and enter that code into a web site to complete the registration process. This linking method is documented in the Device Registration and Linking document in the Roku SDK.

Rendezvous style registration has the benefit of not requiring the user to type information directly into the channel using an on-screen keyboard. The downside is that the user may have to move to a different room to access the website and enter the code. In a future tutorial, we will look at the method where the user directly enters their username and password into the channel.

The first step in a rendezvous style registration is for the channel to request a linking code from your API. When your API receives that request, it should generate a new linking code and store it along with an expiration timestamp that is some reasonable amount of time in the future. Linking codes should be unique enough that no other device will be assigned the same code before the code has expired.

In the simplest case, the API can respond with the newly generated linking code and a timestamp indicating when the code expires. The API response to this request might look like this:

<apiResponse>
  <linkingCode expires="1310598793">ABC123</linkingCode>
</apiResponse>

To make the API request in BrighScript and extract the linking code, your channel would have a function similar to this one:

function GetLinkingCode()
  result = invalid

  xfer = CreateObject("roURLTransfer")
  xfer.SetURL("http://api.example.com/getLinkingCode")
  response = xfer.GetToString()
  xml = CreateObject("roXMLElement")
  if xml.Parse(response)
    result = {
      code: xml.linkingCode.GetText()
      expires: StrToI(xml.linkingCode@expires)
    }
  end if

  return result
end function

When the user validates their linking code, the API should generate a unique token for the device and store that token along with its association to the user’s account. This token will be stored by the channel in the Roku’s registry and used to identify the device to the API during future sessions.

While the registration screen is visible, the channel should periodically poll the API to find out whether the user has validated their linking code and to retrieve the associated token. A successful API response to this request might look like this:

<apiResponse>
  <status>success</status>
  <deviceToken>3F2504E0-4F89-11D3-9A0C-0305E82C3301</deviceToken>
</apiResponse>

The API may also respond to the polling request with a failure status if the user has not yet validated the linking code or the linking code has expired or some other error occurs. That response might look like this:

<apiResponse>
  <status>failure</status>
</apiResponse>

To make this API request in BrightScript and process the result, your channel might implement a function similar to the following. This function will poll the API to check whether the linking code has been validated. If the code has been validated, it saves the associated token in the registry for future use. The function returns a boolean value indicating whether the linking code has been validated.

function ValidateLinkingCode(linkingCode)
  result = false

  xfer = CreateObject("roURLTransfer")
  xfer.SetURL("http://api.example.com/validateLinkingCode?code=" + linkingCode)
  response = xfer.GetToString()
  xml = CreateObject("roXMLElement")
  if xml.Parse(response)
    if UCase(xml.status.GetText()) = "SUCCESS"
      sec = CreateObject("roRegistrySection", "mySection")      
      sec.Write("deviceToken", xml.deviceToken.GetText())
      sec.Flush()

      result = true
    end if
  end if

  return result
end function

With these two functions, you are ready to build your channel’s rendezvous linking experience. This chunk of BrightScript will get a linking code from the API, display it on a roCodeRegistrationScreen, and then start polling the API waiting for the linking code to be validated. Once the code has been validated, the screen will close.

sub ShowLinkScreen()
  dt = CreateObject("roDateTime")

  ' create a roCodeRegistrationScreen and assign it a roMessagePort
  port = CreateObject("roMessagePort")
  screen = CreateObject("roCodeRegistrationScreen")
  screen.SetMessagePort(port)

  ' add some header text
  screen.AddHeaderText("Link Your Account")
  ' add some buttons
  screen.AddButton(1, "Get new code")
  screen.AddButton(2, "Back")
  ' Add a short narrative explaining what this screen is
  screen.AddParagraph("Before you can use this channel, you must link the channel to your account.")
  ' Focal text should give specific instructions to the user
  screen.AddFocalText("Go to http://www.example.com/roku, log into your account, and enter the following code.", "spacing-normal")

  ' display a retrieving message until we get a linking code
  screen.SetRegistrationCode("Retrieving...")
  screen.Show()

  ' get a new code
  linkingCode = GetLinkingCode()
  if linkingCode <> invalid
    screen.SetRegistrationCode(linkingCode.code)
  else
    screen.SetRegistrationCode("Failed to get code...")
  end if
 
  screen.Show()

  while true
    ' we want to poll the API every 15 seconds for validation,
    ' so set a 15000 millisecond timeout on the Wait()
    msg = Wait(15000, screen.GetMessagePort())
   
    if msg = invalid
      ' poll the API for validation
      if ValidateLinkingCode(linkingCode.code)
        ' if validation succeeded, close the screen
        exit while
      end if

      dt.Mark()
      if dt.AsSeconds() > linkingCode.expires
        ' the code expired. display a message, then get a new one
        d = CreateObject("roMessageDialog")
        dPort = CreateObject("roMessagePort")
        d.SetMessagePort(dPort)
        d.SetTitle("Code Expired")
        d.SetText("This code has expired. Press OK to get a new one")
        d.AddButton(1, "OK")
        d.Show()

        Wait(0, dPort)
        d.Close()
        screen.SetRegistrationCode("Retrieving...")
        screen.Show()

        linkingCode = GetLinkingCode()
        if linkingCode <> invalid
          screen.SetRegistrationCode(linkingCode.code)
        else
          screen.SetRegistrationCode("Failed to get code...")
        end if
        screen.Show()
      end if
    else if type(msg) = "roCodeRegistrationScreenEvent"
      if msg.isScreenClosed()
        exit while
      else if msg.isButtonPressed()
        if msg.GetIndex() = 1
          ' the user wants a new code
          code = GetLinkingCode()
          linkingCode = GetLinkingCode()
          if linkingCode <> invalid
            screen.SetRegistrationCode(linkingCode.code)
          else
            screen.SetRegistrationCode("Failed to get code...")
          end if
          screen.Show()
        else if msg.GetIndex() = 2
          ' the user wants to close the screen
          exit while
        end if
      end if
    end if
  end while
 
  screen.Close()
end sub

Once the channel is successfully linked with your API, the device token will be stored in the Roku’s registry. By including the device token in subsequent API requests, your API will be able to associate the channel with the user’s account and provide the channel with appropriate data.

Posted in sdk | 10 Comments