11

The Convergence and AllShare Framework

Introducing the Convergence

Server-Side (SmartTV) Convergence Application

Client-Side (a Mobile Device) Convergence Application

Introducing the AllShare Framework

Summary

Popularity of smart phones and SmartTVs eventually called need of a convergence framework that supports interaction between different platforms. In addition to the smart phones and SmartTVs, recent cameras and cars are also equipped with smart functions. With the concept of the Samsung Smart Home that combines all smart electronics, the Convergence has become popular. The Samsung SmartTV supports the Convergence API for interacting with other devices and leads this trend.

Introducing the Convergence

What is the Convergence?

In a Samsung SmartTV application, the Convergence means the Convergence App API that supports a Samsung SmartTV application to communicate with other devices and allows them to control the application. The Samsung SmartTV Convergence provides a REST-type interface for this connection. This implementation requires other devices to support HTTP (including UPnP) to connect the application. (The Convergence and the Convergence App API will be used in interchanging matter.)

While there are many devices that can access a Samsung SmartTV application, this book will use an Android Smart Phone to explain the Convergence. This chapter is primarily written for developers experienced in Android programming, but also can be read for a broad conceptual understanding.

The chapter will show two Convergence practices, and how they are implemented in both a SmartTV application and an Android smart phone. Finally, this chapter will introduce the AllShare framework, which provides an easier communication between Samsung devices only.

Please check the SDF (SDF -> Guide - > API -> Convergence App) for APIs that are not covered in this chapter.

See below for recommended system requirements for testing the following examples.

images

Table 11-1. System Requirements for Testing the Convergence Example

Application Scenario for the Convergence App

This chapter will use the Gingerbread Man, a children's storybook application developed by Handstudio, to demonstrate a Convergence application scenario.

images

Figure 11-1. Interaction of a Convergence App – Gingerbread Man

  1. Gingerbread Man – a Convergence app – starts on a Samsung SmartTV.
  2. A client version of the Gingerbread app starts on a smart phone / tablet / PC, uses UPnP protocol to search a local Samsung SmartTV, and then connects to the running Gingerbread TV application. (Note that the Gingerbread Man application only supports one device connection at a time, but the Samsung SmartTV Convergence platform is capable of supporting up to four simultaneous connections.)
  3. Connected devices communicate with each other using the Convergence platform.

Connecting a Mobile Device to a SmartTV

There are two methods for connecting a SmartTV and a mobile device — UPnP and HTTP. The SDF recommends the UPnP method.

UPnP

The UPnP (Universal Plug and Play) method presents a device on a local network so that other devices can sense and connect it without additional configuration. It allows a SmartTV and other local devices to automatically share IP addresses.

HTTP

The HTTP method requires a SmartTV's IP address to be manually entered on a mobile device. This allows more direct and controlled access. Once devices are connected, the JSON format is used to exchange types and messages.

Server-Side (SmartTV) Convergence Application

Loading the Convergence API

See below for concept diagrams of full duplex communication between a mobile client and a SmartTV server.

images

Figure 11-2. Client Sending Message to TV App

images

Figure 11-3. TV App Sending Message to Client

In the SmartTV programming, the Web API contains all Convergence functions.

<script type=“text/javascript” language=“javascript” src=“$MANAGER_WIDGET/
Common/webapi/1.0/webapis.js”></script>

Once the API is declared, all the Web API Convergence member functions and properties can be accessed with JavaScript.

this._API = window.webapis.customdevice || {};

Declare a variable to initialize a Web API instance to finish preparing to use the Convergence.

Member Functions of the SmartTV Convergence

The SDF provides a guide for member functions of the SmartTV Convergence, for its easier usage. These functions help easy setup and connectivity to other devices without complex network configuration. The following chart lists major Web API functions for the Convergence function.

images

Table 11-2. Samsung SmartTV Convergence App API Functions

The preceding functions take care of all connections between devices. Let's see how they are used in practice.

Communication from Mobile to TV

images

Table 11-3. The registerManager Callback Function

The registerManagerCallback() function creates or breaks connection between a Samsung SmartTV and a mobile device. The member callback function receives connectivity information as arguments. The connectivity information includes a mobile status event that tells if the connection was successful. Other callback functions can be assigned for a different status. The connected mobile device's status is stored in the eventType variable.

The next table shows event values generated while connecting or disconnecting to a device. The argument callback function uses this value to see if a device is being connected or disconnected, to decide the next events.

images

Table 11-4. Event Values for the registerManager Callback Function

The following example source code uses a registerManager assigned callback function to handle connect or disconnect events of a mobile device to a SmartTV.

