diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
new file mode 100644
index 0000000..b857629
--- /dev/null
+++ b/.github/workflows/CI.yaml
@@ -0,0 +1,30 @@
+name: CI PR Checks
+
+on: pull_request
+
+jobs:
+ eslint:
+ name: bslint
+ permissions:
+ actions: write
+ contents: write
+ id-token: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/setup-node@v3
+ with:
+ node-version: "16"
+
+ - name: 'Checkout ${{ inputs.ref }}'
+ uses: actions/checkout@v2
+ with:
+ path: source
+ ref: '${{ inputs.ref }}'
+
+ - name: Install dependencies
+ run: |
+ yarn install
+
+ - name: Run Linter
+ run: |
+ npx bslint
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..687150d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,25 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.3.0
+ hooks:
+ - id: check-added-large-files
+ - id: check-case-conflict
+ - id: check-executables-have-shebangs
+ - id: check-merge-conflict
+ - id: check-symlinks
+ - id: check-toml
+ - id: check-xml
+ - id: check-json
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: fix-byte-order-marker
+ - id: forbid-new-submodules
+ - id: mixed-line-ending
+ args: [--fix=lf]
+ - id: no-commit-to-branch
+ args: [--branch, main, --branch, develop]
+ - id: pretty-format-json
+ args: [--autofix]
+ - id: sort-simple-yaml
+ - id: trailing-whitespace
+ args: [--markdown-linebreak-ext=md]
diff --git a/Demo/.vscode/launch.json b/Demo/.vscode/launch.json
index a930183..e633d42 100644
--- a/Demo/.vscode/launch.json
+++ b/Demo/.vscode/launch.json
@@ -1,16 +1,13 @@
{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "name": "BrightScript Debug: Launch",
- "type": "brightscript",
- "request": "launch",
- "host": "${promptForHost}",
- "password": "${promptForPassword}",
- "rootDir": "${workspaceFolder}"
- }
- ]
+ "configurations": [
+ {
+ "host": "${promptForHost}",
+ "name": "BrightScript Debug: Launch",
+ "password": "${promptForPassword}",
+ "request": "launch",
+ "rootDir": "${workspaceFolder}",
+ "type": "brightscript"
+ }
+ ],
+ "version": "0.2.0"
}
diff --git a/Demo/components/CampaignView.brs b/Demo/components/CampaignView.brs
index fe01f7a..42c536b 100644
--- a/Demo/components/CampaignView.brs
+++ b/Demo/components/CampaignView.brs
@@ -1,6 +1,8 @@
sub init()
m.scene = m.top.getScene()
m.namiPaywallManager = invalid
+ m.namiCustomerManager = invalid
+ m.namiCampaignManager = invalid
m.campaignList = invalid
m.llCampaign = m.top.findNode("llCampaign")
@@ -8,6 +10,7 @@ sub init()
m.llCampaign.observeField("itemSelected", "OnItemSelected")
m.top.observeField("visible", "onVisibleChange")
+ m.top.observeField("focusedChild","OnFocusedChildChange")
end sub
sub onInitializeChanged(event as dynamic)
@@ -15,9 +18,10 @@ sub onInitializeChanged(event as dynamic)
print "CampaignView : onInitializeChanged : initialize : " initialize
if initialize
m.campaignList = m.top.namiDataSource.campaigns
- print "CampaignView : onInitializeChanged : Campaign List " m.campaignList
- m.namiPaywallManager = m.scene.namiSDK.nami.callFunc("getPaywallManager")
- m.scene.observeField("paywallScreenDismissed", "OnPaywallScreenDismissed")
+ print "CampaignView : onInitializeChanged : Campaign List " ' m.campaignList
+ m.namiPaywallManager = m.scene.namiManager.namiPaywallManager
+ m.namiCustomerManager = m.scene.namiManager.namiCustomerManager
+ m.namiCampaignManager = m.scene.namiManager.namiCampaignManager
m.top.namiDataSource.observeField("paywallScreenDismissed", "OnPaywallScreenDismissed")
OnCampaignListReceived()
end if
@@ -36,6 +40,12 @@ sub onVisibleChange(event as dynamic)
end if
end sub
+sub OnFocusedChildChange()
+ if m.top.hasFocus()
+ m.llCampaign.setFocus(true)
+ end if
+end sub
+
sub OnPaywallScreenDismissed(event as dynamic)
isDismissed = event.getData()
print "CampaignView : OnPaywallScreenDismissed : " isDismissed
@@ -55,6 +65,10 @@ sub OnCampaignListReceived()
m.lNoItems.visible = false
+ ' deviceInfo = CreateObject("roDeviceInfo")
+ ' id = deviceInfo.GetRandomUUID()
+ ' m.namiCustomerManager.callFunc("login", id)
+
m.llCampaign.content = parseCampaignList(m.campaignList)
m.llCampaign.visible = true
@@ -74,22 +88,21 @@ end function
sub onItemSelected(event as dynamic)
selectedIndex = event.getData()
- m.namiCampaignManager = m.scene.namiSDK.nami.callFunc("getCampaignManager")
-
paywallLaunchContext = CreateObject("roSGNode", "NamiSDK:PaywallLaunchContext")
paywallLaunchContext.productGroups = ["group1","group2"]
paywallLaunchContext.customAttributes = {
"contextImage": "https://www.exmaple.com/contextImage.png"
}
- if selectedIndex = 0
- print "CampaignView : onItemSelected : Launching default campaign"
- m.namiCampaignManager.callFunc("launchWithHandler", "", paywallLaunchContext, m.top, "campaignLaunchHandler", m.top.namiDataSource, "paywallActionHandler")
- else
- print "CampaignView : onItemSelected : Launching Campaign : " m.campaignList[selectedIndex - 1].valueField
- m.namiCampaignManager.callFunc("launchWithHandler", m.campaignList[selectedIndex - 1].valueField, paywallLaunchContext, m.top, "campaignLaunchHandler", m.top.namiDataSource, "paywallActionHandler")
+ label = ""
+ if selectedIndex <> 0
+ label = m.campaignList[selectedIndex - 1].valueField
end if
- m.scene.paywallScreenDismissed = false
+
+ print "CampaignView : onItemSelected : Launching Campaign Label : " label
+
+ m.namiPaywallManager.callFunc("registerCloseHandler", m.top, "campaignCloseHandler")
+ m.namiCampaignManager.callFunc("launchWithHandler", label, paywallLaunchContext, m.top, "campaignLaunchHandler", m.top.namiDataSource, "paywallActionHandler")
end sub
function campaignLaunchHandler(isSuccess as boolean, error as dynamic)
@@ -106,6 +119,14 @@ function campaignLaunchHandler(isSuccess as boolean, error as dynamic)
end if
end function
+sub campaignCloseHandler(isSuccess=true as boolean)
+ print "CampaignView : campaignCloseHandler : Is campaign closed successfully : "; isSuccess
+ if (isSuccess)
+ m.llCampaign.setFocus(true)
+ end if
+ ' m.top.namiDataSource.paywallScreenDismissed = isSuccess
+end sub
+
sub showMessageDialog(error)
m.dialog = createObject("roSgNode", "StandardMessageDialog")
m.dialog.title = "ERROR - " + error.domain
@@ -120,7 +141,7 @@ sub showMessageDialog(error)
end sub
sub closeDialog()
- if (m.scene.dialog <> invalid) then
+ if (m.scene.dialog <> invalid)
m.scene.dialog.close = true
m.scene.dialog = invalid
end if
diff --git a/Demo/components/CampaignView.xml b/Demo/components/CampaignView.xml
index 70db386..9c06ad0 100644
--- a/Demo/components/CampaignView.xml
+++ b/Demo/components/CampaignView.xml
@@ -6,11 +6,12 @@
+
-
-
+
diff --git a/Demo/components/ContentView.brs b/Demo/components/ContentView.brs
index 458e67a..5f646a5 100644
--- a/Demo/components/ContentView.brs
+++ b/Demo/components/ContentView.brs
@@ -1,11 +1,12 @@
sub init()
m.scene = m.top.getScene()
+ m.navbar = m.top.findNode("navbar")
m.campaignViewControl = m.top.findNode("campaignViewControl")
m.profileViewControl = m.top.findNode("profileViewControl")
+ m.entitlementViewControl = m.top.findNode("entitlementViewControl")
- m.top.observeField("visible", "onVisibleChange")
-
- m.isFirstTime = true
+ m.navbarEntries = ["Campaigns", "Profile"] 'Add when show list "Entitlements"
+ m.navbar.observeField("itemSelected","OnNavbarItemSelected")
end sub
sub onInitializeChanged(event as dynamic)
@@ -13,33 +14,98 @@ sub onInitializeChanged(event as dynamic)
print "ContentView : onInitializeChanged : initialize : " initialize
if initialize
m.namiDataSource = m.top.CreateChild("NamiDataSource")
+ setNavbarItems()
+ setInitialFocus()
+ end if
+end sub
+
+sub showCampaignView()
+ m.profileViewControl.visible = false
+ m.entitlementViewControl.visible = false
+ if (not m.campaignViewControl.initialize)
m.campaignViewControl.namiDataSource = m.namiDataSource
- ' m.profileViewControl.namiDataSource = m.namiDataSource
m.campaignViewControl.initialize = true
- showCampaignView()
end if
+ m.campaignViewControl.visible = true
end sub
-sub onVisibleChange(event as dynamic)
- isVisible = event.getData()
- if isVisible
- showCampaignView()
+sub showProfileView()
+ m.campaignViewControl.visible = false
+ m.entitlementViewControl.visible = false
+ if (not m.profileViewControl.initialize)
+ m.profileViewControl.namiDataSource = m.namiDataSource
+ m.profileViewControl.initialize = true
end if
+ m.profileViewControl.visible = true
end sub
-sub showCampaignView()
- m.campaignViewControl.visible = true
+sub showEntitlementView()
+ m.campaignViewControl.visible = false
+ m.profileViewControl.visible = false
+ if (not m.entitlementViewControl.initialize)
+ m.entitlementViewControl.namiDataSource = m.namiDataSource
+ m.entitlementViewControl.initialize = true
+ end if
+ m.entitlementViewControl.visible = true
end sub
-sub showProfileView()
- m.profileViewControl.namiDataSource = m.namiDataSource
- m.profileViewControl.visible = true
+sub setNavbarItems()
+ navbarContent = createObject("roSGNode","ContentNode")
+ for each item in m.navbarEntries
+ navbarItem = navbarContent.createChild("NavbarContent")
+ navbarItem.title = item
+ navbarItem.isSelected = false
+ end for
+ m.navbar.content = navbarContent
+ m.navbar.translation = [(1920 - m.navbar.boundingRect().width)/2, 70]
+end sub
+
+sub setInitialFocus()
+ m.navbar.setFocus(true)
+ m.navbar.itemSelected = 0
+end sub
+
+sub OnNavbarItemSelected(event as dynamic)
+ index = event.getData()
+ itemSelected = m.navbar.content.getChild(index)
+ updateSelectedItem(itemSelected.title)
+ if (itemSelected.title = "Campaigns")
+ showCampaignView()
+ else if (itemSelected.title = "Profile")
+ showProfileView()
+ else if (itemSelected.title = "Entitlements")
+ showEntitlementView()
+ end if
+end sub
+
+sub updateSelectedItem(selectedtTitle as String)
+ for index = 0 to m.navbar.content.getChildCount() - 1
+ child = m.navbar.content.getChild(index)
+ if child.title = selectedtTitle
+ child.isSelected = true
+ else
+ child.isSelected = false
+ end if
+ end for
end sub
function onKeyEvent(key as String, press as Boolean) as Boolean
result = false
if (press)
print "ContentView : onKeyEvent : key = " key " press = " press
+ if key = "up"
+ m.navbar.setFocus(true)
+ result = true
+ else if key = "down"
+ if (m.navbar.hasFocus())
+ if (m.campaignViewControl.visible)
+ m.campaignViewControl.setFocus(true)
+ else if (m.profileViewControl.visible)
+ m.profileViewControl.setFocus(true)
+ end if
+ end if
+ result = true
+ end if
end if
return result
end function
diff --git a/Demo/components/ContentView.xml b/Demo/components/ContentView.xml
index 68a64b3..1f90926 100644
--- a/Demo/components/ContentView.xml
+++ b/Demo/components/ContentView.xml
@@ -7,11 +7,11 @@
-
+
-
-
-
-
+
+
diff --git a/Demo/components/EntitlementView.brs b/Demo/components/EntitlementView.brs
new file mode 100644
index 0000000..0550211
--- /dev/null
+++ b/Demo/components/EntitlementView.brs
@@ -0,0 +1,33 @@
+sub init()
+ m.scene = m.top.getScene()
+ m.lTitle = m.top.findNode("lTitle")
+
+ m.top.observeField("visible", "onVisibleChange")
+end sub
+
+sub onVisibleChange(event as dynamic)
+ isVisible = event.getData()
+ if isVisible
+ m.lTitle.setFocus(true)
+ else
+ end if
+end sub
+
+sub onIntializeChanged(event as dynamic)
+ initialize = event.getData()
+ print "EntitlementView : onIntializeChanged : initialize : " initialize
+ if initialize
+ m.namiPaywallManager = m.scene.namiManager.namiPaywallManager
+ m.namiCustomerManager = m.scene.namiManager.namiCustomerManager
+ m.top.namiDataSource.observeField("paywallScreenDismissed", "OnPaywallScreenDismissed")
+ end if
+end sub
+
+function onKeyEvent(key as String, press as Boolean) as Boolean
+ result = false
+ if (press)
+ print "EntitlementView OnKeyEvent: press " press " key : " key
+ end if
+
+ return result
+end function
diff --git a/Demo/components/EntitlementView.xml b/Demo/components/EntitlementView.xml
new file mode 100644
index 0000000..9e5a99e
--- /dev/null
+++ b/Demo/components/EntitlementView.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Demo/components/Item/NavbarContent.xml b/Demo/components/Item/NavbarContent.xml
new file mode 100644
index 0000000..d16c8ad
--- /dev/null
+++ b/Demo/components/Item/NavbarContent.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Demo/components/Item/NavbarItem.brs b/Demo/components/Item/NavbarItem.brs
new file mode 100644
index 0000000..0e5dfe0
--- /dev/null
+++ b/Demo/components/Item/NavbarItem.brs
@@ -0,0 +1,46 @@
+sub init()
+ m.top.id = "NavbarItem"
+ m.itemText = m.top.findNode("itemText")
+ m.itemSelect = m.top.findNode("itemSelect")
+ m.itemFocus = m.top.findNode("itemFocus")
+end sub
+
+sub showContent()
+ itemContent = m.top.itemContent
+ m.itemText.text = itemContent.title
+ if itemContent.isSelected
+ m.itemSelect.visible = true
+ m.itemText.color = "#FFFFFF"
+ else
+ m.itemSelect.visible = false
+ m.itemText.color = "#000000"
+ end if
+end sub
+
+sub focusPercentChanged(event as Dynamic)
+ value = event.GetData()
+ if (m.top.gridHasFocus)
+ changeFocus(value)
+ else
+ changeFocus(0)
+ end if
+end sub
+
+sub itemHasFocusChanged(event as Dynamic)
+ value = event.GetData()
+ if (value)
+ changeFocus(1)
+ end if
+end sub
+
+sub gridHasFocusChanged()
+ if (m.top.GridHasFocus And (m.top.ItemHasFocus Or m.top.FocusPercent = 1))
+ changeFocus(1)
+ else
+ changeFocus(0)
+ end if
+end sub
+
+sub changeFocus(focusPercent)
+ m.itemFocus.opacity = focusPercent
+end sub
diff --git a/Demo/components/Item/NavbarItem.xml b/Demo/components/Item/NavbarItem.xml
new file mode 100644
index 0000000..371a316
--- /dev/null
+++ b/Demo/components/Item/NavbarItem.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Demo/components/MainScene.brs b/Demo/components/MainScene.brs
index 9eb1d2a..e83c9e9 100644
--- a/Demo/components/MainScene.brs
+++ b/Demo/components/MainScene.brs
@@ -10,6 +10,7 @@ end sub
sub setupControls()
print "Mainscene : setupControls"
createBusySpinner()
+ m.lError = m.top.findNode("lError")
m.contentViewControl = m.top.findNode("contentViewControl")
InitializeNamiSDK()
end sub
@@ -22,32 +23,22 @@ sub createBusySpinner()
m.loader.visible = true
end sub
-sub showContentView()
- print "MainScene : showContentView"
+sub showContentView(isReady as boolean)
+ print "MainScene : showContentView : isReady : " isReady
hideLoader()
- m.contentViewControl.initialize = true
- m.contentViewControl.visible = true
-end sub
+ if isReady
+ m.namiCampaignManager = m.namiManager.namiCampaignManager
+ m.namiCustomerManager = m.namiManager.namiCustomerManager
+ m.namiPaywallManager = m.namiManager.namiPaywallManager
+ m.namiEntitlementManager = m.namiManager.namiEntitlementManager
-sub onSDKValidationStatus(event as Dynamic)
- payload = event.getData()
- if payload <> invalid
- if payload <> invalid and payload.status = "none"
- print "Mainscene : onSDKValidationStatus none : " payload.message
- else if payload <> invalid and payload.status = "validating"
- print "Mainscene : onSDKValidationStatus validating : " payload.message
- else if payload <> invalid and payload.status = "validated"
- print "Mainscene : onSDKValidationStatus Success " payload.message
- else if payload <> invalid and payload.status = "fail"
- print "Mainscene : onSDKValidationStatus Error : " payload.message
- end if
+ m.contentViewControl.initialize = true
+ m.contentViewControl.visible = true
+ else
+ m.lError.visible = true
end if
end sub
-sub initialize()
- m.top.setFocus(true)
-end sub
-
sub showLoader()
m.loader.visible = true
end sub
@@ -69,28 +60,20 @@ function getAppConfigFromFile() as Dynamic
return configJson
end function
-function onPaywallDismissed()
- print "MainScene : onPaywallDismissed"
- m.top.paywallScreenDismissed = true
-end function
-
-function onExitApp()
+sub onExitApp()
hideLoader()
m.top.outRequest = {"ExitApp": true}
-end function
+end sub
function onKeyEvent(key as String, press as Boolean) as Boolean
result = false
if (press)
print "Mainscene : onKeyEvent : key = " key " press = " press
if (key = "up" or key = "down")
- else if (key = "back") then
- if (m.top.paywallScreenDismissed = false)
- m.namiPaywallManager.callFunc("dismiss", m.top, "OnPaywallDismissed")
- result = true
- else if m.nami <> invalid
+ else if (key = "back")
+ if (m.namiManager.namiStatus = "READY")
showLoader()
- m.nami.callFunc("doTasksBeforeAppExit", m.top, "onExitApp")
+ m.namiManager.callFunc("doTasksBeforeAppExit", m.top, "onExitApp")
result = true
end if
end if
diff --git a/Demo/components/MainScene.xml b/Demo/components/MainScene.xml
index df8c0ec..cab28ba 100644
--- a/Demo/components/MainScene.xml
+++ b/Demo/components/MainScene.xml
@@ -5,17 +5,18 @@
-
-
+
+
-
+
+
diff --git a/Demo/components/NamiDataSource.brs b/Demo/components/NamiDataSource.brs
index 184f7fc..e8d0f2d 100644
--- a/Demo/components/NamiDataSource.brs
+++ b/Demo/components/NamiDataSource.brs
@@ -5,11 +5,10 @@ end sub
sub setupLocals()
m.scene = m.top.getScene()
- m.namiSDK = m.scene.namiSDK
- m.namiManager = m.namiSDK.nami
- m.namiCustomerManager = m.namiManager.callFunc("getCustomerManager")
- m.namiCampaignManager = m.namiManager.callFunc("getCampaignManager")
- m.namiPaywallManager = m.namiManager.callFunc("getPaywallManager")
+ m.namiManager = m.scene.namiManager
+ m.namiCustomerManager = m.namiManager.namiCustomerManager
+ m.namiCampaignManager = m.namiManager.namiCampaignManager
+ m.namiPaywallManager = m.namiManager.namiPaywallManager
end sub
sub initializeNamiSDKValues()
@@ -21,12 +20,14 @@ sub initializeNamiSDKValues()
m.top.journeyState = m.namiCustomerManager.callFunc("journeyState")
m.top.campaigns = m.namiCampaignManager.callFunc("allCampaigns")
+ print "NamiDataSource : initializeNamiSDKValues"
m.namiPaywallManager.callFunc("registerBuySkuHandler", m.top)
m.namiCustomerManager.callFunc("registerAccountStateHandler", m.top)
m.namiCustomerManager.callFunc("setCustomerDataPlatformId", "aaaa")
end sub
function registerBuySkuHandlerCallback(sku as dynamic)
+ print "NamiDataSource : registerBuySkuHandlerCallback : Dismiss Paywall"
m.namiPaywallManager.callFunc("dismiss", m.top, "OnPaywallDismissed")
' TODO : RSS : Add purchase flow
@@ -48,7 +49,7 @@ sub showPurchaseDialog(skuDetail)
end sub
sub closeDialog()
- if (m.scene.dialog <> invalid) then
+ if (m.scene.dialog <> invalid)
m.scene.dialog.close = true
m.scene.dialog = invalid
end if
@@ -63,9 +64,10 @@ end sub
sub successfulPurchase()
isSuccessfulPurchase = true
if isSuccessfulPurchase
- purchaseSuccess = m.namiSDK.CreateChild("namiSDK:NamiPurchaseSuccess")
+ purchaseSuccess = m.namiManager.CreateChild("namiSDK:NamiPurchaseSuccess")
purchaseSuccess.product = m.top.sku
-
+ purchaseSuccess.rokuProductId = m.top.sku.product.id
+ purchaseSuccess.productId = m.top.sku.skuId
purchaseSuccess.purchaseId = ""
purchaseSuccess.qty = ""
purchaseSuccess.amount = ""
@@ -74,7 +76,7 @@ sub successfulPurchase()
purchaseSuccess.price = ""
purchaseSuccess.currencyCode = ""
purchaseSuccess.locale = ""
- ' m.namiPaywallManager.callFunc("buySkuComplete", purchaseSuccess)
+ m.namiPaywallManager.callFunc("buySkuComplete", purchaseSuccess)
end if
closeDialog()
end sub
@@ -110,21 +112,21 @@ function onAccountStateChanged(state, isSuccess, error)
end if
else
if state = 0
- print "error logging in: " + error.message
+ print "ERROR: logging in: "; error.message
else if state = 1
- print "error logging out: " + error.message
+ print "ERROR: logging out: "; error.message
else if state = 2
- print "error setting advertising id: " + error.message
+ print "ERROR: setting advertising id: "; error.message
else if state = 3
- print "error clearing advertising id: " + error.message
+ print "ERROR: clearing advertising id: "; error.message
else if state = 4
- print "error setting vendor id: " + error.message
+ print "ERROR: setting vendor id: "; error.message
else if state = 5
- print "errot clearing vendor id: " + error.message
+ print "ERROR: clearing vendor id: "; error.message
else if state = 6
- print "error in setting cdp id: " + error.message
+ print "ERROR: in setting cdp id: "; error.message
else if state = 7
- print "error in clearing dp id: " + error.message
+ print "ERROR: in clearing dp id: "; error.message
end if
end if
end function
diff --git a/Demo/components/NamiSDKIntegrationHelper.brs b/Demo/components/NamiSDKIntegrationHelper.brs
index d75403b..fb44f70 100644
--- a/Demo/components/NamiSDKIntegrationHelper.brs
+++ b/Demo/components/NamiSDKIntegrationHelper.brs
@@ -3,9 +3,9 @@ sub InitializeNamiSDK()
print "NamiSDKIntegrationHelper : InitializeNamiSDK : Loading SDK, Path : " m.global.appConfig.namiSDKPath
m.namiSDK = m.top.createChild("ComponentLibrary")
m.namiSDK.id = "namiSDK"
+ m.top.appendChild(m.bitmovinPlayerSDK)
m.namiSDK.observeField("loadStatus", "onSDKLoadStatusChanged")
m.namiSDK.uri = m.global.appConfig.namiSDKPath
- m.top.addFields({"namiSDK": m.namiSDK})
end sub
' Creates the SDK wrapper once the component library node successfully loads the namiSDK
@@ -14,59 +14,66 @@ sub onSDKLoadStatusChanged(event as dynamic)
if loadStatus = "ready"
print "NamiSDKIntegrationHelper : onSDKLoadStatusChanged : SDK loaded successfully."
m.namiSDK.unobserveField("loadStatus")
- setupWrapperSDK()
- initialize()
+ configureNamiManager()
else if loadStatus = "failed"
print "*** ERROR *** NamiSDKIntegrationHelper : InitializeNamiSDK : Failed to load SDK."
' Add Error Hanlding if required
end if
end sub
-sub setupWrapperSDK()
- ' Production and staging appPlatformId are set from the appData.json
- appPlatformId = m.global.appConfig.appPlatformIdProduction
- if m.global.appConfig.environment = "staging"
- appPlatformId = m.global.appConfig.appPlatformIdStaging
- end if
+sub configureNamiManager()
+ createNamiManager()
+ namiConfig = getNamiConfig()
- ' Create NamiConfiguration object and configure it with required data
- m.namiConfig = m.namiSDK.CreateChild("namiSDK:NamiConfiguration")
- m.namiConfig.callFunc("configuration", appPlatformId, m.global.appConfig.fonts)
- m.namiConfig.logLevel = ["info", "warn", "error"] ' "debug"
+ ' Get SDK ready state before displaying campaign
+ m.namiManager.observeField("namiStatus", "OnNamiStatusReceived")
- ' Only for Nami internal
- ' if m.global.appConfig.environment = "staging"
- ' m.namiConfig.namiCommands = ["useStagingAPI"]
- ' end if
+ m.namiManager.callFunc("configure", namiConfig)
+end sub
- ' Uncomment if you have initial config files in your appData.json
- ' initialConfigFileText = ReadAsciiFile(m.global.appConfig.namiInitialConfigFilePath)
- ' if initialConfigFileText <> invalid and initialConfigFileText <> ""
- ' m.namiConfig.initialConfig = initialConfigFileText
- ' end if
+sub createNamiManager()
+ m.namiManager = CreateObject("roSGNode", "namiSDK:Nami")
+ m.top.namiManager = m.namiManager
+end sub
- m.namiManager = m.namiSDK.CreateChild("namiSDK:Nami")
- m.namiSDK.addFields({"nami": m.namiManager})
+sub getNamiConfig() as object
+ ' TODO: REMOVE THIS
+ if (m.global.appConfig.isUseDummyProducts)
+ dummyProducts = ReadAsciiFile(m.global.appConfig.namiDummyProductsFilePath)
+ if (dummyProducts <> invalid and dummyProducts <> "")
+ m.namiManager.dummyProducts = dummyProducts
+ end if
+ end if
- ' Get SDK ready state before displaying campaign
- m.namiManager.observeField("isInitialDataLoaded", "OnSDKReadyWithData")
+ appPlatformId = m.global.appConfig.appPlatformIdProduction
+ if m.global.appConfig.environment = "staging"
+ appPlatformId = m.global.appConfig.appPlatformIdStaging
+ end if
- configureStatus = m.namiManager.callFunc("configure", m.namiConfig)
- print "NamiSDKIntegrationHelper : setupWrapperSDK : Nami configuration status : " configureStatus
+ namiCommands = []
+ if m.global.appConfig.environment = "staging"
+ namiCommands = ["useStagingAPI"]
+ end if
- ' Get required objects as following
- m.namiCampaignManager = m.namiManager.callFunc("getCampaignManager")
- m.namiCustomerManager = m.namiManager.callFunc("getCustomerManager")
- m.namiPaywallManager = m.namiManager.callFunc("getPaywallManager")
- m.namiEntitlementManager = m.namiManager.callFunc("getEntitlementManager")
+ initialConfig = ReadAsciiFile(m.global.appConfig.namiInitialConfigFilePath)
+ namiConfig = {
+ appPlatformId : appPlatformId
+ fonts : m.global.appConfig.fonts
+ environment : m.global.appConfig.environment
+ logLevel : ["info", "warn", "error", "debug"] '
+ namiCommands : namiCommands
+ initialConfig : initialConfig
+ }
+ return namiConfig
end sub
-sub OnSDKReadyWithData(event as dynamic)
- isReady = event.getData()
- print "NamiSDKIntegrationHelper : SDK Status with data : " isReady
- if isReady
- showContentView()
- else
- ' TODO: Add required handling here
+sub OnNamiStatusReceived(event as dynamic)
+ namiStatus = event.getData()
+ print "NamiSDKIntegrationHelper : OnNamiStatusReceived : SDK Status with data : " namiStatus
+ m.namiManager.unobserveField("namiStatus")
+ if (namiStatus = "READY")
+ showContentView(true)
+ else if (namiStatus = "ERROR")
+ showContentView(false)
end if
end sub
diff --git a/Demo/components/ProfileView.brs b/Demo/components/ProfileView.brs
index 47a4a4f..c1b7601 100644
--- a/Demo/components/ProfileView.brs
+++ b/Demo/components/ProfileView.brs
@@ -1,38 +1,60 @@
sub init()
m.scene = m.top.getScene()
- m.namiSDK = m.scene.findNode("namiSDK")
- m.namiCustomerManager = m.namiSDK.findNode("NamiCustomerManagerObj")
m.isFirstTime = false
m.isActionInProcess = false
m.lDeviceId = m.top.findNode("lDeviceId")
m.lUserInfo = m.top.findNode("lUserInfo")
m.lJourneyState = m.top.findNode("lJourneyState")
- m.lInstruction = m.top.findNode("lInstruction")
+ m.userAction = m.top.findNode("userAction")
m.top.observeField("visible", "onVisibleChange")
+ m.top.observeField("focusedChild", "OnFocusedChildChange")
+ m.userAction.observeField("buttonSelected", "onButtonSelected")
+end sub
+
+sub onInitializeChanged(event as dynamic)
+ initialize = event.getData()
+ print "ProfileView : onIntializeChanged : initialize : " initialize
+ if initialize
+ m.namiPaywallManager = m.scene.namiManager.namiPaywallManager
+ m.namiCustomerManager = m.scene.namiManager.namiCustomerManager
+ m.top.namiDataSource.observeField("paywallScreenDismissed", "OnPaywallScreenDismissed")
+ updateProfileView()
+ end if
end sub
sub onVisibleChange(event as dynamic)
isVisible = event.getData()
if isVisible
- m.lInstruction.setFocus(true)
+ m.userAction.setFocus(true)
updateProfileView()
else
end if
end sub
+sub OnFocusedChildChange()
+ if m.top.hasFocus()
+ m.userAction.setFocus(true)
+ end if
+end sub
+
sub updateProfileView()
m.isActionInProcess = false
m.lDeviceId.text = "Device ID: " + m.top.namiDataSource.deviceId
if m.top.namiDataSource.isLoggedIn = true
- m.lInstruction.text = "Press * to logout"
+ m.userAction.text = "Logout"
m.lUserInfo.text = "Registered User: External ID: " + m.top.namiDataSource.loggedInId
+ m.userAction.minWidth = "210"
else
- m.lInstruction.text = "Press * to login"
+ m.userAction.text = "Login"
m.lUserInfo.text = "Anonymous User"
+ m.userAction.minWidth = "180"
end if
+ userActionRect = m.userAction.boundingRect()
+ centerx = (1920 - userActionRect.width) / 2
+ m.userAction.translation = [centerx, 520]
m.scene.callFunc("hideLoader")
end sub
@@ -61,11 +83,13 @@ sub OnUserActionLogin()
m.namiCustomerManager.callFunc("login", id)
end sub
-sub onInitializeChanged(event as dynamic)
- initialize = event.getData()
- print "CampaignView : onInitializeChanged : initialize : " initialize
- if initialize
- updateProfileView()
+sub OnDataSourceUpdated()
+ updateProfileView()
+end sub
+
+sub onButtonSelected()
+ if (m.isActionInProcess = false)
+ OnUserAction()
end if
end sub
@@ -73,9 +97,8 @@ function onKeyEvent(key as String, press as Boolean) as Boolean
result = false
if (press)
print "ProfileView OnKeyEvent: press " press " key : " key
- if key = "options" and m.isActionInProcess = false
- OnUserAction()
- result = true
+ if key = "OK"
+
end if
end if
diff --git a/Demo/components/ProfileView.xml b/Demo/components/ProfileView.xml
index 1e5baf7..8b9dadc 100644
--- a/Demo/components/ProfileView.xml
+++ b/Demo/components/ProfileView.xml
@@ -8,9 +8,14 @@
-
-
-
-
+
+
+
+
+
+
+
diff --git a/Demo/images/border.9.png b/Demo/images/border.9.png
new file mode 100644
index 0000000..6465277
Binary files /dev/null and b/Demo/images/border.9.png differ
diff --git a/Demo/images/filled.9.png b/Demo/images/filled.9.png
new file mode 100644
index 0000000..090673d
Binary files /dev/null and b/Demo/images/filled.9.png differ
diff --git a/Demo/images/ring.9.png b/Demo/images/ring.9.png
new file mode 100644
index 0000000..53617ab
Binary files /dev/null and b/Demo/images/ring.9.png differ
diff --git a/Demo/source/.DS_Store b/Demo/source/.DS_Store
index 175583b..e5e4f1f 100644
Binary files a/Demo/source/.DS_Store and b/Demo/source/.DS_Store differ
diff --git a/Demo/source/data/appData.json b/Demo/source/data/appData.json
index 7c5b134..32d8275 100644
--- a/Demo/source/data/appData.json
+++ b/Demo/source/data/appData.json
@@ -1,10 +1,14 @@
{
- "namiSDKPath": "https://packages.namiml.com/NamiSDK/Roku/NamiRoku.pkg",
- "appPlatformIdProduction": "YOUR_APP_PLATFORM_ID_PROD",
- "appPlatformIdStaging": "YOUR_APP_PLATFORM_ID_STAGE",
- "environment": "production",
+ "appPlatformIdProduction": "APP_PLATFORM_ID_PROD",
+ "appPlatformIdStaging": "APP_PLATFORM_ID_STAGING",
+ "environment": "staging",
"fonts": {
+ "MuseoSans-500": "pkg:/source/fonts/Museo_Sans-500.otf",
"opensans_bold": "pkg:/source/fonts/opensans_bold.ttf",
"opensans_regular": "pkg:/source/fonts/opensans_regular.ttf"
- }
-}
\ No newline at end of file
+ },
+ "isUseDummyProducts": false,
+ "namiDummyProductsFilePath": "pkg:/source/data/dummyProducts.json",
+ "namiInitialConfigFilePath": "pkg:/source/data/initialConfig.json",
+ "namiSDKPath": "pkg:/source/lib/nami_sdk.pkg"
+}
diff --git a/Demo/source/data/dummyProducts.json b/Demo/source/data/dummyProducts.json
new file mode 100644
index 0000000..d85af1f
--- /dev/null
+++ b/Demo/source/data/dummyProducts.json
@@ -0,0 +1,88 @@
+{
+ "rokuProducts": [
+ {
+ "HDPosterUrl": "",
+ "SDPosterUrl": "",
+ "code": "vip_annual",
+ "cost": "$3.99",
+ "description": "VIP Annual - Nami",
+ "freeTrialQuantity": 7,
+ "freeTrialType": "Days",
+ "id": "4a495a14-011b-11ee-bf05-0a58a9feac71",
+ "inStock": "true",
+ "name": "VIP Annual - Nami",
+ "offerEndDate": "",
+ "offerStartDate": "",
+ "productImagePortrait": "",
+ "productImageUrl": "",
+ "productType": "YearlySub",
+ "qty": 0,
+ "trialCost": "$0.00",
+ "trialQuantity": 0,
+ "trialType": "None"
+ },
+ {
+ "HDPosterUrl": "",
+ "SDPosterUrl": "",
+ "code": "plus_monthly",
+ "cost": "$0.99",
+ "description": "Plus Monthly - Nami",
+ "freeTrialQuantity": 7,
+ "freeTrialType": "Days",
+ "id": "d6036d6b-0095-11ee-a333-0a58a9feac2f",
+ "inStock": "true",
+ "name": "Plus Monthly - Nami",
+ "offerEndDate": "",
+ "offerStartDate": "",
+ "productImagePortrait": "",
+ "productImageUrl": "",
+ "productType": "MonthlySub",
+ "qty": 0,
+ "trialCost": "$0.00",
+ "trialQuantity": 0,
+ "trialType": "None"
+ },
+ {
+ "HDPosterUrl": "",
+ "SDPosterUrl": "",
+ "code": "vip_monthly",
+ "cost": "$2.99",
+ "description": "VIP Monthly - Nami",
+ "freeTrialQuantity": 7,
+ "freeTrialType": "Days",
+ "id": "7096f50a-011b-11ee-a333-0a58a9feac2f",
+ "inStock": "true",
+ "name": "VIP Monthly - Nami",
+ "offerEndDate": "",
+ "offerStartDate": "",
+ "productImagePortrait": "",
+ "productImageUrl": "",
+ "productType": "MonthlySub",
+ "qty": 0,
+ "trialCost": "$0.00",
+ "trialQuantity": 0,
+ "trialType": "None"
+ },
+ {
+ "HDPosterUrl": "",
+ "SDPosterUrl": "",
+ "code": "plus_annual",
+ "cost": "$1.99",
+ "description": "Plus Annual - Nami",
+ "freeTrialQuantity": 7,
+ "freeTrialType": "Days",
+ "id": "bf602461-0095-11ee-ad89-0a58a9feac8d",
+ "inStock": "true",
+ "name": "Plus Annual - Nami",
+ "offerEndDate": "",
+ "offerStartDate": "",
+ "productImagePortrait": "",
+ "productImageUrl": "",
+ "productType": "YearlySub",
+ "qty": 0,
+ "trialCost": "$0.00",
+ "trialQuantity": 0,
+ "trialType": "None"
+ }
+ ]
+}
diff --git a/Demo/source/main.brs b/Demo/source/main.brs
index 76e4dcd..dd648f2 100644
--- a/Demo/source/main.brs
+++ b/Demo/source/main.brs
@@ -4,29 +4,30 @@ sub Main()
end sub
sub showChannelSGScreen()
- screen = CreateObject("roSGScreen")
- m.port = CreateObject("roMessagePort")
-
- screen.setMessagePort(m.port)
- m.scene = screen.CreateScene("MainScene")
- screen.show()
- m.scene.observeField("outRequest", m.port)
+ screen = CreateObject("roSGScreen")
+ m.port = CreateObject("roMessagePort")
- while(true)
- msg = wait(0, m.port)
- msgType = type(msg)
- if msgType = "roSGScreenEvent"
- if msg.isScreenClosed() then return
- else if msgType = "roSGNodeEvent"
- ' When The AppManager want to send command back to Main
- if msg.getField() = "outRequest"
- request = msg.getData()
- if request <> invalid
- if request.DoesExist("ExitApp") and (request.ExitApp = true)
- screen.close()
- end if
+ screen.setMessagePort(m.port)
+ m.scene = screen.CreateScene("MainScene")
+ screen.show()
+ m.scene.observeField("outRequest", m.port)
+ m.scene.setFocus(true)
+ while(true)
+ msg = wait(0, m.port)
+ msgType = type(msg)
+ if msgType = "roSGScreenEvent"
+ if msg.isScreenClosed()
+ return
+ end if
+ else if msgType = "roSGNodeEvent"
+ if msg.getField() = "outRequest"
+ request = msg.getData()
+ if request <> invalid
+ if request.DoesExist("ExitApp") and (request.ExitApp = true)
+ screen.close()
+ end if
+ end if
+ end if
end if
- end if
- end if
- end while
+ end while
end sub
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..363bfe4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+Your use is subject to the terms of the agreement at: https://www.nami.ml/legal/tos
diff --git a/bsconfig.json b/bsconfig.json
new file mode 100644
index 0000000..27bbc3e
--- /dev/null
+++ b/bsconfig.json
@@ -0,0 +1,26 @@
+{
+ "plugins": [
+ "@rokucommunity/bslint"
+ ],
+ "rules": {
+ "aa-comma-style": "no-dangling",
+ "anon-function-style": "auto",
+ "assign-all-paths": "error",
+ "block-if-style": "no-then",
+ "case-sensitivity": "warn",
+ "condition-style": "no-group",
+ "consistent-return": "error",
+ "eol-last": "always",
+ "inline-if-style": "then",
+ "named-function-style": "auto",
+ "no-print": "off",
+ "no-stop": "warn",
+ "no-todo": "off",
+ "todo-pattern": "TODO|todo|FIXME",
+ "type-annotations": "off",
+ "unreachable-code": "info",
+ "unsafe-iterators": "error",
+ "unsafe-path-loop": "error",
+ "unused-variable": "warn"
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e755bcc
--- /dev/null
+++ b/package.json
@@ -0,0 +1,32 @@
+{
+ "author": {
+ "email": "hello@namiml.com",
+ "name": "Nami ML Inc.",
+ "username": "hellonami"
+ },
+ "dependencies": {
+ "@rokucommunity/bslint": "^0.8.9",
+ "brighterscript": "^0.65.4"
+ },
+ "description": "Nami for Roku - Easy subscriptions & in-app purchases, with powerful built-in paywalls and A/B testing.",
+ "keywords": [
+ "in-app-purchase",
+ "ios",
+ "ipados",
+ "tvos",
+ "paywall",
+ "react-native",
+ "storekit",
+ "subscriptions",
+ "iap",
+ "play-billing",
+ "payments"
+ ],
+ "license": "SEE LICENSE FILE",
+ "main": "index.js",
+ "name": "nami-roku",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "version": "1.0.0"
+}