Building settings based on modules
Problem description
Settings are often modular. They could be based on different level of modularity :
- depending on if module has been taken for this profile
- depending on back-end configuration (for example FAQs or channel scan)
- depending on hardware capability (for example HDMI CEC depends on TV capability)
You should avoid having all settings screen and service in one module for two main reasons :
- Build size. Most settings are mandatory module but some are not. So you need to avoid to embed code that does not make sense for a platform.
- Logic. Settings of a module should be part of the module responsible for the feature.
But you also need to mutualize screens and behaviors because there is a similar root to all screens.
For example, this is the first level of settings :
And this is a specific screen that is a classic multi choice settings :
Proposed solution
To define settings structure, use SharedObject
in modules.
SharedObject structure and logic
Each module can define SharedObject
of type SETTINGS
. This will be considered as first level of the settings. Then id of this “first level” could be used as type of other SharedObject
to define “subLevel”. For example, based on above schema we have
- SETTINGS
- SETTINGS.PARENTAL_CONTROL
- SETTINGS.PARENTAL_CONTROL.AGE_RATING
- SETTINGS.PARENTAL_CONTROL.CHANNEL_LOCK
- SETTINGS.ACCESSIBILITY
- SETTINGS.ACCESSIBILITY.SYSTEM_LANGUAGE
- SETTINGS.ACCESSIBILITY.AUDIO_LANGUAGE
- SETTINGS.SYSTEM_INFO
For the need of the example, use 2 methods in it :
- getPreviewAsync : will allow to retrieve preview value for an entry if it exists. It can call service of the module if required
- isEnabledAsync : will allow to check if the settings is enabled (i.e. should be visible). For example, CEC will depend on hardware capability, FAQ on BE results…
SettingsTreeService
Create a SettingsTreeService
in SettingsModule
. Its role is to build settings architecture and allow screens to retrieve those information. It will expose 3 methods :
getEntry
: should allow to retrieve information relative to anEntry
based on its idgetChildren
: should allow to retrieve children of anEntry
based on its id- call
getSharedObject
and mapSharedObject
to aSettingsEntry
(be carefullSharedObject
are mixins, so you should call.create()
on them) - should only build a
SettingsEntry
if call toisEnabledAsync
returnstrue
- should call
getPreviewAsync
and set result inpreviewValue
(do not put methods in aModel
) - can do specific stuff based on id (for example FAQ will be dynamically created and can’t be based on
SharedObject
)
- call
getParent
: should allow to retrieve parent of enEntry
based on its id
Screens
To mutualize screen behaviour it is important that you create multiple AbstractSettingsScren
. Then you will be able to inherit from those AbstractSettingsScreen
to build SettingsScreen
of your module. Do not declare all SettingsScreen
in SettingsModule
. It is very important to use modularization for screens.
As soon as you arrive at a specific screen, use routing to move to another settings. It will allow you to only keep action and logic specific to the selected settings in one screen. Otherwise, it will become harder and harder to maintain.
Do not forget to add settings module as dependencies in your package.json
of your module (modules/myModule/package.json
) :
"wtvDependencies": {
"settings": "1.0.0"
}
Conclusion
- Mutualise setting screens and views in
SettingsModule
and then define specific settings screens in modules that inherits or uses element of Settings module. - Define settings screens in each module and use routing
- Handle settings choices inside module responsible for the feature instead of inside the module responsible for the settings
- Use
SharedObject
to avoid full module loading for settings tree building