var Convergence = {
    API: window.webapis.customdevice || {},
    aDevice: [],
    init: function() {
        this.API.registerManagerCallback(Convergence.registerManager);
        this.API.getCustomDevices(Convergence.getCustomDevices);
    },
    registerManager: function(oManagerEvent) {
        var _this = Convergence;

        alert(‘UID: ’ + oManagerEvent.UID);
        // UID: mobile device ID
        alert(‘name: ’ + oManagerEvent.name);
        // name: mobile device name
        alert(‘eventType: ’ + oManagerEvent.eventType);
        // eventType: mobile status event
        alert(‘deviceType: ’ + oManagerEvent.deviceType);
        // deviceType: mobile device type

        switch(oManagerEvent.eventType) {
            case _this.API.MGR_EVENT_DEV_CONNECT:
                 alert(‘MGR_EVENT_DEV_CONNECT’);

                 _this.API.getCustomDevices(Convergence.getCustomDevices);
                 break;
            case _this.API.MGR_EVENT_DEV_DISCONNECT:
                 alert(‘MGR_EVENT_DEV_DISCONNECT’);

                 _this.API.getCustomDevices(Convergence.getCustomDevices);
                 break;
            default:
                 alert(‘EVENT_UNKNOWN’);
                 break;

            }
     },
Convergence.init();

The next getCustomDevices() function receives an instance of the connected device as an array property. The device information can be used to assign different callback functions. It is used to update mobile connectivity information when there is a change in mobile connection.

images

Table 11-5. The getCustomDevice () Function

The following example uses the getCustomDevice() function.

var Convergence = {
    API: window.webapis.customdevice || {},
    aDevice: [],
    init: function() {
        this.API.registerManagerCallback(Convergence.registerManager);
        this.API.getCustomDevices(Convergence.getCustomDevices);
    },
    registerManager: function(oManagerEvent) {
        var _this = Convergence;

        switch(oManagerEvent.eventType) {
            case _this.API.MGR_EVENT_DEV_CONNECT:
                alert(‘MGR_EVENT_DEV_CONNECT’);
                _this.API.getCustomDevices(Convergence.getCustomDevices);
                break;
            case _this.API.MGR_EVENT_DEV_DISCONNECT:
                alert(‘MGR_EVENT_DEV_DISCONNECT’);
                _this.API.getCustomDevices(Convergence.getCustomDevices);
                break;
            default:
                alert(‘EVENT_UNKNOWN’);
                break;
        }
    },
    getCustomDevices: function(aDevice) {
        var _this = Convergence;

        _this.aDevice = aDevice;
        alert(‘aDevice.length: ’ + aDevice.length);
        for(var i = 0; i < aDevice.length; i++) {
            var sID = aDevice[i].getUniqueID();
            alert(‘getUniqueID: ’ + aDevice[i].getUniqueID());
            // getUniqueID: mobile device ID
            alert(‘getName: ’ + aDevice[i].getName());
            // getName: mobile device name
            alert(‘getDeviceID: ’ + aDevice[i].getDeviceID());
            // deviceID: mobile device ID
            alert(‘getType: ’ + aDevice[i].getType());
            // getType: mobile device type

            aDevice[i].registerDeviceCallback(function(oDeviceInfo) {
                _this.registerDevice(sID, oDeviceInfo);
            });
        }
    }
};
Convergence.init();

The next registerDeviceCallback() function receives an instance of the connected mobile device and uses it to set events for the assigned callback function. The infoType property holds status information of the mobile instance. Table 11-7 lists possible event values of the infoType property. This function is used to assign behavior according to a message from a mobile device.

images

Table 11-6. The registerDeviceCallback () Function

images

Table 11-7. Events List for the registerDevice Callback Function

The next example uses the registerDeviceCallback() function.

var Convergence = {
    API: window.webapis.customdevice || {},
    aDevice: [],
    init: function() {
        this.API.registerManagerCallback(Convergence.registerManager);
        this.API.getCustomDevices(Convergence.getCustomDevices);
    },
    registerManager: function(oManagerEvent) {
        var _this = Convergence;

        switch(oManagerEvent.eventType) {
             case _this.API.MGR_EVENT_DEV_CONNECT:
                 
                 alert(‘MGR_EVENT_DEV_CONNECT’);

                 _this.API.getCustomDevices(Convergence.getCustomDevices);
                 break;
             case _this.API.MGR_EVENT_DEV_DISCONNECT:
                 alert(‘MGR_EVENT_DEV_DISCONNECT’);

                 _this.API.getCustomDevices(Convergence.getCustomDevices);
                 break;
             default:
                 alert(‘EVENT_UNKNOWN’);
                 break;
         }
     },
     getCustomDevices: function(aDevice) {
         var _this = Convergence;

         _this.aDevice = aDevice;
         alert(‘aDevice.length: ’ + aDevice.length);
         for(var i = 0; i < aDevice.length; i++) {
             var sID = aDevice[i].getUniqueID();

             aDevice[i].registerDeviceCallback(function(oDeviceInfo) {
                 _this.registerDevice(sID, oDeviceInfo);
             });
         }
     },
     registerDevice: function(sID, oDeviceInfo) {
         var _this = Convergence;

         alert(‘sID: ’ + sID);
         // sID: unique mobile device ID
         alert(‘infoType: ’ + oDeviceInfo.infoType);
         // infoType: mobile device information type
         for(var key in oDeviceInfo.data) {
             alert(key + ‘ : ’ + oDeviceInfo.data[key]);
         }
         // data: data received from the mobile device
         
         switch(oDeviceInfo.infoType) {
             case _this.API.DEV_EVENT_MESSAGE_RECEIVED:
                 alert(‘DEV_EVENT_MESSAGE_RECEIVED’);
                 break;
             case _this.API.DEV_EVENT_JOINED_GROUP:
                 alert(‘DEV_EVENT_JOINED_GROUP’);
                 break;
             case _this.API.DEV_EVENT_LEFT_GROUP:
                 alert(‘EVENT_DEVICE_LEFT_GROUP’);
                 break;
         }
    }
};
Convergence.init();

File Transfer from Mobile to TV

The Convergence App API can also transfer files, in addition to sending text messages. This allows a mobile device to send an image file to a SmartTV and display it on the TV screen. In this case, the SmartTV application only needs to get the location of the mobile transferred image file and display it on the desired coordinate.

images

Table 11-8. Example of Image File Transfer Format

Maximum file size for the mobile to Samsung SmartTV transfer is 3MB. However, it is recommended to use the smallest possible files to avoid network delay. Note that the transfer cannot replace an existing file. The preceding URL address format is used to display an image file on the SmartTV screen.

Communication from TV to Mobile

A SmartTV can also transfer data to a mobile device. The next member functions enable the data transfer.

images

Table 11-9. Data Transfer Member Functions

Client-Side (a Mobile Device) Convergence Application

Protocol Design

Introduction

A Convergence app functions through a connection between mobile devices and a SmartTV. This calls for a protocol that will connect two different devices. Samsung Electronics' SDF provides the Convergence App API Guide to provide request header and application methods information.

images

Figure 11-4. Structure of a Convergence App

Figure 11-4 maps how a Convergence App functions. The design is based on the REST interface and uses a URL format to define all the details for file transfer. This interface is standardized and simpler than the SOAP interface. It allows fast development cycle and fast execution.

The SDF suggested model is using an HTTP protocol-supporting mobile device to discover and connect through the UPnP scan.

This chapter will focus on the mobile device (client) part of the Samsung SmartTV Convergence App API, and explain how to implement connecting, sending a message, sending an image, receiving a message, and disconnecting between an Android-based mobile device and a SmartTV.

Request Header

A mobile device uses the HTTP protocol to send a message to a SmartTV, using the following header information. Some of the header information is mandatory, while some is optional. It is vital to have a clear idea when using the table.

images

Table 11-10. Header Information for a Convergence App Request

SLDeviceID

SLDeviceID is a header component that allows a SmartTV application to identify a client device. Usually, a unique device ID (UUID) is used. This component is mandatory for all requests except the Get Application Info method.

Content-Type

This header component indicates the content type in a JSON or XML value. It is mandatory for all POST requests.

ProductID

This header identifies a smart device, and is mandatory for a connection request. Note that the ID must be an exactly eight-letter string that starts with “SMART.”

Example: SMARTDev, SMARTtvi, etc.

VendorID

This header identifies the smart device's manufacturer, and is mandatory for a connection request. Note that the ID must also be an exactly eight-letter string.

Example: vendorMe, vendorTV

DeviceName

This header contains the name of the client device, and is mandatory for a connection request. This header can be up to 64 letters.

User-Agent

This header is used to identify type and specification of the client device, and is mandatory for all requests.

Custom

This user-defined header can be added if necessary. It is packaged in a JSON object to be sent.

Response Code

In a Convergence app, a mobile device and a SmartTV communicate using the HTTP protocol. Therefore, its status response code is also the same as the standard HTTP protocol's response code. See the following table for a list of the response codes.

images

Table 11-11. Response Code for the Convergence App

Main Methods

Since a Convergence app's main functions are handled through message exchanges between two devices, Connect, Send Message, Send Image, Retrieve Message, and Disconnect are the most frequently used methods. Descriptions and examples of the five major methods follow.

Connect

The Connect method connects a mobile device to a SmartTV. Keyword connect is used for this request. SLDeviceID, VendorID, and ProductID header components are mandatory.

images

Table 11-12. Convergence App Connect Request Header

Send Message

This method adds a message to a SmartTV's message queue. The message body can be transmitted in JSON format. See the following table's (body) label for the JSON-formatted message with type and value pairs.

images

Table 11-13. Convergence App Send Message Request Header

Send Image

The Send Image request uses the same queue keyword as the Send Message request. Therefore, its header components are also very similar. Instead of the JSON object, this request includes the filename for the image to send.

images

Table 11-14. Convergence App Send Image Request Header

Retrieve Message

This method checks if the SmartTV's message queue has any message to be sent to the mobile device. If there is a message, it will be JSON formatted and returned with a response code. Instead of actually sending a message, a SmartTV simply stores a message in its queue for a mobile device to periodically check and retrieve it.

images

Table 11-15. Convergence App Retrieve Message Request Header

Disconnect

The Disconnect method is sent by the mobile device (client) to be disconnected from the SmartTV (server.) The method only works when the two devices are already connected. Otherwise, the 404 response code will be returned.

images

Table 11-16. Convergence App Disconnect Request Header

Connecting a Device

Now that we have reviewed protocols for the mobile device—SmartTV Convergence app, let's see an example that actually connects the two devices. The SDF provides connect() for making a connecting; disconnect() for disconnecting; queue() for message exchange; info() for receiving TV application information; join() for device grouping; and leave() for device ungrouping.

This chapter will cover the more common connect(), disconnect(), and queue() methods to implement connecting, disconnecting, message exchanging, and image transferring. The following examples are for the Android platform.

Please note that some codes were repeated to demonstrate the preceding functions one at a time. For a production app, please increase the efficiency and readability of the code by modulating those repeated codes into functions. Also, it would be better to have separate implementations for AbstractHttpMessage objects, since the GET method is used for message or application information receiving.

All convergence app communication is processed in the URL format. As shown above, the URL format needs the TV's IP address, port number, TV Application ID, and the communication method. Note that the mobile device and SmartTV need to be in the same local network, and the port number change depends on the network environment. Also, a production app needs to use a Samsung-issued application ID.

An Internet router is commonly used to develop a convergence app. An Internet router usually assigns a 192.168.AAA.BBB type private IP address to connected local devices. The same AAA value means that the two devices are in the same local network. Different AAA values mean that the two devices are connected to two different Internet routers and cannot connect to each other.

Different devices usually use different port number. For example, a SmartTV uses port 80, while an emulator uses port 8008. An incorrect port number in the URL request will result in the 404 error code. Any random application ID can be used for a test, but a 12-digit Samsung-issued application ID must be used for a production app.

Connect to a TV

A mobile device uses a request URL with the Connect keyword to connect to a SmartTV. The platform supports security-enhanced HTTPS in addition to HTTP. The HTTPS method requires a security BKS file (key store) and an SSLSocket request. This book will only cover a normal HTTP-based connection.

private void connect()
{
    // URL Configuration
    String URLStr = “http://” + IP Address + “:” + Port Number + “/ws/app/” +
Application ID + “/connect”;
    URL URL = null;
    try {
      URL = new URL(URLStr);
    } catch (MalformedURLException e) {
       e.printStackTrace();
    }

    // Create a HttpClient object and configure protocol
    HttpClient httpClient = new DefaultHttpClient();
    ProtocolVersion protocol = new ProtocolVersion(“HTTP”, 1, 1);
    httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
protocol);

    // Create a Connect request header
    AbstractHttpMessage message = new HttpPost(URLStr);
    message.setParams(new BasicHttpParams().setParameter(URLStr, URL));
    message.addHeader(“User-Agent”, “Android-Phone”);
    message.addHeader(“SLDeviceID”, “12345”);
    message.addHeader(“VendorID”, “VendorTV”);
    message.addHeader(“ProductID”, “SMARTdev”);
    message.addHeader(“DeviceName”, “SamsungGalaxyS3”);

    try {
       // Send the Connect request to the TV (returns HttpResponse object)
       HttpResponse response = httpClient.execute((HttpUriRequest) message);

       // Returns the response code
       int statusCode = response.getStatusLine().getStatusCode();
    } catch (ClientProtocolException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();
    }
}

Send Message

With the Connect request successfully processed, let's move to sending a message. A similar step to the preceding Connect request will be used, except that a JSON-formatted message will be sent along. Receive a pair of strings formatted type and msg and create a JSON object, convert it to a string, in byte format, and include it in an HttpEntityEnclosingRequest object to be sent.

private void sendMessage(String type, String msg)
{
    // URL Configuration
    String URLStr = “http://” + IP Address + “:” + Port Number + “/ws/app/” +
Application ID + “/queue”;
    URL URL = null;
    try {
       URL = new URL(URLStr);
    } catch (MalformedURLException e) {
       e.printStackTrace();
    }

    // Create a JSON object and store type and message values
    JSONObject jsonObj = new JSONObject();
    try {
       jsonObj.put(JSON_TYPE, type);
       jsonObj.put(JSON_VALUE, msg);
    } catch (JSONException e1) {
       e1.printStackTrace();
    }

    // Convert the JSON object to a String
    String body = jsonObj.toString();

    // Create an HttpClient object and configure protocol
    HttpClient httpClient = new DefaultHttpClient();
    ProtocolVersion protocol = new ProtocolVersion(“HTTP”, 1, 1);
    httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
protocol);

    // Create a Send Message request header
    AbstractHttpMessage message = new HttpPost(URLStr);
    message.setParams(new BasicHttpParams().setParameter(URLStr, URL));
    message.addHeader(“User-Agent”, “Android-Phone”);
    message.addHeader(“SLDeviceID”, “12345”);

    // // Translate type and message data in the JSON object into bytes
    AbstractHttpEntity entity = new ByteArrayEntity(body.getBytes());

    // Configure Content-Type header
    entity.setContentType(“application/json”);

    // Store the entity object with byte format type
    // and message data into the HttpEntityEnclosingRequest object
    HttpEntityEnclosingRequest entityMessage = (HttpEntityEnclosingRequest)
