• Quick Start
  • Booting
  • Platform
  • Portals
  • References
    • API Reference TOI3
    • IIP Reference
  • Resources
ARRIS Enterprises, Inc. Confidential Information

Multiple Application Support

Using toish for easier development

The toish sec and toish as info commands on the STB are very useful when developing and debugging multi-app support. ssh into the STB and run toish sec list to get a list of the installed secondary applications:

~ # toish sec list
WebKit Browser    (State: STOPPED)
WebGLDemo         (State: HIDDEN)

The primary application has focus.
~ #

Although we only added the kreatv-app-webgldemo IIP to the boot image config, the webgldemo IIP has a dependency on the WebKit Browser app, meaning it also has been installed. This tutorial and demo doesn't reference the browser at all.

You can use toish sec to change the input focus between apps, check which app has focus, and show, hide, start and stop them. You can use toish as info to see detailed info on all installed apps (even non-interactive ones) and their properties.

Get application properties

As part of its initialization, the portal looks at the available secondary applications to find the icon, and add it to the main menu, as follows:

<sdk_root>/examples/example-html-portal/modules/menu/menu.js

addSecondaryApplications: function() {
  var that = this;
  var apps = toi.applicationService.getSecondaryApplications();
  apps.forEach(function(app) {
    var props = getAppProperties(app);
    if (props.iconUrl) {
      // add icon to the menu
      ...
    }
  });
},

The ToiApplicationService.getSecondaryApplications() function returns an array containing the secondary applications. For each application, the getProperties() function is used to get an array containing the application properties (those defined in the applications properties XML file). The iconUrl custom property is located, and the main menu is updated with the icon and some event handling for when it is clicked. We won't go into the menu system and UI details here.

Starting Secondary Applications

The example portal is written to automatically detect, and optionally start, secondary applications with the appropriate properties set. The following code is run for each secondary application as the portal initializes:

<sdk_root>/examples/example-html-portal/multiapp.js


var startCallback = {
  onCompleted: function() {
    console.log(app.getName() + " has started!");
  },
  onProgress: function() {},
  onError: function() {
    console.log("Error: Could not start " + app.getName());
  }
};

// If the custom "autoStart" property in the applications XML file is
// set to "true" then start the app now
if (isAutoStart(app)) {
  // start the application start
  app.start(startCallback);
}

The key piece here is the ToiSecondaryApplication.start() function call, and its startCallbacks argument, discussed next.

The final part of initializing the portal is to subscribe to various key presses, discussed in the input handling page.

Callbacks

Since actions like starting, stopping, showing and hiding a secondary application can take some time, and may fail, the TOI AsyncCallback mechanism is used. Calls to start(), stop(), show() and hide() all return immediately, so the portal can continue to do other things while the application changes state. The above functions all take an object containing functions which are to be executed when the async operation completes:

<sdk_root>/examples/example-html-portal/multiapp.js


var startCallback = {
  onCompleted: function() {
    console.log(app.getName() + " has started!");
  },
  onProgress: function() {},
  onError: function() {
    console.log("Error: Could not start " + app.getName());
  }
};

If the start operation completes successfully, the onCompleted function is called, otherwise the onError is called.

Switching to a secondary application

When switching to a secondary application, the first thing the portal needs to do is determine the state of the secondary. Since the secondary may have previously been stopped, exited, or crashed, it's not guaranteed to be in STATE_HIDDEN, ready to be shown.

<sdk_root>/examples/example-html-portal/multiapp.js

switch (app.getState()) {
  case toi.consts.ToiSecondaryApplication.STATE_STOPPED:
    // when start completes, it's callback calls show()
    app.start(startCallbacks);
    break;
  case toi.consts.ToiSecondaryApplication.STATE_VISIBLE:
    return;
  case toi.consts.ToiSecondaryApplication.STATE_LOADING:
    // nothing, the start callback will handle this once started
    break;
  case toi.consts.ToiSecondaryApplication.STATE_HIDDEN:
    showApplication(app);
    break;
}

If the application is in STATE_STOPPED, it is started similar to the previous section. If it is in STATE_HIDDEN then the start stage is skipped and we go straight to the portals showApplication() function.

When switching to a secondary application, the media player should be closed and put into STATE_IDLE to avoid any potential race conditions or resource conflicts (see Playback Ownership). This means that, in addition to the callback mechanism used to handle the async application state change (show()) the portal needs to shut down the media player if it is running and wait until it reaches STATE_IDLE.

<sdk_root>/examples/example-html-portal/multiapp.js

function showApplication(app) {
  var callbacks = {
    onCompleted: function() {
      // showing complete. Time to refocus the input and get the portal
      // out of the way
      app.setInputFocus();
      framework.emit("HideMenu");
   },
    onError: function() {
      console.log("Error: Couldn't switch to application " + app.getName());
      switchToPortal(defaultPortalCallback);
    },
    onProgress: function() {}
  };

  // Since the secondary application might use the video decoding resources,
  // it is generally a good idea to wait until the media player is in
  // STATE_IDLE before showing the secondary application
  if (mediaPlayer.getState() == toi.consts.ToiMediaPlayerBase.STATE_IDLE) {
    // no conflicts, just show immediately
    app.show(callbacks);
  }
  else {
    // store the call to show()
    jobQueue.push(function() {
      // this starts the switch to the secondary...
      app.show(callbacks);
    });

    // add an event listener which will be triggered once the media player
    // gets to STATE_IDLE
    mediaPlayer.addEventListener(
          toi.consts.ToiMediaPlayerBase.ON_STATE_CHANGED,
          onMediaPlayerStateChanged);

    // start closing the media player
    console.log("Closing media player");
    mediaPlayer.close();
  }
}

If the media player is not in STATE_IDLE the portal registers an event handler on the media player for the ON_STATE_CHANGED event (see here for more on events). When the media player reaches its idle state, the queued up job (the show() call) is executed, and the application switch begins. In a similar way to start(), above, a callbacks object is provided to show(), and this callback finally gets executed once the secondary application reaches STATE_VISIBLE. In this case, the callback sets the input focus, and hides the portal menu, so that the secondary can be seen.

Return to the portal

To return to the portal, the example uses the following callback, providing it to the hide() or the stop() function, depending on whether the secondary app should be left running in the background or stopped.

<sdk_root>/examples/example-html-portal/multiapp.js

var callbacks = {
  onCompleted: function() {
    if (callback) {
      return callback();
    }
  },
  onError: function() {
     console.log("Error switching to portal!");
  }
};

The onCompleted function is used here to call the callback function provided to switchToPortal. This allows the menu bar and other areas of the portal to decide what should happen once the transition completes (such as showing the info bar or a logo, restarting the media player). The callback may also change the input focus if necessary.

5.1.1.p8

Copyright (c) 2018 ARRIS Enterprises, LLC. All Rights Reserved. ARRIS Enterprises, LLC. Confidential Information.