top

Migrating Legacy Platform to Tizen

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

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.

    Important

    The Samsung Legacy Platform “config.xml” file is not compatible with Tizen.

  2. 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:
      a. In the Tizen Studio menu, go to “File > Import”.
      b. Select “General > Existing Projects into Workspace” and click “Next”.
      c. Select the existing project folder or archive file.

    For more information, see Creating TV Applications.

  3. 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.

    Table 1. Tizen equivalents to Samsung Legacy Platform "Widget.js" methods
    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()
  • 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

    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.

    Important

    The localStorage object has weak security, as other applications can also access the data. Always use a unique key value, and always encrypt security- or privacy-related data stored in the localStorage object.

    • 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