Skip to content

marcnew2101/roku-boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

206 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Roku Boilerplate

A starting point for new Roku applications.

Features

  • Preset globals
  • Theme customization
  • Node management + history + focus
  • BaseScreen with lifecycle hooks (onScreenVisible / onScreenHidden)
  • Centralized constants via Const()
  • Leveled logging via logError / logWarn / logInfo / logDebug
  • Translations
  • Pop-up dialogs
  • Testing (Not yet implemented)

Requirements

Installation

  • This project uses the VSCode IDE along with the BrightScript Language Extension for packaging and deploying builds.
  • Clone the repository to your preferred folder
  • Rename the ".env-sample" file to ".env". This file is located at the root of the project folder.
    • ROKU_HOST = your Roku device IP address
    • ROKU_USERNAME = "rokudev" (default)
    • ROKU_PASSWORD = the password you set when enabling developer mode

Deployment

ZIP file is automatically generated in /out/roku-deploy.zip and sent to your Roku device:

  • Windows, press F5
  • MacOS, press Fn + F5

Console & Debugging

  • The OUTPUT and DEBUG CONSOLE within VSCode will show the app activity as well as any print commands.
    • Enabling (true) "showDeviceInfo" and/or "showAppInfo" at the top of /source/Main.brs will print information about the device to the console.
  • If the app closes, the console/debug session will automatically close

Logging

Use the leveled helpers in /components/utils/Logger.brs instead of inline ? statements:

logError("something failed", "Source.brs")
logWarn("deeplink missing mediaType")
logInfo("App loaded")
logDebug("up key pressed", "LandingScreen.brs")

Output format: [LEVEL] message (Source.brs). The source argument is optional but useful for grep.

Level When printed
logError Always (prefixed with a blank line)
logWarn Always
logInfo Only when m.global.devLogging is true
logDebug Only when m.global.devLogging is true

devLogging is declared at the top of /source/Main.brs alongside showDeviceInfo / showAppInfo / showBandwidth / showHttpErrors. Set it to false for a quieter production build.

Configuration

Globals

Roku uses "m.global" to reference an object as seen in /source/Main.brs

Screenshot

  • Global object values can be accessed from anywhere in the app.
    • m.global.ui could return a value of "HD" or "FHD". This is useful when picking remote image sizes (HD = 1280 x 720, FHD = 1920 x 1080).
    • 4 of the keys (model, os, internet, hdcp) are used with /components/data/requirements.json to determine app eligibility at startup. See Requirements for more info.
    • deeplink is set from the launch args (contentId / mediaType) when present. See getDeepLinks in /source/Main.brs.
    • devLogging controls whether logInfo / logDebug output is printed. See Logging.



Themes

Roku's Scene node exposes a palette field for theming colors in child nodes; this template applies the selected theme onto the scene at startup.

"dark" and "red" is the default theme here if no 2nd argument is provided.

setTheme(true, { "type": "dark", "color": "red" })
  • The palette colors, background colors and selectors can be customized in /components/data/themes.json
    • themes are defined using the following format:
{
   "dark": [
       {
           "color": {}
       }
   ],
   "light": [
       {
           "color": {}
       }
   ]
}
m.label.color = theme().colors.primaryTextColor   ' palette color
m.list.focusBitmapUri = theme().selectorUri       ' scene-level theme field

Palette colors live under theme().colors; scene-level theme fields (backgroundColor, backgroundUri, selectorUri) are at the top level.



Constants

Magic strings and numbers (key codes, file paths, registry section, beacon names, dialog typography) live in /components/utils/Constants.brs as an AA returned by Const():

key = Const().key.back                          ' "back"
ReadAsciiFile(Const().path.messages)            ' "pkg:/components/data/messages.json"
node.signalBeacon(Const().beacon.launchComplete)
fontSize = Const().dialog.messageFontSize       ' 32

Add new groups to Const() rather than introducing new literals in call sites. See /components/utils/Constants.brs for the full table.



Debug Helpers

Dev-time logging via logDebug (suppressed unless m.global.devLogging is true) — call from any screen handler:

  • showAllScreens() — every direct child of HomeScene with its id
  • getHistory() — current screen-stack contents (only populated on HomeScene)
  • showFocus() — root-to-leaf focus chain
  • showTheme() — palette colors + scene-level theme fields



Node Management

Screen Lifecycle Hooks

Any screen that extends BaseScreen must define its own init() and call baseScreenInit() as the first line — that hides the screen until shown and wires the visibility observer. After that, set up node refs and field observers as usual:

