Note: At this time, Content Launcher and Account Login are available to select partners only.
This document serves as a validation checklist for developers integrating Content Launcher and Account Login within their application. Before submitting your build for Amazon review or proceeding to final stages of testing, ensure each of the items below is carefully verified.
The checklist covers manifest declarations, package dependencies, ,interactive and headless component configuration, login status handling, and shared state implementation.
Manifest Validation
Ensure your manifest matches the sample reference. Confirm the following:
Note: Sample package id ‘com.kepler.contentlauncherapp’ is used in below example. Replace it with your app’s package id.
-
com.amazon.category.kepler.mediais declared under[[components.interactive]] -
com.amazon.category.kepler.mediais NOT declared under[[components.services]] -
Sections
[[message]]and[[offers.module]]are added -
override_command_componentis NOT present under[extras.value.application.interface]withinterface_name = "com.amazon.kepler.media.IContentLauncherServer" -
override_attribute_componentis present under[[extras.value.application.interface]]withinterface_name = "com.amazon.kepler.media.IAccountLoginServer", and it points to ‘id’ of the [[components.service]]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IAccountLoginServer"
attribute_options = ["Status"]
override_attribute_component = { Status = "com.kepler.contentlauncherapp.interface.provider" }
-
The interactive component (
<package_id>.main) is added forcomponent-idin[[extras]], NOT<package_id>.interface.provider.[[extras]] key = "interface.provider" component-id="com.kepler.contentlauncherapp.main" -
The correct
partner-id(provided by your Amazon contact) is added. -
The
runtime-modulekepler.keplerscript.runtime.loader is declared for interactive component.
[[components.interactive]]
id = "com.kepler.contentlauncherapp.main"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
categories = ["com.amazon.category.main","com.amazon.category.kepler.media"]
- The
runtime-modulekepler.headless.runtime.loader is declared for service component.
[[components.service]]
id = "com.kepler.contentlauncherapp.interface.provider"
runtime-module = "/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
- [[needs.module]] sections are added for both account login and content launcher
[needs]
[[needs.module]]
id = "/com.amazon.kepler.media.@IAccountLogin1"
[[needs.module]]
id = "/com.amazon.kepler.media@IContentLauncher1"
Package dependencies
Ensure you’re using the latest supported package versions. Update version as below and do a clean build. Delete package-lock.json before rebuild.
- “@amzn/kepler-media-content-launcher”: “^2.0.0”
- “@amzn/kepler-media-account-login”: “^1.1.0”
- “@amzn/headless-task-manager”: “^1.1.0”
componentInstance used in Content Launcher handler
-
Have you replaced setHandler() with
setHandlerForComponent()to use in handleLaunchContent? -
Have you called
useComponentInstance()to createcomponentInstanceto use in setHandlerForComponent()?
const componentInstance: IComponentInstance = useComponentInstance();
- Are you passing
componentInstanceto setHandlerForComponent() ?
contentLauncherServer.setHandlerForComponent(contentLauncherHandler, componentInstance);
Headless service setup
-
service.jsis placed at the same level asindex.jsand thesrcfolder. -
No unsupported modules are imported in
service.js. -
Using
registerHeadlessEntryPoint2()foronStartServiceand onStopServicein service.js
HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
'com.kepler.contentlauncherapp.interface.provider::onStartService',
() => onStartService);
HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
'com.kepler.contentlauncherapp.interface.provider::onStopService',
() => onStopService);
-
onStartServiceandonStopServiceare imported fromAccountLoginWrapper.tsin service.js
import { onStartService, onStopService } from './src/AccountLoginWrapper';
-
onStartService()andonStopService()are declared and exported in AccountLoginWrapper.ts.(HeadlessEntryPointRegistry.registerHeadlessEntryPoint2 passes
componentInstanceto onStartService)
export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
console.log('Starting Headless service');
return AccountLoginWrapperInstance.onStart(componentInstance);
};
-
componentInstanceis passed toonStart()in AccountLoginWrapper.ts.componentInstanceis passed from onStartService()
onStop() does not havecomponentInstanceparameter.
onStart(componentInstance: IComponentInstance): Promise<void> {
console.log('AccountLoginWrapper onStart()');
this.setupAccountLoginServer(componentInstance);
return Promise.resolve();
}
-
setHandlerForComponent()is used (notsetHandler()), andcomponentInstanceis passed in AccountLoginWrapper.ts.componentInstanceis passed through onStart().
this.m_accountLoginServer?.setHandlerForComponent(this.createAccountLoginHandler(), componentInstance);
State sharing between components
State sharing between the interactive and headless service components is required. Since they run in separate processes, using in-memory variables is not sufficient, as memory state is not shared. A shared mechanism, such as a file, is needed to persist and transfer state between them. Currently AsyncStorage and MMKV are supported by headless service.
- Ensure interactive component (UI App) and headless service share login state using a persistent storage such as AsyncStorage.
Login status handling
-
updateStatus()is called with ‘true’ whenever app is logged in. Ensure to store the state to the persistent storage. -
updateStatus()is called with ‘false’ whenever app is logged out. Ensure to store the state to the persistent storage. -
updateStatus()is called with login state ‘true/false’ whenever the app is launched.
console.log(`Content_Launcher_Sample: Login status update is sent ${status}`);
AccountLoginWrapperInstance.updateStatus(status);
- Check
updateStatus()is called ONLY after writing the login status update to persistent storage , to ensure updateStatus() and handleReadStatus() send same status.
<Link
onPress={async () => {
if (loginStr == 'Login') {
await saveLoginStatus(true);
setIsLoggedIn(true); //updateStatus() is triggered only after saveLoginStatus is completed
} else {
await saveLoginStatus(false);
setIsLoggedIn(false);
}
}}
- Check
handleReadStatus()used in createAccountLoginHandler() returns correct login status. It should use the login state read from the persistent storage.
handleReadStatus: async (): Promise<IStatus> => {
const status = await this.readAccountLoginStatus(); // Reading from storage
console.log(`handleReadStatus: ${status.getStatus() === StatusType.SIGNED_IN ? 'SIGNED_IN' : 'SIGNED_OUT'}`);
return status;
}
Account Login initialization
-
onStart()andonStop()are called from the interactive app
ts
useEffect(() => {
AccountLoginWrapperInstance.onStart(componentInstance);
return () => {
AccountLoginWrapperInstance.onStop();
};
}, []);