message;
    
    entityMessage.setEntity(entity);

    try {
       Send the message to the TV (returns HttpResponse object)
       HttpResponse response = httpClient.execute((HttpUriRequest) entityMessage)
;

       // Returns the response code
       int statusCode = response.getStatusLine().getStatusCode();
    } catch (ClientProtocolException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();
    }
}

Send Image

The Send Image uses the same queue method as the Send Message, and similar steps are used. Store type value and image filename into a JSON object, convert it to a string, and include it in a Multipart Entity object. The image itself is included in the FileBody object. Then package it into an HttpEntityEnclosingRequest and send it in an HttpClient object to the SmartTV.

private void sendImage(String FileName)
{
    File path and URL Configuration
    String filePath = Environment.getExternalStorageDirectory().
getAbsolutePath() + “/Download/”;
    String URLStr = “http://” + IP Address + “:” + Port Number + “/ws/app/” +
Application ID + “/queue”;
    URL URL = null;
    try {
       URL = new URL(URLStr);
    } catch (MalformedURLException e) {
       e.printStackTrace();
    }
    // Create a JSON object and store type and message values
      JSONObject jsonObj = new JSONObject();
      try{
         jsonObj.put(“type”, “upload_image”);
         jsonObj.put(“msg”, FileName);
      } catch(Exception e) {
         e.printStackTrace();
    }

    // Convert the JSON object to a String
    String body = jsonObj.toString();
    int bodyLength = jsonObj.toString().length();

    // Create an HttpClient object and configure protocol
    HttpClient httpClient = new DefaultHttpClient();
    ProtocolVersion protocol = new ProtocolVersion(“HTTP”, 1, 1);
    httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
protocol);

    // Create a request header
    AbstractHttpMessage message = new HttpPost(URLStr);
    message.setParams(new BasicHttpParams().setParameter(URLStr, URL));
    message.addHeader(“User-Agent”, “Android-Phone”);
    message.addHeader(“SLDeviceID”, “12345”);

    // Create a MultipartEntity object and set up FileBody, StringBody
properties
    MultipartEntity multiEntity = new MultipartEntity();
    try {
       // Use the image file's path to create a File object
       File file = new File(filePath + FileName);
       multiEntity.addPart(“upload_image”, new FileBody(file, FileName,
“photo”, null));
       multiEntity.addPart(“upload_image”, new StringBody(body));
    } catch(Exception e) {
       e.printStackTrace();
    }
    
    // Store the MultipartEntity object with Send Image information
    // to an HttpEntityEnclosingRequest object
    HttpEntityEnclosingRequest entityMessage = (HttpEntityEnclosingRequest)