sub init()
    baseScreenInit()
    m.myList = m.top.findNode("myList")
    m.myList.observeField("itemSelected", "onItemSelected")
end sub

Two optional hooks fire when the screen's visible field changes — override either by redeclaring it in the screen's .brs:

sub onScreenVisible()
    ' run every time the screen becomes visible — fetch data, set focus, etc.
end sub
sub onScreenHidden()
    ' run every time the screen is hidden — pause animations, cancel requests, etc.
end sub

Each hook is a no-op by default. Implementing one in a subclass overrides the parent's no-op since SceneGraph loads child scripts after the parent's. See /components/screens/landing/LandingScreen.brs for a working example.

BaseScreen also declares a focusedNode field on its interface, used by setFocus(node, saveFocus) to remember which child node was last focused — see Setting screen focus below.

Adding a new node to the root scene node:

Screens are identified by their component name (the XML name attribute, also returned by subType()). Adding a screen whose subtype already exists logs a console error and skips the add — only one instance of a given screen type lives in the scene at a time.

addScreen("screenName")

Additional arguments can be used as follows:

addScreen(
   screenName     ' the name of the node/XML file (also the subtype)
   showScreen     ' sets the node visibility (true by default)
   hidePrevScreen ' hides the previous/current screen (true by default)
   trackInHistory ' adds the node to the history stack (true by default)
)

Finding an existing node:

Looked up by component subtype, walking the immediate children of the scene. Returns invalid (and logs a warning) if not found.

getScreen("screenName")

Additional arguments can be used as follows:

getScreen(
   screenName  ' the component name of the existing screen
   showScreen  ' set the node visibility to true (false by default)
)

Removing a node from the root scene:

Pass the child node directly, or pass the component name of the screen to look up and remove.

removeScreen(node or "screenName")

Additional arguments can be used as follows:

removeScreen(
   screen           ' the child node itself or the component name of the screen
   showPrevScreen   ' shows the previous screen (true by default)
   untrackHistory   ' removes the node from the history stack (true by default)
)

Setting screen focus

Pass the node directly, or pass the component name of the screen to focus.

setFocus(node or "screenName")

Additional arguments can be used as follows:

setFocus(
   node        ' the node itself or the component name of the screen
   saveFocus   ' writes the focused node to m.top's focusedNode field (true by default)
)

focusedNode is declared on the BaseScreen interface. When a BaseScreen-derived screen becomes visible again after being hidden, removeFromStack reads this field and re-focuses the saved node.

Note

Screens extending BaseScreen automatically inherit every utility script (Screens.brs, History.brs, Themes.brs, Messages.brs, Requirements.brs, General.brs, Beacons.brs, Constants.brs, Logger.brs, Debug.brs) via the parent component's <script> tags — you don't need to re-include them. The only place to register new utilities is in /components/screens/base/BaseScreen.xml (and /components/HomeScene.xml if HomeScene also needs the utility).

See /components/screens/landing/LandingScreen.xml for a working example.



Translations

  • The language translation folders are located at /locale
  • Each folder is named according to the locale ID (see Roku localization docs for list of ID's)
  • The files in each folder use the XLIFF format
  • additional folders and translations can be added by following the format above
  • tr("string") is a Roku built-in that resolves the string against the active locale's XLIFF entries; it returns the original string when no translation exists, so it's safe to use everywhere. Plain string literals work too if you don't need translation.



Dialogs & Messages

Show a dialog from a /components/data/messages.json entry by key:

showMessage("message")

Or pass a dialog config object directly to showDialog(config) (see /components/utils/Messages.brs) when the dialog isn't backed by a JSON entry.

Entries in messages.json follow this schema:

{
   "message": {
      "title": "title at top of dialog window",
      "message": "text inside body of dialog window",
      "help": [
         "items shown below the message body",
         "help is optional and can be an empty array if not needed"
      ],
      "buttons": [
         { "label": "OKAY", "exitApp": true },
         { "label": "CANCEL" }
      ],
      "allowBack": set to true to allow pressing the back button on the remote to exit the dialog window
   }
}

Each button is an object with a required label and two optional fields:

  • exitApp: true — sets scene().exitApp when pressed, breaking the main loop in /source/Main.brs
  • onPress: { "node": nodeRef, "func": "funcName" } — invokes nodeRef.callFunc(funcName, { label, index }) when pressed

Buttons without exitApp dismiss the dialog after firing their onPress (if any). See onButtonSelected in /components/screens/dialogs/DialogModal.brs.

Resources

About

Project template for creating a Roku/Brightscript channel

Topics

Resources

License

Stars

12 stars

Watchers

2 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors