Skip to content

Commit

Permalink
Update 20201130
Browse files Browse the repository at this point in the history
  • Loading branch information
rokudevtools committed Nov 30, 2020
1 parent c77bf37 commit 3652b53
Show file tree
Hide file tree
Showing 623 changed files with 45,358 additions and 7,443 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Scene Graph Developer Extensions

> 08.28.20
> 11.30.20
## v.2.5

### Features

* EntitlementView now supports upgrade/downgrade functionality for RokuPay
* All new audio UX in MediaView
* Improved performance of MediaView in audio mode
* Developers can now add buttons to MediaView in audio mode
* The system screensaver will now work with MediaView in audio mode
* Added new theming options for MediaView in audio mode
* Added a new field enableTrickplay on MediaView

### Bug Fixes

* Fixed an issue in MediaView where bookmarks might be deleted unexpectedly when using the seek field

## v.2.4

Expand Down
330 changes: 279 additions & 51 deletions documentation/1-components.md

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions documentation/2-Contenthandlers_Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,26 @@ Manager with config data to drive that view's Content Getter(s).

# Content Handlers

## Concept

Content Handlers (CH) are responsible for all content loading tasks in
SGDEX. Channels typically extend ContentHandler to implement specific
content or data loading tasks. ContentHandlers should implement a
function called GetContent(). This function is called by SGDEX, allowing
the handler to make application specific API calls or perform other
operations to load the content associated with the handler.

CHs modify the Content Node referenced by m.top.content to get the
CHs modify the ContentNode referenced by m.top.content to get the
content.

A Content Handler is made up of two components; Markup, and
## Limitations

- It is important to remember that SGDEX will **not** invoke any CHs while the
screensaver is active.

## Structure

A ContentHandler is made up of two pieces; Markup, and
BrightScript. A very simple CH might look like the following:

**Markup** (SimpleContentGetter.xml)
Expand Down
2 changes: 2 additions & 0 deletions extensions/SGDEX/ComponentController/ComponentController.brs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ function Show(config as Object)
else if cmType <> invalid and GetInterface(cmType, "ifString") <> invalid and cmTypesSupported[cmType] <> invalid
contentManager = CreateObject("roSgNode", cmTypesSupported[cmType]["nodeType"])
contentManager.configFieldName = cmTypesSupported[cmType]["configName"]
else if cmType <> invalid and cmTypesSupported[cmType] = invalid
print "[SGDEX] Content Manager was not created. Please specify correct value for contentManagerType view interface."
else
print "[SGDEX] Content Manager was not created"
end if
Expand Down
2 changes: 1 addition & 1 deletion extensions/SGDEX/Handlers/BookmarksHandler.brs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Sub bookmarksHandler()
lastState = data
else if field = "position" then
if data < minBookmark OR data > duration - maxBookmark then
if not bookmarkAlreadyRemoved then
if duration > 0 and not bookmarkAlreadyRemoved then
m.top.position = 0
RemoveBookmark()
lastSavedPosition = 0
Expand Down
104 changes: 49 additions & 55 deletions extensions/SGDEX/Handlers/EntitlementHandler.brs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,7 @@

sub Init()
m.top.observeField("state", "OnStateChange")

' get entitlements config params
m.config = {}
ConfigureEntitlements(m.config)

' keep mode value casted to a String for further calling
' mode-specific internal functions
m.sMode = Box(m.config.mode).ToStr()

' build internal functionality for each specific mode (by mode prefix)
' to be called as m.funcAA[m.sMode][fname]()
m.funcAA = {
RokuBilling: {
Init: RokuBilling__Init
SilentCheckEntitlement: RokuBilling__SilentCheckEntitlement
RunEntitlementFlow: RokuBilling__Subscribe
}
UserPass: {
SilentCheckAuthentication: UserPass__SilentCheckAuthentication
SilentDeAuthenticate: UserPass__SilentDeAuthenticate
RunEntitlementFlow: UserPass__Authenticate
}
}

' default mode should be "RokuBilling" (if mode = invalid)
m.funcAA.invalid = m.funcAA.RokuBilling

' call mode-specific init function if exists
CallModeFunc("Init")
m.top.functionName = "HandlerFunctionRunner"
end sub

' "state" interface callback
Expand Down Expand Up @@ -65,21 +37,25 @@ end sub
' To override by end developer
'------------------------------------------------------------------------------

' Allows end developer to configure entitlements.
' Developer should override this subroutine in their
' component extended from EntitlementHandler
' @param config [AA] should contain fields:
' @param config.mode [String] the desired mode: "RokuBilling" (default) or "UserPass"
' @param config.products [Array] of AAs (only for "RokuBilling" mode):
' @param config.products[i].code [String] product code in ChannelStore
' @param config.products[i].hasTrial [Boolean] true if product has trial period
' Required for "RokuBilling" mode. Developer should override this subroutine
' in their component extended from EntitlementHandler to be able to
' - determine user subscription status
' - define RokuPay products to be displayed/offered to the user for the subscription flow
' @param config [AA]:
' @param config.catalogProducts [Object][Read-Only] in "RokuBilling" mode - prepopulated with array of catalog products per roChannelStore.GetCatalog(); otherwise invalid
' @param config.purchases [Object][Read-Only] in "RokuBilling" mode - prepopulated with array of purchases per roChannelStore.GetPurchases(); otherwise invalid
' @param config.isSubscribed [Boolean][Write-Only] for "RokuBilling" silentCheckEntitlement only - developer must specify true if user has active subscription, false otherwise
' @param config.displayProducts [Array][Write-Only] for "RokuBilling" subsciption flow only - developer must specify the list of products to be displayed to the user
' @param config.displayProducts[i].code [String] product code
' @param config.displayProducts[i].name [String] optional, product display name; if not specified, SGDEX will use the Channel Store data
' @param config.displayProducts[i].action [String] optional, product action for on-device upgrade/downgrade: "upgrade" or "downgrade", case insensitive; if not specified, SGDEX assumes regular purchase
sub ConfigureEntitlements(config as Object)
? "SGDEX: you should implement "
? " sub ConfigureEntitlements(config as Object)"
? " in your "m.top.Subtype()" component"
end sub

' For "RokuBilling" mode only. Allows end developer to inject some suctom logic on
' For "RokuBilling" mode only. Allows developer to inject some business logic on
' purchase success by overriding this subroutine in their component extended from
' EntitlementHandler. Default implementation does nothing
sub OnPurchaseSuccess(transactionData as Object)
Expand Down Expand Up @@ -145,23 +121,41 @@ end function
' Handler functions invoked by EntitlementView
'------------------------------------------------------------------------------

' Initiates silent entitlement checking (no UI)
sub SilentCheckEntitlement()
CallModeFunc("SilentCheckEntitlement")
end sub
sub HandlerFunctionRunner()
' entitlements config params
m.config = {}

' Initiates silent authentication checking (no UI)
sub SilentCheckAuthentication()
CallModeFunc("SilentCheckAuthentication")
end sub
' keep mode value casted to a String for further calling
' mode-specific internal functions
m.sMode = m.top.view.mode
m.isNewFlow = (m.sMode.Len() > 0)
if m.isNewFlow = false
? "SGDEX warning: your ("m.top.view.Subtype()") doesn't specify mode."
? "Using fallback flow with getting mode from ConfigureEntitlements config"
ConfigureEntitlements(m.config)
m.sMode = Box(m.config.mode).ToStr()
end if

' Initiates silent de-authentication (no UI)
sub SilentDeAuthenticate()
CallModeFunc("SilentDeAuthenticate")
end sub
' build internal functionality for each specific mode (by mode prefix)
' to be called as m.funcAA[m.sMode][fname]()
m.funcAA = {
RokuBilling: {
Init: RokuBilling__Init
SilentCheckEntitlement: RokuBilling__SilentCheckEntitlement
RunEntitlementFlow: RokuBilling__Subscribe
}
UserPass: {
SilentCheckAuthentication: UserPass__SilentCheckAuthentication
SilentDeAuthenticate: UserPass__SilentDeAuthenticate
RunEntitlementFlow: UserPass__Authenticate
}
}

' Initiates entitlement flow - subscription or authentication, depenging
' on "config.mode" specified (see ConfigureEntitlements)
sub RunEntitlementFlow()
CallModeFunc("RunEntitlementFlow")
end sub
' default mode should be "RokuBilling" (if mode = invalid)
m.funcAA.invalid = m.funcAA.RokuBilling
' call mode-specific init function if exists
CallModeFunc("Init")

' call flow start function
CallModeFunc(m.top.fn)
end sub
97 changes: 71 additions & 26 deletions extensions/SGDEX/Handlers/EntitlementHandler.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,78 @@