message;
    entityMessage.setEntity(multiEntity);

    try {
      // Send the message to the TV (returns HttpResponse object)
      HttpResponse response = httpClient.execute((HttpUriRequest)
entityMessage);
      org.apache.http.Header[] header = response.getAllHeaders();

      // Returns the response code
      int statusCode = response.getStatusLine().getStatusCode();
    } catch (ClientProtocolException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();
    }
}

Retrieve Message

The message retrieval process doesn't directly receive a message. Instead, it makes a request for a response object that contains a message, and retrieves a message from the object. First, the process creates an HttpClient object and requests for a message to the SmartTV. Then the SmartTV takes a message from its message queue, and sends it with the response object. Then the mobile device converts it to a string and finally parses it to a JSON-format type and msg pair.

The returned value can be used by activities that need the message, using the Android application's internal broadcast intent method. This entire process is implemented in the following sample code.

The Send Image uses the same queue method as the Send Message, and similar steps are used. Store type value and image filename in a JSON object, convert it to a string, and include it in a Multipart Entity object. The image itself is included in the FileBody object. Then package it in an HttpEntityEnclosingRequest and send it in an HttpClient object to the SmartTV.

// Retrieves a message from the TV
private void receiveMessage()
{
    HttpResponse response = null;
    String type = “”;
    String msg = “”;

    // URL Configuration
    String URLStr = “http://” + ipAddress + “:” + portNumber + “/ws/app/” +
appId
        + “/queue/device/” + “12345”;
    URL URL = null;
    try {
       URL = new URL(URLStr);
    } catch (MalformedURLException e) {
       e.printStackTrace();
    }

   // Create an HttpClient object and configure protocol
    HttpClient httpClient = new DefaultHttpClient();
    ProtocolVersion protocol = new ProtocolVersion(“HTTP”, 1, 1);
    httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
protocol);

    Create a Retrieve Message request header
    AbstractHttpMessage message = new HttpGet(URLStr); // GET Type
    message.setParams(new BasicHttpParams().setParameter(URLStr, URL));
    message.addHeader(“SLDeviceID”, “12345”);
    try {
      // Request for a message (returns Response object)
      aresponse = httpClient.execute((HttpUriRequest) message);
      int statusCode = response.getStatusLine().getStatusCode();
      // If the return code is 200 (Success)
      if(statusCode == 200)
      {
         // Translate the message in the Response object
         HttpEntity entity = response.getEntity();
         if(entity != null) {
            // Convert an InputStream format message in a String value
            InputStream is = entity.getContent();
            StringBuffer out = new StringBuffer();
            byte[] b = new byte[4096];
            for(int n; (n=is.read(b)) != -1;)
            {
               out.append(new String(b, 0, n));
            }
            String responseBody = out.toString();

            // Store the String format message's type and msg value in a JSON object
               JSONObject jsonObj;
               try {
                 jsonObj = new JSONObject(responseBody);
                 if(jsonObj != null) {
                    type = jsonObj.getString(“type”);
                    msg = jsonObj.getString(“msg”);
               }
            }catch (JSONException e) {
               e.printStackTrace();
            }
         }

         // If there is a converted type and msg pair, send
         // it using the internal broadcast mechanism.
         if(!type.equals(“”) && !msg.equals(“”)) {
            Intent intent = new Intent();
            // RECEIVE constant value is used for message filtering by the receiver
            intent.setAction(RECEIVE);
            intent.putExtra(“type”, type);
            intent.putExtra(“msg”, msg);
            sendBroadcast(intent);
         }
        }
     } catch (ClientProtocolException e) {
        e.printStackTrace();
     } catch (IOException e) {
        e.printStackTrace();
     }
}

// Broadcast receiver
private BroadcastReceiver mReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
       {
       // Use received Intent's Action value for message filtering
       String action = intent.getAction();
       if(action.equals(RECEIVE)) {
          String type = intent.getStringExtra(“type”);
          String msg = intent.getStringExtra(“msg”);
          Log.e(TAG, “[type] ” + type + “ [msg] ” + msg);
          // Use Log function to check the message value
        }
    }
};

Disconnect from a TV

A mobile device uses this function to request a SmartTV to be disconnected. The request returns the 404 response code if the two devices are not connected.

private void disconnect()
{
    // URL Configuration
    String URLStr = “http://” + IP Address + “:” + Port Number + “/ws/app/” +
Application ID + “/disconnect”;
    URL URL = null;
    try {
       URL = new URL(URLStr);
    } catch (MalformedURLException e) {
       e.printStackTrace();
    }

    // Create an HttpClient object and configuration protocol
    HttpClient httpClient = new DefaultHttpClient();
    ProtocolVersion protocol = new ProtocolVersion(“HTTP”, 1, 1);
    httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
protocol);

    // Create a Disconnect request header
    AbstractHttpMessage message = new HttpPost(URLStr);
    message.setParams(new BasicHttpParams().setParameter(URLStr, URL));
    message.addHeader(“SLDeviceID”, “12345”);

    try {
       // Send the Disconnect request to the TV (returns Response object)
       HttpResponse response = httpClient.execute((HttpUriRequest) message);

       // Returns the response code
       int statusCode = response.getStatusLine().getStatusCode();
    } catch (ClientProtocolException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();
    }
}

Introducing the AllShare Framework

The AllShare Framework provides an easy way to enjoy and share media contents (VOD, pictures, music, etc.) between Samsung devices. It provides many convenient features, such as playing smart phone–stored content on a TV without copying it into the TV, using a smart phone to play web-stored media content on a SmartTV, mirroring a smart phone's screen with a SmartTV, and sending files.

This may sound similar to the Convergence App API. But it has its own purpose. While the Convergence App API connects a smart phone app with a SmartTV app, the AllShare Framework shares and plays media contents using DLNA and wireless Internet (Wi-Fi Direct).

