Migrating Legacy Platform to Tizen

This topic describes how to convert applications from Samsung Legacy Platform to Tizen.


Related Info


To migrate an application from Samsung Legacy Platform to Tizen, you must import the project to the Tizen Studio, and make the necessary changes in the code.

Importing an Application Project

To import a Samsung Legacy Platform application to Tizen:

  1. Remove the existing "config.xml" and "widget.info" files from the application.
  1. Create the project in the Tizen Studio.

    You can do this in 2 different ways:

    • Create a new Web project in the Tizen Studio using the "Empty" template and copy the existing application files to the new project.
    • Import the existing Samsung Legacy Platform project to the Tizen Studio as a Tizen Web project.

      To import an existing project:
      1. In the Tizen Studio menu, go to "File > Import".
      2. Select "General > Existing Projects into Workspace" and click "Next".
      3. Select the existing project folder or archive file.

    For more information, see Creating TV Applications.

  2. Create a new "config.xml" file for your project.

    For more information, see Configuring TV Applications.

Ensuring HTML5 Compatibility

To conform to HTML5 standards, modules from the Samsung Legacy Platform Common Modules API, such as the "Widget.js" and "PlugIn.js" libraries, are no longer supported. In Tizen, the Application Manager functionalities are handled using the Application API.

  • "Widget.js" library

    Replace the "Widget.js" library methods with their HTML5 equivalents in Tizen.

    Samsung Legacy Platform Tizen
    widgetAPI.putInnerHTML(pdiv,pContents)

    pdiv.innerHTML = pContents

    widgetAPI.blockNavigation()

    event.preventDefault()

    widgetAPI.sendReadyEvent()

    -
    widgetAPI.sendExitEvent()

    tizen.application.getCurrentApplication().exit()

    widgetAPI.sendReturnEvent()

    tizen.application.getCurrentApplication().hide()

    Table 1. Tizen equivalents to Samsung Legacy Platform "Widget.js" methods

  • curWidget object

    The curWidget object is not used in Tizen:

    • Before:

      curWidget.id
    • After:

      tizen.application.getCurrentApplication().appInfo.id
  • Log messages

    In Tizen, the alert() method is used to display popup windows. Use the console.log() method to print log messages.

  • Viewport configuration

    To display an application in fullscreen, set the application width as the viewport width in the "index.html" file:

    <meta name="viewport" content="width=960" />
    

For more information, see Managing Screen Resolution.

Setting Content Security Policy

You must set the content security policy in the "config.xml" file. Tizen supports the W3C standard WARP security rules.

To set the content security policy for the application:

  1. In the "Policy" tab of the configuration editor in the Tizen Studio, add a line to the table by clicking "+".
  2. In the "Network URL" column, enter your server address.
  3. In the "Allow subdomain" column, switch the value to "true" by clicking the table cell.

    Figure 1. Content security policy configuration

Ensuring User Interaction

Tizen handles user interaction differently from Samsung Legacy Platform:

  • Anchor elements

    In Samsung Legacy Platform, when mouse devices were not supported, applications registered the keydown event for clicks on an anchor element. In Tizen, use the click event for mouse and remote control clicks.

    To migrate key handling on anchor elements to Tizen:

    • If there is only 1 anchor element, in the body element, register the key handler at the anchor element.
    • If there are multiple anchor elements, key handlers are needed, but each case can be implemented differently depending on the context.
  • IME

    Tizen provides the IME by default. When an input element is focused, the IME appears automatically. You do not need to create the IME using the IMEShell() method.

    For more information on implementing the IME in Tizen, see Keyboard/IME.

  • Key values

    The key values in the Samsung Legacy Platform "TVKeyValue.js" library have changed in Tizen. If you want to continue using the same keyValue enumeration as before, include the TizenTVKeyValue.js file (direct download).

    <script src="Common/API/TizenTVKeyValue.js"></script> 
    
    // TizenTVKeyValue.js maps Tizen keyCode values to Legacy Platform' js 
    // You can use the same "KeyValue" values from the Legacy Platform: 
    var tvKey = new Common.API.TVKeyValue(); 
    
  • Play/Pause key

    The basic remote control was distributed with Samsung TVs until 2014. Since 2015, Samsung Smart TVs are distributed with a Samsung Smart Remote.

    The Samsung Smart Remote has a "Play/Pause" key, and you must make sure your Tizen application handles its key clicks correctly. During media playback, clicking the "Play/Pause" key must pause playback, and clicking it while playback is paused must resume playback.

Supporting Multitasking

Multitasking is supported by default on Samsung TVs using Tizen. Unlike in Samsung Legacy Platform, you do not need to configure multitasking in the "config.xml" file. However, you must register an event handler for visibility changes.

