import "pkg:/source/api/apiPromise.bs"
import "pkg:/source/roku_modules/log/LogMixin.brs"
import "pkg:/source/utils/misc.bs"
sub init()
' initialize the log manager. second param sets log output:
' 1 error, 2 warn, 3 info, 4 verbose, 5 debug
#if debug
log.initializeLogManager(["log_PrintTransport"], 5) ' Debug mode: show all logs (info + verbose + debug)
#else
log.initializeLogManager(["log_PrintTransport"], 2) ' Production mode: show warn and error only
#end if
end sub
' Function called when the screen is displayed by the screen manager
' It is expected that screens override this function to handle focus
' managmenet and any other actions required on screen shown
sub onScreenShown()
restoreFocus(m.top)
end sub
' Function called when the screen is hidden by the screen manager
' It is expected that screens override this function if required,
' to handle focus any actions required on the screen being hidden
sub onScreenHidden()
end sub
' Function called when the screen is being permanently removed by SceneManager
' Override this to clean up resources: task nodes, observers, large data structures
' Parent components are responsible for calling onDestroy() on children that need it
'
' Floor for screens that DON'T override onDestroy(): abandon any pending
' fetchAsync promises so a late pool response can't fire into the destroyed
' node. Screens that override onDestroy() get this via the auto-abandon-promises
' BSC plugin (it injects abandonApiPromises() into their own onDestroy) — SG
' component onDestroy does not chain to this base.
sub onDestroy()
abandonApiPromises()
end sub
' ---------------------------------------------------------------------------
' sgRouter view-lifecycle bridge
'
' sgRouter drives the views it mounts in its outlet through a promise-native
' lifecycle (onViewOpen / onViewSuspend / onViewResume / beforeViewClose) and
' asks them to take focus via handleFocus(). JellyRock screens instead
' implement onScreenShown / onScreenHidden / onDestroy and own their focus
' restoration via lastFocus. These overrides bridge the two contracts so every
' screen works under the router with NO per-screen changes.
'
' The router is the only thing that mounts/suspends/resumes/destroys screens —
' there is no scene stack.
'
' These names override sgrouter_View's no-op defaults; the interface
' declarations are inherited from sgrouter_View (via JRGroup), so no <function>
' entries are needed in JRScreen.xml.
' ---------------------------------------------------------------------------
' Router: this view has just become active for the first time. Publish it as the
' active routed view BEFORE onScreenShown so JRScene's overhang controller and
' main.bs's playback-event observer-attach react to the right node. Screens that
' need route data read m.top.route (routeParams / queryParams / context) inside
' their own onScreenShown / onRouteUpdate overrides.
function onViewOpen(_params = {} as object) as dynamic
m.global.activeRoutedView = m.top
onScreenShown()
return promises.resolve(invalid)
end function
' Router: a previously-suspended (keepAlive) view has returned to the top of the
' stack. Re-publish it as the active routed view, then restore via onScreenShown.
function onViewResume(_params = {} as object) as dynamic
m.global.activeRoutedView = m.top
onScreenShown()
return promises.resolve(invalid)
end function
' Router: a new view is being pushed on top of this one (still alive).
' Save focus the same way SceneManager.pushScene does so it can be restored.
function onViewSuspend(_params = {} as object) as dynamic
saveLastFocus()
onScreenHidden()
return promises.resolve(invalid)
end function
' Router: this view is about to be permanently destroyed.
function beforeViewClose(_params = {} as object) as dynamic
onScreenHidden()
onDestroy()
return promises.resolve(invalid)
end function
' Router: take remote focus. Mirrors onScreenShown's focus rule (restore the
' saved descendant, else focus the view root).
function handleFocus(_data = {} as object) as boolean
restoreFocus(m.top)
return true
end function
' Walk the focus chain to the deepest focused descendant and remember it in
' m.top.lastFocus. Lifted from SceneManager.pushScene's focus-save loop.
sub saveLastFocus()
if isValid(m.top.focusedChild)
focused = m.top.focusedChild
while isValid(focused) and focused.hasFocus() = false
focused = focused.focusedChild
end while
if isValid(focused)
m.top.lastFocus = focused
end if
end if
end sub