Reference Document: go/magic-stack-adding-module-instruction (For further questions, see the original design docs linked within the reference document.)
This document outlines the architecture and steps required to add a new module to the Chrome Magic Stack on Android.
The Magic Stack is a horizontally scrollable container on Clank's New Tab Page that suggests relevant, dynamic modules to help users resume or accelerate their journeys.
From an architectural standpoint:
HomeModulesCoordinator: The central coordinator that owns the RecyclerView and manages the Magic Stack. It creates, shows, and hides modules based on rankings provided by the Segmentation Service. It acts as the ModuleDelegate.ModuleProvider: The interface implemented by individual modules allowing the Magic Stack to control their visibility and lifecycle. Modules interact back with the Magic Stack using the ModuleDelegate API.For the full detailed design, see the original design doc: go/magic-stack-on-clank-dd.
Modules are categorized into two types:
No matter which type of module you are adding, you must first create a ModuleType in ModuleDelegate. See Appendix: Creating a New Module Type for all the places this new type must be registered.
If your module is a stable module or an ephemeral module that does not use the educational tip layout, you need to create a complete Model-View-Coordinator (MVC) structure inside the chrome/browser/magic_stack or another appropriate directory.
ModuleProviderBuilder, ModuleConfigChecker):ChromeTabbedActivity.InputContext) to the magic stack.ModuleProvider):module_layout.xml.Educational Tip modules use a shared MVC architecture to avoid duplication and rely on a Factory Design Pattern to configure specific cards.
ModuleType inside EducationalTipModuleUtils#getModuleTypes().EducationalTipCardProvider interface inside the educational_tip/cards/ directory.EducationalTipCardProviderFactory.EducationalTipModuleMediatorUnitTest.Ephemeral modules (both standard and educational tips) must be integrated into the Ephemeral Module Cards platform, which uses the Contextual Cards API to evaluate eligibility and ranking dynamically.
constants.h: Add the module name and the signal names used in the trigger scenarios.home_modules_card_registry.cc:NotifyCardShown and NotifyCardInteracted).CreateAllCards().new_module.cc:CardSelectionInfo.IsEnabled(int impression_count): Checked during cold startup to verify feature flags and display limits.GetInputs(): Defines the signals and maps feature queries by SignalKey. Signals can come from C++ metrics or Java via InputContext.ComputeCardResult(const CardSelectionSignals& signals): Dynamically checks real-time signals and user interaction history to return the card's rendering position (EphemeralHomeModuleRank::kTop, kLast, or kNotShown).If your GetInputs() implementation requires signals passed in as an InputContext:
EducationalTipCardProviderSignalHandler. They will be triggered by its createInputContext() method.createInputContext() function you created in your custom module Builder (from Section 1).Don't forget to add C++ and Java unit tests covering your integration logic:
home_modules_card_registry_unittest.ccnew_module_unittest.ccEducationalTipCardProviderSignalHandlerUnitTest.java (if applicable)The ModuleType enum is utilized across configuration settings and metrics. When you create a new module type in ModuleDelegate.ModuleType, you must consistently register it in the following places:
HomeModulesConfigSettings:getTitleForModuleTypeHomeModulesMetricsUtils:getModuleNameHomeModulesMetricsUtils:convertLabelToModuleTypetools/metrics/histograms/metadata/magic_stack/histograms.xml (Update ModuleType enums)tools/metrics/histograms/metadata/magic_stack/enums.xml (Update ModuleType enums)If the module is an Educational Tip, also update: 6. HomeModulesUtils:sEducationalTipCardList 7. EducationalTipCardProviderSignalHandler:createInputContext