The sendReturnEvent(), onPause(), and onResume() methods are not used in Tizen:

  • Before:

    window.onPause = function() {
      // pause
    }
    
    window.onResume = function() {
      // resume
    }
    
  • After:

    document.addEventListener("visibilitychange",
      function() {
        if (document.hidden) {
          // Something you want to do when application is paused 
          console.log("lifecycle [pause]");
    
          // This code handles playing media when application is hidden
          // webapis.avplay.suspend();
        } 
        else { 
          // Something you want to do when application is resumed
          console.log("lifecycle [resume]");
    
          // This code handles playing media when application is visible
          // webapis.avplay.restore();
        }
      }
    );
    

Implementing Screensaver

In Samsung Legacy Platform, the application controlled the screensaver using the setOnScreenSaver() and setOffScreenSaver() methods. The screensaver functionality is handled by the Samsung Product API in Tizen:

  • Before:

    var nnaviPlugin = document.getElementById("pluginObjectNNavi");
    // ScreenSaver ON
    var screenStateON = 3;
    nnaviPlugin.SendEventToDevice(screenStateON, 1);
    
    // ScreenSaver OFF
    // var screenStateOFF = 4; //ScreenSaver OFF
    // nnaviPlugin.SendEventToDevice(screenStateOFF, 1);
    
  • After:

    function onsuccess(data) {
      console.log("setScreensavervalue = " + data);
    }
    
    function onerror(error) {
      console.log("error code : " + error.code);
    }
    
    try {
      // ScreenSaver ON
      var screenState = webapis.appcommon.AppCommonScreenSaverState.SCREEN_SAVER_ON;
    
      // ScreenSaver OFF
      var screenState = webapis.appcommon.AppCommonScreenSaverState.SCREEN_SAVER_OFF;
    
      var value = webapis.appcommon.setScreenSaver(screenState, onsuccess, onerror);
    } catch (error) {
      console.log(" error code = " + error.code);
    }
    

For more information, see Setting Screensaver.

Playing Media

In Samsung Legacy Platform, media playback was implemented using a plugin object and the Player API. These are not supported in Tizen.

In Tizen, you can implement media playback in 2 different ways:

  • Using the HTML5 video and audio elements

    Common media formats can be played using the HTML5 video and audio elements. For more information, see Using Video Elements and Using Audio Elements.
  • Using the AVPlay API from the Samsung Product API

    The AVPlay API has additional features, such as support for DRM technologies and live streaming. For more information, see Using AVPlay.

To implement media playback with a video element:

  • Player implementation:

    • Before:

      // getElement
      var videoElem = document.getElementById("pluginPlayer");
      
      // setDisplayArea
      videoElem.SetDisplayArea(left, top, width, height); // set any value
      
      // init
      videoElem.InitPlayer(url); // set your url
      
      // After InitPlayer is called. Seek play to sec.
      videoElem.StartPlayback(sec); // set any value
      
      // play
      videoElem.Play(url); // set your url
      
      // pause
      videoElem.Pause();
      
      // resume
      videoElem.Resume();
      
      // stop
      videoElem.Stop();
      
      // Rewind / Fast forward
      videoElem.JumpForward(sec);
      videoElem.JumpBackward(sec);
      
      // get Duration
      var duration = videoElem.GetDuration();
      
      // get mute status
      var muteStatus = videoElem.GetUserMute();
      
      // set mute
      videoElem.SetUserMute(true);
      // videoElem.SetUserMute(false);
      
      // set volume
      videoElem.SetVolumeWithKey(0);
      
      // get volume
      var volume = videoElem.GetVolume();
      
    • After:

      // getElement
      var videoElem = document.createElement("video");
      videoElem.style.backgroundColor = "#000";
      document.body.appendChild(videoElem);
      
      // setDisplayArea
      videoElem.style.left = left + "px"; // set any value
      videoElem.style.top = top + "px"; // set any value
      videoElem.style.width = width + "px"; // set any value
      videoElem.style.height = height + "px"; // set any value
      
      // init
      videoElem.src = url; // set your url
      videoElem.load();
      
      // Seek play to sec.
      if (sec > 0) {
        var jumper = function () {
          this.currentTime = sec;
          this.removeEventListener('loadedmetadata', jumper);
        };
        videoElem.addEventListener('loadedmetadata', jumper);
      }
      videoElem.play();
      
      // play
      videoElem.src = url; // set your url
      videoElem.play();
      
      // pause
      videoElem.pause()
      
      // resume
      videoElem.play();
      
      // stop
      if (videoElem.readyState > 0) {
        videoElem.src = '';
      }
      
      // Rewind / Fast forward
      videoElem.currentTime += sec;
      videoElem.currentTime -= sec;
      
      // get Duration
      var duration = videoElem.duration * 1000;
      
      // get mute status
      var muteStatus = videoElem.muted;
      
      // set mute
      videoElem.muted = true;
      // videoElem.muted = false;
      
      // set volume (deprecated)
      
      // get volume
      var volume = videoElem.volume;
      
  • Event handler implementation:

    • Before:

      // load metadata
      VideoElem.OnStreamInfoReady
      
      // time update
      VideoElem.OnCurrentPlayTime = "Player.OnCurrentPlayTime";
      
      // ended
      VideoElem.OnRenderingComplete;
      
      // buffering event
      VideoElem.OnBufferingStart = "Player.OnBufferingStart";
      VideoElem.OnBufferingComplete = "Player.OnBufferingComplete";
      
      // error
      VideoElem.OnRenderError = "Player.OnRenderError";
      VideoElem.OnStreamNotFound = "Player.OnStreamNotFound";
      VideoElem.OnConnectionFailed = "Player.OnConnectionFailed";
      VideoElem.OnNetworkDisconnected = "Player.OnNetworkDisconnected";
      
    • After:

      // load metadata
      VideoElem.addEventListener('loadedmetadata', function(){}, false);
      
      // time update
      VideoElem.addEventListener('timeupdate',function() {
        Player.OnCurrentPlayTime(this.currentTime * 1000);
      }, false);
      
      // ended
      VideoElem.addEventListener('ended', function(){}, false);
      
      // buffering event
      var bBuffering = false;
      
      VideoElem.addEventListener("stalled", function(e) {
        console.log("stalled");
        !bBuffering && Player.OnBufferingStart();
        bBuffering = true;
      }, false);
      
      VideoElem.addEventListener("waiting", function(e) {
        console.log("waiting");
        !bBuffering && Player.OnBufferingStart();
        bBuffering = true;
      }, false);
      
      VideoElem.addEventListener("timeupdate", function(e) {
        bBuffering && Player.OnBufferingComplete();
        bBuffering = false;
      });
      
      // error
      VideoElem.addEventListener("error", function(e) {
        if (this.getAttribute("src") === "" && 
            this.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
          // MEDIA_ERR_SRC_NOT_SUPPORTED occurs 
          // when changing or initializing source during playback
          // Do not treat this as an error
          // this.src is not null string but baseURI ("index.html" path) 
          // Use the getAttribute() method to get the current source
        }
        else if (this.error && typeof this.error.code === "number") {
          switch (this.error.code) {
            case MediaError.MEDIA_ERR_ABORTED:
              Player.OnConnectionFailed.call(PlayerEvtListener, e);
              break;
      
            case MediaError.MEDIA_ERR_ENCRYPTED:
            case MediaError.MEDIA_ERR_DECODE:
            case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
              Player.OnRenderError.call(Player, e);
              break;
      
            case MediaError.MEDIA_ERR_NETWORK:
              Player.OnNetworkDisconnected.call(Player, e);
              break;
      
            default:
              PlayerEvtListener.OnRenderError.call(Player, e);
          }
        }
      });
      