This chapter will introduce, among other AllShare Framework functions, playing a VOD file stored in a smart phone on a SmartTV screen. This function allows using the AllShare button to play a Samsung Android smart phone VOD on a TV.

images

Figure 11-5. Using the AllShare to Play a Smart Phone VOD on a SmartTV

The following methods are used to play a VOD using the AllShare Framework.

  1. Device search using DeviceFinder
  2. Playing the VOD using AV Player

See the recently published AllShare Framework document.

See below for recommended system requirements for testing the following examples.

images

Table 11-17. System Requirements for the AllShare Framework

Preparation for using the AllShare Framework

The AllShare Framework requires Android SDK 4.1.2 (API 16) or higher. Verify the Android version before installing the AllShare Framework.

01. Installing the AllShare Framework Development Tool on the AllShare SDK Use the following URL address to download the tool, as you did for the ADT.

images

Figure 11-6. Select the “Install New Software” Menu on the Eclipse

images

Figure 11-7. Install the AllShare Framework Development Tool

images

Figure 11-8. Install the AllShare Framework SDK

For more information on installing the framework, visit http://developer.samsung.com/allshare-framework/start and read 1.2. Installing AllShare Framework Development Tool and SDK.

02. Create an Android AllShare Project

After installing the AllShare Framework Development Tool and SDK, the “New Project” menu will have the new “AllShare Android Project” option.

images

Figure 11-9. Create an AllShare Android Project

For more information on creating an Android AllShare project, visit http://developer.samsung.com/allshare-framework/start and read 2.1, the Creating a New AllShare project.

A newly created AllShare project has the following differences from a standard Android project. A standard Android project can also have the AllShare feature by manually adding access rights and the AllShare library in the manifest option.

Check Manifest Update

<?xml version=“1.0” encoding=“utf-8” standalone=“no”?>
<manifest xmlns:android=“http://schemas.android.com/apk/res/android”
    package=“com.hz.allshareplayer”
    android:versionCode=“1”
    android:versionName=“1.0” >

    <uses-sdk android:minSdkVersion=“16” />

    <!-- User permission for using the AllShare Framework -->
    <uses-permission android:name=“com.sec.android.permission.PERSONAL_MEDIA”
/>

    <!-- Additional user permission for using the AllShare Framework for
AllShare Cast and Remote control -->
    <uses-permission android:name=“com.android.setting.permission.ALLSHARE_
CAST_SERVICE” />
    <uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE”/>
    <uses-permission android:name=“android.permission.ACCESS_WIFI_STATE”/>

<application
    android:icon=“@drawable/ic_launcher”
    android:label=“@string/app_name” >
    <activity
        android:name=“.AllSharePlayerActivity”
        android:label=“@string/app_name” >
        <intent-filter>
                <action android:name=“android.intent.action.MAIN” />

                <category android:name=“android.intent.category.LAUNCHER” />
           </intent-filter>
      </activity>
  </application>

</manifest>

Check a New Library in the Java Build Path

images

Figure 11-10. A New Library is Added in the Java Build Path

Finding a Device using DeviceFinder

Finding a Device

Use the DeviceFinder class to find an AllShare device. The DeviceFinder class provides the following four functions.

images

Table 11-18. List of DeviceFinder Functions

The following example will use the last function that finds a device with the set Device ID and DeviceType values.

Device Type

The AllShare Framework provides several device types. The device type can be changed after acquiring the device instance. See Table 11-19 for the supported device types.

images

Table 11-19. List of AllShare Supported Device Types

Let's use the AV Player with DEVICE_ALLPLAYER.

Retrieving Device Information

After retrieving the device list, use DeviceClass to retrieve information for each device. The AllShare Framework provides the following information.

images

Table 11-20. Information Provided by the AllShare Framework

Once the preceding functions and types are familiarized, review the next example to see how they are used to find a device. The example uses the AllShare Framework to find a VOD playback-capable device.

package com.hz.allshareplayer;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import com.sec.android.allshare.Device;
import com.sec.android.allshare.Device.DeviceDomain;
import com.sec.android.allshare.Device.DeviceType;
import com.sec.android.allshare.DeviceFinder;
import com.sec.android.allshare.DeviceFinder.IDeviceFinderEventListener;
import com.sec.android.allshare.ERROR;
import com.sec.android.allshare.ServiceConnector;
import com.sec.android.allshare.ServiceConnector.IServiceConnectEventListener;
import com.sec.android.allshare.ServiceConnector.ServiceState;
import com.sec.android.allshare.ServiceProvider;

public class AllSharePlayerActivity extends Activity {

    public static final String TAG = “AllSharePlayerActivity”;
    private ServiceProvider mServiceProvider = null;
    private TextView mText = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mText = (TextView) findViewById(R.id.txtLog);
        mText.append(“

” + “Creating service provider!” + “

”);

            ERROR err = 1ServiceConnector.createServiceProvider(this, new
IServiceConnectEventListener()
        {
            @Override
               public void onCreated(ServiceProvider sprovider, ServiceState
state)
            {
                mServiceProvider = sprovider;
                2showDeviceList();
            }
            @Override
            public void onDeleted(ServiceProvider sprovider)
            {
                mServiceProvider = null;
            }
        });
        if (err == ERROR.FRAMEWORK_NOT_INSTALLED)
        {
            // AllShare Framework Service is not installed.
        }
        else if (err == ERROR.INVALID_ARGUMENT)
        {
            // Input argument is invalid. Check and try again
        }
        else
        {
            // Success on calling the function.
        }
    }

       private final DeviceFinder.IDeviceFinderEventListener
