Content Launcher and Account Login integration validation checklist

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.

:warning: 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.media is declared under [[components.interactive]]

  • com.amazon.category.kepler.media is NOT declared under [[components.services]]

  • Sections [[message]] and [[offers.module]] are added

  • override_command_component is NOT present under [extras.value.application.interface] with interface_name = "com.amazon.kepler.media.IContentLauncherServer"

  • override_attribute_component is present under [[extras.value.application.interface]] with interface_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 for component-id in [[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-module kepler.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-module kepler.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"

:warning: 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

:warning: componentInstance used in Content Launcher handler

  • Have you replaced setHandler() with setHandlerForComponent() to use in handleLaunchContent?

  • Have you called useComponentInstance() to create componentInstance to use in setHandlerForComponent()?

    const componentInstance: IComponentInstance = useComponentInstance();
  • Are you passing componentInstance to setHandlerForComponent() ?
    contentLauncherServer.setHandlerForComponent(contentLauncherHandler, componentInstance);

:warning: Headless service setup

  • service.js is placed at the same level as index.js and the src folder.

  • No unsupported modules are imported in service.js.

  • Using registerHeadlessEntryPoint2() for onStartService and onStopService in service.js

   HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
   'com.kepler.contentlauncherapp.interface.provider::onStartService',
   () => onStartService);

   HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
   'com.kepler.contentlauncherapp.interface.provider::onStopService',
   () => onStopService);
  • onStartService and onStopService are imported from AccountLoginWrapper.ts in service.js
   import { onStartService, onStopService } from './src/AccountLoginWrapper';
  • onStartService() and onStopService() are declared and exported in AccountLoginWrapper.ts.

    (HeadlessEntryPointRegistry.registerHeadlessEntryPoint2 passes componentInstance to onStartService)

   export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
          console.log('Starting Headless service');
          return AccountLoginWrapperInstance.onStart(componentInstance);
   };

  • componentInstance is passed to onStart() in AccountLoginWrapper.ts. componentInstance is passed from onStartService()
    onStop() does not have componentInstance parameter.
   onStart(componentInstance: IComponentInstance): Promise<void> {
      console.log('AccountLoginWrapper onStart()');
      this.setupAccountLoginServer(componentInstance);
     return Promise.resolve();
   }
  • setHandlerForComponent() is used (not setHandler()), and componentInstance is passed in AccountLoginWrapper.ts. componentInstance is passed through onStart().
    this.m_accountLoginServer?.setHandlerForComponent(this.createAccountLoginHandler(), componentInstance);

:warning: State sharing between components

:red_exclamation_mark: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.

:warning: 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;   
   }

:warning: Account Login initialization

  • onStart() and onStop() are called from the interactive app
ts
useEffect(() => {
    AccountLoginWrapperInstance.onStart(componentInstance);
    return () => {
          AccountLoginWrapperInstance.onStop();
    };
}, []);
1 Like