Accessing File System

Samsung Legacy Platform provided 2 file system APIs:

  • The asynchronous Filesystem API of the WebAPI module
  • The synchronous FileSystem() method of the Browser API

If you use the asynchronous Filesystem API, you do not need to make changes to the application logic. In Tizen, however, the Filesystem API is no longer part of the WebAPI module but a Tizen API:

  • Before:

    webapis.filesystem.resolve()
    
  • After:

    tizen.filesystem.resolve() 
    

Tizen does not provide a synchronous file system API. If you used the FileSystem() method, you must change your application implementation in one of the following ways:

  • Adapt the application logic to use the file system asynchronously and use the Filesystem API:

    • Before:

      var filesystemObj = new FileSystem();
      var fileObj = fileSystem.obj.openCommonFile(curWidget.id + "sample.data", "w");
      fileObj.writeAll("Something to write.");
      var str = fileObj.readAll();
      filesystemObj.closeCommonFile(fileObj);
      filesystemObj.deleteCommonFile(curwidget.id + "sample.data");
      
    • After:

      var str;
      tizen.filesystem.resolve("wgt-private", function(dir ){
        dir.createFile("sample.data");
        dir.resolve("wgt-private/sample.data").openStream("rw", 
          function(stream) {
            stream.write("Something to write.");
            str = stream.read(100);
            stream.close();
            tizen.filesystem.resolve("wgt-private", function(dir2) {
              dir2.deleteFile("wgt-private/sample.data");
          });
        });
      });
      
  • If it is difficult to adapt the synchronous logic to asynchronous logic, you can use the Web standard Web Storage API. For more information, see Using Web Storage.

  • Before:

    // write data
    var fileSystemObj = new FileSystem();
    var fileObj = fileSystemObj.openCommonFile(curWidget.id + '/testFile.data', 'w');
    fileObj.writeAll('something to write.');
    fileSystemObj.closeCommonFile(fileObj);
    
    // read data
    var fileObj = fileSystemObj.openCommonFile(curWidget.id + '/testFile.data', 'r');
    var strResult = fileObj.readAll();
    
    // delete file
    var bResult = fileSystemObj.deleteCommonFile(curWidget.id + '/testFile.data');
    
    // validation check
    var bValid = fileSystemObj.isValidCommonPath(curWidget.id);
    
  • After:

    // write data
    localStorage.setItem('testFile', 'something to write.');
    
    // read data
    var strResult = localStorage.getItem('testFile');
    
    // delete file
    localStorage.removeItem('testFile');
    
    // validation check
    // always true