mDeviceDiscoveryListener = new IDeviceFinderEventListener()
    {
        @Override
           public void onDeviceRemoved(DeviceType deviceType, Device device,
ERROR err)
        {
                mText.append(“AVPlayer: ” + device.getName() + “ [” + device.
getIPAddress() + “] is removed” + “
”);
        }

        @Override
        public void 6onDeviceAdded(DeviceType deviceType, Device device, ERROR
err)
        {
              mText.append(“Add - AVPlayer: ” + device.getName() + “ [” +
device.getIPAddress() + “] is found” + “
”);
        }
    };

    private void showDeviceList()

    {
        if (mServiceProvider == null)
            return;

        3DeviceFinder mDeviceFinder = mServiceProvider.getDeviceFinder();
        4mDeviceFinder.setDeviceFinderEventListener(DeviceType.DEVICE_
AVPLAYER, mDeviceDiscoveryListener);
             5ArrayList<Device> mDeviceList = mDeviceFinder.
getDevices(DeviceDomain.LOCAL_NETWORK, DeviceType.DEVICE_AVPLAYER);

        if (mDeviceList != null)
        {
            for (int i = 0; i < mDeviceList.size(); i++)
            {
                 mText.append(“AVPlayer: ” + mDeviceList.get(i).getName() + “
[” + mDeviceList.get(i).getIPAddress() + “] is found” + “
”);
            }
       }
    }


    @Override
    protected void onDestroy()
    {
        if (mServiceProvider != null)
            7ServiceConnector.deleteServiceProvider(mServiceProvider);
        super.onDestroy();
    }
}

1 ServiceConnector.createServiceProvider – Initialize the service to use the AllShare Framework.

2 After step 1 is successfully completed, the onCreated callback function will be called by the listener. The function then calls the showDeviceList() function that performs the actual device browsing.

3 Initialize DeviceFinder using the getDeviceFinder() function.

4 Register IDeviceFinderEventListener to DeviceFinder. If the AllShare Framework's target device is added or removed, the onDeviceAdded, or onDeviceRemoved callback function is called.

5 Search for AllShare Framework VOD playback-capable local target devices using the getDevices() function.

images Warning: The target device search function uses an Ajax request. A slower network may cause step 3 to be executed before step 5, resulting in devices not found.

6 Target devices not found in step 5 will be eventually found by the IDeviceFinder EventListener's onDeviceAdded callback function.

7 Disconnect the AllShare Framework using ServiceConnector.deleteServiceProvider.

images

Figure 11-11. A Samsung SmartTV Is Found

VOD Playing using the AV Player

The AllShare Framework uses an item instance to play media files. This item receives FilePath and MIME type as parameters.

AVPlayer Status

The AV Player has one of the following statuses.

images

Table 11-21. Types of AV Player Statuses

The UNKNOWN status indicates that there is a network connectivity error and the application or service cannot set the status.

The CONTENT_CHANGED status indicates that another device changed its media contents. An application needs to change the playing UI when receiving this event.

images

Figure 11-12. Status Change Diagram for the AV Player


package com.hz.allshareplayer;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.sec.android.allshare.Device;
import com.sec.android.allshare.Device.DeviceDomain;
import com.sec.android.allshare.Device.DeviceType;
import com.sec.android.allshare.DeviceFinder;
import com.sec.android.allshare.DeviceFinder.IDeviceFinderEventListener;
import com.sec.android.allshare.ERROR;
import com.sec.android.allshare.Item;
import com.sec.android.allshare.ServiceConnector;
import com.sec.android.allshare.ServiceConnector.
IServiceConnectEventListener;
import com.sec.android.allshare.ServiceConnector.ServiceState;
import com.sec.android.allshare.ServiceProvider;
import com.sec.android.allshare.media.AVPlayer;
import com.sec.android.allshare.media.AVPlayer.AVPlayerState;
import com.sec.android.allshare.media.ContentInfo;
import com.sec.android.allshare.media.MediaInfo;

public class AllSharePlayerActivity extends Activity {

    public static final String TAG = “AllSharePlayerActivity”;
    private ServiceProvider mServiceProvider = null;
    private TextView mText = null;

    private String mDeviceID = null;
    private AVPlayer mPlayer = null;
    private Item mItem = null;
    private String filePath = “/mnt/sdcard/Movies/test.mp4”;
    private String mimeType = “video/mp4”;
    boolean isPlay = false;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mText = (TextView) findViewById(R.id.txtLog);
        mText.append(“

” + “Creating service provider!” + “

”);

        ERROR err = ServiceConnector.createServiceProvider(this,
                    new IServiceConnectEventListener()
        {
            @Override
            public void onCreated(ServiceProvider sprovider, ServiceState state)
            {
                mServiceProvider = sprovider;
                showDeviceList();

            }
            @Override
            public void onDeleted(ServiceProvider sprovider)
            {
                mServiceProvider = null;
            }
        });

        if (err == ERROR.FRAMEWORK_NOT_INSTALLED)
        {
            // AllShare Framework Service is not installed.
        }
        else if (err == ERROR.INVALID_ARGUMENT)
        {
            // Input argument is invalid. Check and try again
        }
        else
        {
            // Success on calling the function.
        }
    }
        private final DeviceFinder.IDeviceFinderEventListener