<!--
@Public
developer should implement own Handler in channel that extends EntitlementHandler
In this handler developer can override 2 function:
- ConfigureEntitlements(config) [Required]
- OnPurchaseSuccess(transactionData) [Optional]
In ConfigureEntitlements developer can update config with his own params:
config should contain fields:
config.mode [String] the desired mode. If mode isn't specified then it defaults to "RokuBilling" for backward compatibility.
Supported values are:
"RokuBilling" – SVOD Roku Billing;
"UserPass" – username/password authentication.
When config.mode = "RokuBilling" or not specified:
config.products [Array] product configuration as an array of AAs:
config.products[i].code [String] product code in ChannelStore
config.products[i].hasTrial [Boolean] true if product has trial period
OnPurchaseSuccess is a callback that allows end developer to inject some suctom logic on purchase success
by overriding this subroutine. Default implementation does nothing.
@Sample:
// [In <component name="HandlerEntitlement" extends="EntitlementHandler"> in channel]
In order to use EntitlementView in the channel app, you need to implement your handler extended from EntitlementHandler.
EntitlementHandler allows developer to implement their business logic for
- handling RokuPay subscription (Roku Billing)
- handling username/password authentication
depending on EntitlementView.mode.
In EntitlementView.mode = "RokuBilling", developer is able to override the following functions:
- sub ConfigureEntitlements(config as Object) [Required]
- sub OnPurchaseSuccess(transactionData as Object) [Optional]
"RokuBilling" mode now supports not only regular RokuPay purchase but also [on-device upgrade/downgrade](https://developer.roku.com/docs/developer-program/roku-pay/implementation/on-device-upgrade-downgrade.md).
Developer must populate config.catalogProducts in their ConfigureEntitlements(config) implementation to specify products to be offered to the user for purchase or upgrade/downgrade.
sub ConfigureEntitlements(config as Object)
config.products = [
'{code: "PROD1", hasTrial: false}
{code: "PROD2", hasTrial: false}
]
end sub
- provides config object prepopulated with config.catalogProducts and config.purchases
- you must override this function in your handler extended from EntitlementHandler
- config.catalogProducts - contains array of catalog products per roChannelStore.GetCatalog()
- config.purchases - contains array of purchases per roChannelStore.GetPurchases()
- for silentCheckEntitlement flow you must specify config.isSubscribed: true if user has active subscription, false otherwise
- for subscription purchase/upgrade/downgrade flow you must populate config.displayProducts with the list of products to be displayed to the user
Each item in config.displayProducts should be AA containing the following fields
- code [String] product code
- name [String] optional, product display name; if not specified, SGDEX will use the Channel Store data
- action [String] optional, allows to specify product action for on-device upgrade/downgrade; supported values are "upgrade" and "downgrade", case insensitive
If _action_ is set to "upgrade" or "downgrade" then
- SGDEX assumes this is a product for upgrade or downgrade operation, respectively, not for a regular purchase
- if user selects this product then SGDEX initiates upgrade/downgrade operation, essentially subscription change to this product instead of a regular purchase
- on the Channel Store side, developer must add products to be used for upgrade/downgrade [to a product group](https://developer.roku.com/docs/developer-program/roku-pay/quickstart/in-channel-products.md#adding-product-groups) in order to indicate that these are mutually exclusive
if _action_ is not specified or set to some unsupported value then SGDEX treats this as a regular purchase product, not for upgrade/downgrade.
sub OnPurchaseSuccess(transactionData as Object)
- if overridden, allows developer to implement some business logic on subscription purchase success
- transactionData - AA containing RokuPay transaction data per Channel Store response
- default implementation does nothing
In EntitlementView.mode = "UserPass", developer is able to override the following functions
- function CheckAuthentication() as Boolean
- function Authenticate(username as String, password as String) as Boolean
- function DeAuthenticate() as Boolean
CheckAuthentication()
- here you should implement the business logic to validate user authentication state (silentCheckAuthentication)
- return value should be true if user is authenticated, false otherwise
Authenticate(username, password)
- here you should implement the business logic for authentication by username and password
- return value should be true if successfully authenticated, false otherwise
DeAuthenticate()
- here you should implement the business logic to de-authenticate user (silentDeAuthenticate)
- return value should indicate result of the operation and be true if successfully de-authenticated, false otherwise
See EntitlementView documentation for usage samples.
-->

<component name="EntitlementHandler" extends="Task" xsi:noNamespaceSchemaLocation="https://devtools.web.roku.com/schema/RokuSceneGraph.xsd">
Expand Down
Loading

0 comments on commit 3652b53

Please sign in to comment.