mDeviceDiscoveryListener = new IDeviceFinderEventListener()
    {
        @Override
        public void onDeviceRemoved(DeviceType deviceType, Device device,
ERROR err)
        {
                mText.append(“AVPlayer: ” + device.getName() + “ [” + device.
getIPAddress() + “] is removed” + “
”);
        }

        @Override
        public void onDeviceAdded(DeviceType deviceType, Device device, ERROR
err)
        {
                mText.append(“Add - AVPlayer: ” + device.getName() + “ [” + device.
getIPAddress() + “] is found” + “
”);
                if(!isPlay){
                    mText.append(“ Play : ” + device.getName() + “ [” + device.
getIPAddress() + “]” + “
”);
                    startAllShare(device.getID());
                }
            }
        };

        private void showDeviceList()
        {
            if (mServiceProvider == null)
                return;

            DeviceFinder mDeviceFinder = mServiceProvider.getDeviceFinder();
            mDeviceFinder.setDeviceFinderEventListener(DeviceType.DEVICE_
    AVPLAYER, mDeviceDiscoveryListener);
            ArrayList<Device> mDeviceList = mDeviceFinder.getDevices(DeviceDomain.
    LOCAL_NETWORK, DeviceType.DEVICE_AVPLAYER);

        if (mDeviceList != null)
        {
        for (int i = 0; i < mDeviceList.size(); i++)
        {
            mText.append(“AVPlayer: ” + mDeviceList.get(i).getName() + “
[” + mDeviceList.get(i).getIPAddress() + “] is found” + “
”);
            if(!isPlay){
                mText.append(“ Play : ” + mDeviceList.get(i).getName() +
“ [” + mDeviceList.get(i).getIPAddress() + “]” + “
”);
                        startAllShare(mDeviceList.get(i).getID());
                    }
                }
            }
        }


        @Override
        protected void onDestroy()
        {
            if (mServiceProvider != null)
                ServiceConnector.deleteServiceProvider(mServiceProvider);
        super.onDestroy();
        }

        private void initItem() {

            DeviceFinder deviceFinder = mServiceProvider.getDeviceFinder();
        AVPlayer avPlayer = (AVPlayer) deviceFinder.getDevice(mDeviceID,
    DeviceType.DEVICE_AVPLAYER); // 1

            if (avPlayer == null)
            {
                return;
            }

        Item.LocalContentBuilder lcb = new Item.LocalContentBuilder(filePath,
    mimeType); // 4
            lcb.setTitle(“”);
            mItem = lcb.build();
            mPlayer = avPlayer;
            mItem = new Item.LocalContentBuilder(filePath, mimeType).build();

        }

        private void startAllShare(String deviceId) {
             mDeviceID = deviceID; // 1
            initItem(); // 2
            registerEventListener(); // 5
            registerResponseHandler();// 6
            play();// 7
            isPlay = true;
        }

        private void play() {
            ContentInfo.Builder builder = new ContentInfo.Builder();
            ContentInfo info = builder.build();
            mPlayer.play(mItem, info);
        }

        /**
         * register AVPlayer state changed callback function
         */
        private void registerEventListener()
        {

            if (mPlayer == null)
                return;

            mPlayer.setEventListener(new AVPlayer.IAVPlayerEventListener()
            {

                @Override
                public void onDeviceChanged(AVPlayerState state, ERROR err)
                {
                    Log.d(TAG,“onDeviceChanged state = ” + state);
                    if (AVPlayerState.PLAYING.equals(state))
                    {
                       mPlayer.getMediaInfo();
                    }
                }
            });
        }

        /**
         * register AVPlayer callback function
         */
        private void registerResponseHandler()
        {

            if (mPlayer == null)
                return;

            mPlayer.setResponseListener(new AVPlayer.IAVPlayerPlaybackResponseListener()
             {
                @Override
                public void onStopResponseReceived(ERROR err){
                }

                @Override
                public void onGetMediaInfoResponseReceived(MediaInfo arg0,
                        ERROR arg1) {
                }

                @Override
                public void onGetPlayPositionResponseReceived(long arg0, ERROR arg1)
                {
                }

                @Override
                public void onGetStateResponseReceived(AVPlayerState arg0,
                        ERROR arg1) {
                }
                @Override
                public void onPauseResponseReceived(ERROR arg0) {
                }

                @Override
                public void onPlayResponseReceived(Item arg0, ContentInfo arg1,
                            ERROR arg2) {
                }

                @Override
                public void onResumeResponseReceived(ERROR arg0) {
                }

                @Override
                public void onSeekResponseReceived(long arg0, ERROR arg1) {
                }
          });
    }
}

1 mDeviceID = device.getID(): Copy and store a newly added device's ID from onDeviceAdded. The Device ID is necessary to call an AVPlayer instance that handles VOD-playing using the AllShare Framework.

2 Prepare for the playing and create a List of playback information.

3 Find an AllShare Framework media playing–capable device using mDeviceID, and obtain the device's AVPlayer instance.

4 Create a playback information list using a file path from Item.LocalContentBuilder and MIME TYPE.

5 Register AVPlayer.IAVPlayerEventListener so that it can call the onDeviceChanged callback function and notify it, if the AV Player's status changes.

6 Register AVPlayer.IAVPlayerPlaybackResponseListener to receive SmartTV messages using the callback function.

7 Send the prepared VOD to the SmartTV using the AllShare Framework; play it on the TV screen.

images Callback functions listed in steps 5 and 6 are described in the AllShare Framework SDK.

images

Figure 11-13. A Device Is Found and Selected for Playing

images

Figure 11-14. A VOD Is Being Played on a SmartTV Using the AllShare Framework

A sample program was created to implement playback of a smart phone VOD on a SmartTV screen using the AllShare Framework. Additional coding will be needed to create commercial software. However, the information was enough to implement advanced media contents sharing and playing capability using the AllShare Framework.

Study more example codes on the SDF and try adding more capabilities.

Summary

The Samsung SmartTV provides data exchange with other devices using the HTTP and UPnP based Convergence. This capability is implemented using the Web API member functions on a SmartTV, and accessed using response headers and HTTP objects on a mobile device. The AllShare Framework is an additional sharing technology that supports easier connection between a Samsung SmartTV and other Samsung devices.

Merging different device types is not the future technology. The Samsung SmartTV already supports it using the Convergence and AllShare.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset