Ad

Saturday, August 31, 2013

The Chord Android SDK Starter Program - Part 1


 With all of the knowledge shared with you in the last 3 articles, I wanted to develop a very basic Android app – almost like a “Hello World” App using the Chord Android SDK.

However, by the time I developed the 1stapp, it still seemed like a lot of code. Of course this kind of an app is meant to do a lot more than just say ‘Hello World’. It’s power is best utilized in developing games, I would say.

Like the poker game that is available in the Samsung Android developer documentation.   

So, What are the steps to start with?

Step 1:  You first need to create a channel.  I do this when I initialize my service.  I have written a service that gets an instance of the ChordManager.

public voidinitialize(IChordServiceListener csListener) {
         
          if (mChord != null) {
               return;
          }
         
          mChord = ChordManager.getInstance(this);
          mListener = csListener;
         
          mChord.setTempDirectory(chordFilePath);
          mChord.setHandleEventLooper(getMainLooper());
         
          …
     }

Note that I have got an ChordManager here and associated a temporary file path and a the app’s main looper.

Step 2: Next, You would want a way to join the channel So, let us implement the joinChannel() method and leaveChannel() method as its complement.

This is after the ChordManager is up and running.

public IChordChannel joinChannel(String channelName) {
      
        if (channelName == null || channelName.equals("")) {
          
            mPrivateChannelName = MYCHANNEL;
        } else {
            mPrivateChannelName = channelName;
        }

       
        IChordChannel channelInst = mChord.joinChannel(mPrivateChannelName, mChannelListener);

        if (null == channelInst) {
           
            return null;
        }

        return channelInst;
    }

This seems straight forward. MYCHANNEL is a constant declared earlier as public static String MYCHANNEL = "com.sai.CHANNELONE";

If the caller does not pass a channel name, the default one declared would be used.

While joining a channel, we need to pass the Channel name and also an associated listener that is of the type IChordChannelListener.

So next step would be implement this listener.

Note the leave channel code would be like this:
public voidleaveChannel() {
      
        mChord.leaveChannel(mPrivateChannelName);
        mPrivateChannelName = "";
    }

Step 3: Implement the IChordChannelListener.

All the 9 methods of this have to be implemented. These are the methods that would be called back when a file is sent or data is received etc. And the actual implementation for this would be in the Activity that is invoking the ChordManager service.

Here, based on the example from Samsung, I have also implemented these methods mainly as a way of calling a listener that is implemented by the activity invoking it.

@Override
          public void onDataReceived(String arg0, String arg1, String arg2,
                     byte[][] arg3) {
               if (!CHORD_APITEST_MESSAGE_TYPE.equals(payloadType))
                return;

            byte[] buf = payload[0];
            if (null != mListener)
                mListener.onReceiveMessage(fromNode, fromChannel, new String(buf));
              
          }

          @Override
          public void onFileChunkReceived(String fromNode, String fromChannel, String fileName,
                     String hash, String fileType, String exchangeId, long fileSize, long offset) {
               if (null != mListener) {
                int progress = (int)(offset * 100 / fileSize);
                mListener.onFileProgress(false, fromNode, fromChannel, progress, exchangeId);
               }
              
          }

          @Override
          public void onFileChunkSent(String toNode, String toChannel, String fileName,
                     String hash, String fileType, String exchangeId, long fileSize, long offset,
                     long chunckSize) {
                if (null != mListener) {
                     int progress = (int)(offset * 100 / fileSize);
                     mListener.onFileProgress(true, toNode, toChannel, progress, exchangeId);
                 }
              
          }

          @Override
          public void onFileFailed(String node, String channel, String fileName,
                     String hash, String exchangeId, int reason) {
               switch (reason) {
            case ERROR_FILE_REJECTED: {
              
                if (null != mListener) {
                    mListener.onFileCompleted(IChordServiceListener.REJECTED, node, channel,
                            exchangeId, fileName);
                }
                break;
            }

            case ERROR_FILE_CANCELED: {
              
                if (null != mListener) {
                    mListener.onFileCompleted(IChordServiceListener.CANCELLED, node, channel,
                            exchangeId, fileName);
                }
                break;
            }
            case ERROR_FILE_CREATE_FAILED:
            case ERROR_FILE_NO_RESOURCE:
            default:
               
                if (null != mListener) {
                    mListener.onFileCompleted(IChordServiceListener.FAILED, node, channel,
                            exchangeId, fileName);
                }
                break;
        }
              
          }

          @Override
          public void onFileReceived(String fromNode, String fromChannel, String fileName,
                String hash, String fileType, String exchangeId, long fileSize, String tmpFilePath) {
               String savedName = fileName;

            int i = savedName.lastIndexOf(".");
            String name = savedName.substring(0, i);
            String ext = savedName.substring(i);
            Log.d("CHORDSERVICE",  "onFileReceived : " + fileName);
            Log.d("CHORDSERVICE", "onFileReceived : " + name + " " + ext);

            File targetFile = new File(chordFilePath, savedName);
            int index = 0;
            while(targetFile.exists()) {
                savedName = name + "_" + index + ext;
                targetFile = new File(chordFilePath, savedName);

                index++;

                Log.d("CHORDSERVICE",  "onFileReceived : " + savedName);
            }

            File srcFile = new File(tmpFilePath);
            srcFile.renameTo(targetFile);

            if (null != mListener) {
                mListener.onFileCompleted(IChordServiceListener.RECEIVED, fromNode, fromChannel,
                        exchangeId, savedName);
            }
              
          }

          @Override
          public void onFileSent(String toNode, String toChannel, String fileName,
                     String hash, String fileType, String exchangeId) {
               if (null != mListener) {
                mListener.onFileCompleted(IChordServiceListener.SENT, toNode, toChannel,
                        exchangeId, fileName);
            }
              
          }

          @Override
          public void onFileWillReceive(String fromNode, String fromChannel, String fileName,
                     String hash, String fileType, String exchangeId, long fileSize) {
              

               File targetdir = new File(chordFilePath);
            if(!targetdir.exists()) {
                targetdir.mkdirs();
            }
           
            //verifying if the external storage is avaiable
            StatFs stat = new StatFs(chordFilePath);             
            long blockSize = stat.getBlockSize();    
            long totalBlocks = stat.getAvailableBlocks();
            longavailableMemory = blockSize * totalBlocks;
           
            if(availableMemory < fileSize) {
                rejectFile(fromChannel, exchangeId);
                if (null != mListener)
                    mListener.onFileCompleted(IChordServiceListener.FAILED, fromNode, fromChannel,
                            exchangeId, fileName);
                return;
            }
           
            if (null != mListener)
                mListener.onFileWillReceive(fromNode, fromChannel, fileName, exchangeId);
              
          }

          //This is called whenever a node joins a channel
          @Override
          public void onNodeJoined(String fromNode, String fromChannel) {
               Log.v("CHORDSERVICE", "onNodeJoined(), fromNode : " + fromNode + ", fromChannel : "
                    + fromChannel);
            if (null != mListener)
                mListener.onNodeEvent(fromNode, fromChannel, true);
          }

          //This is called whenever a node leaves the channel
          @Override
          public void onNodeLeft(String fromNode, String fromChannel) {
               Log.v("CHORDSERVICE", "onNodeJoined(), fromNode : " + fromNode + ", fromChannel : "
                    + fromChannel);
            if (null != mListener)
                mListener.onNodeEvent(fromNode, fromChannel, false);
          }
   
    };

Step 4: What should happen when a Node joins the channel and leaves the channel is also dictated by the methods overridden above.
In all of the above, you see that the Listener (a new one declared in the Service class) is what is passed in by the activity.

i.e. in the ChordService class, I have declared a new Listener – IChordServiceListener. And what does the listener do? It declares methods that the activity that called the ChordService should implement. They are related what should the activity do on receiving a message, when a node joins or leaves, when a network state changes etc.

So, IChordServiceListener is becoming the glue between the implementation in the activity and the call back methods with the ChordService to the IChordChannelListener.

Here is IChordServiceListener declaration:

public interfaceIChordServiceListener {
        voidonReceiveMessage(String node, String channel, String message);

        voidonFileWillReceive(String node, String channel, String fileName, String exchangeId);

        voidonFileProgress(boolean bSend, String node, String channel, int progress,
                String exchangeId);

        public static final int SENT = 0;

        public static final int RECEIVED = 1;

        public static final int CANCELLED = 2;

        public static final int REJECTED = 3;

        public static final int FAILED = 4;

        voidonFileCompleted(int reason, String node, String channel, String exchangeId,
                String fileName);

        voidonNodeEvent(String node, String channel, boolean bJoined);

        voidonNetworkDisconnected();

        void onUpdateNodeInfo(String nodeName, String ipAddress);

        voidonConnectivityChanged();
    }

Step 5: Now that we have implemented the IChordChannelListener, we have 2 more Listeners that we need to deal with. One is the INetworkListener.
This is implemented in the initialize method itself as follows:

mChord.setNetworkListener(new INetworkListener() {

            @Override
            public void onConnected(int interfaceType) {
                if (null != mListener) {
                    mListener.onConnectivityChanged();
                }
            }

            @Override
            public void onDisconnected(int interfaceType) {
                if (null != mListener) {
                    mListener.onConnectivityChanged();
                }
            }

        });

Step 6: The only other listener we need to Implement is the IChordManagerListener. This is typically used by the ChordManager when it starts.  Here is the implementation for the same.

public int start() {
             
          return mChord.start(ChordManager.INTERFACE_TYPE_WIFI, new IChordManagerListener() {

               @Override
               public void onError(int arg0) {
                    
               }

               @Override
               public void onNetworkDisconnected() {
                     if (null != mListener)
                    mListener.onNetworkDisconnected();
                    
               }

               @Override
               public void onStarted(String name, int reason) {
                    
                     if (null != mListener)
                    mListener.onUpdateNodeInfo(name, mChord.getIp());
                    
                     if (STARTED_BY_RECONNECTION == reason) {
                    return;
                }
                    
                     IChordChannel channel= mChord.joinChannel(ChordManager.PUBLIC_CHANNEL,
                        mChannelListener);

               }
              
          });

         
     }

Note that I have made an assumption that the ChordManager is going to use only Wifi as the mode of communication between nodes for simplicity sake.

With most of the above code , the basic aspects of the ChordService are ready for being invoked by any calling activity. There are a few aspects around binding to a service, unbinding to a service etc. I have in the code but not explaining here as it is not an aspect of the Chord Android SDK. It is more of a basic android service.
The complete code for this along with the activity that invokes the ChordService will be part of the next article in the series.

So, with all this basic exploration, I am curious to know if any of you have entered the Samsung Android Contest and also to see the plethora of ways this SDKhas been used innovatively.


Sunday, August 25, 2013

Setting up the Samsung Chord SDK

Continuing from the previous posts, I would like to write about setting up the development environment for using the Chord Android SDK and give an overview of the main classes to be used for your first program (to come in a later article).

This is towards aiding android developers in writing apps for the Samsung Android Contest.

  1. Download the Samsung Chord SDK available here
  2. Download the Samsung Chord SDK emulator also available here (need to accept the license agreement in order to download both)
  3. Add the Eclipse plugin for Chord SDK as described in "Chord SDK Emulator Guide ver1.2".
  4.    You can download sample apps from the chord sdk link and run it on the emulator.
  5. The screen shot shows the basic interface of the sample demo. Of course, this can meaning fully execute only after you start the Chord Relay server in the eclipse environment which shows the console and how the UDP broadcast is happening from the emulator. This allows you to do basic stuff like Starting Chord, Joining and leaving a channel.


While the above shows the basics in terms of getting up the first Samsung chord SDK app to work, we need to understand what the Android SDK gives us and what are the classes we have to use to create our own app.

What are the basic classes and interfaces provided by the Chord Android SDK? All of the fundamental ones are described here:

ChordManager– This forms the core of the SDK. This is used to create a Node and helps in managing which nodes are connected to which channels. If you want to know more about channels and nodes, please refer the previous article.

This is the class that hides all the network complexity from the developer. These are the basic features provided by this class:
  1. Tells you the list of available network interfaces like wi-fi or Mobile Access Point etc.
  2.  It gives you an option to start and stop chord
  3.  It gives you an option to leave or join a channel
  4.  Helps you list all the channels the node has joined
  5. And also provides a way to hook up a network listener to respond to changes in the network status.


Most of the main service or game or whatever your application is aiming to achieve through node interactions should be through using the Chord Manager. However, your app – the game or service will also have to provide implementation for 3 listeners:

  1. INetworkListener-  ChordManagerhas to listen on this listener to understand the changes in the network state – whether connected or disconnected to the network (of choice – wifi, wifi direct etc.)
  2.  IChordChannelListener– It is through this listener you will have to implement all actions that need to happen when a node joins the network or leaves a network.  Through this you also need to implement what happens when a file/data is received or sent.
  3. IChordManagerListener– is a listener that needs to be implemented when you start your ChordManager. This is required to handle the changes when a ChordManagerstarts. Also, after starting, the ChordManager, if there is an error or a network disconnect, what should the ChordManagerdo – is defined in the implementation of this listener.

The above 4 classes/listeners help in implementing your own service that can be used with all your nodes in the network.

However, while you have the entire above service ready, you need to decide which channels are you interested in interacting with?  It could be the public channel or a private channel with a specific name or even many at a time.

The specific channel implementations are those classes that a developer needs to develop by implementing the IChordChannelInterface.

The interface provides the methods for sending and receiving data.

So, in summary, you have the crux of what you really want to do in your own implementation of the IChordChannel.

However, the ChordManagerhelps you manage connecting and communicating with various nodes in the private or public channels by using 3 listeners.

It is as simple as that. Hope this clarifies the Samsung Chord SDK a bit in order for you to be able to take part in the Samsung Smart App Challenge.




Sunday, August 18, 2013

Samsung Chord SDK - An Introduction

The Samsung Smart App challenge has motivated me to explore a bit more about the Samsung Chord SDK.

If you want to know more about the SamsungSmart App Challenge, please visit here  or to know more about the Android SDK – Here . There is a growing community for the android developer as well. 

Here is that I understand are the highlights of this SDK:
1.     It is a framework developed by Samsung for networking between devices – making it easy for people without network programming skills.
2.     It is not physical network connection that is created. It is application layer messaging protocol that uses the underlying TCP/IP network. i.e it is different from wi-fi or Bluetooth which work on a physical network protocol.
3.     Samsung also provides an add-in emulator to the standard eclipse development environment to test the networking of your apps.
4.     The Chord SDK allows creating a network of devices allowing for an n:n communication. It supports
a.     Synchronized content sharing
b.     Broadcast of messages to near by devices
c.      Data transfer between devices
d.     Multi-player games
5.     The Security features need improvement as any mobile with the same app on it can join the group or leave the group
6.      Chord supports only Android 4.0 and above


A bit about the architecture of the Chord SDK:

(This diagram is from Samsung SDK documentation.)


The application layer is where our imagination can be used to build innovative apps.

There are a few other basic aspects we need to understand in order to be able to use the Chord SDK.
1.     Channel Manager – This is the part of the software that manages all the mobiles in a network. The various devices are called nodes
2.     Messaging protocol – This is the part of the SDk that helps in exchanging of messages between the nodes
3.     File Transfer Protocol – This is the protocol used for file transfers between the nodes
4.     Discovery protocol -  This is the component that helps in discovering the various devices within range to for the logical group of nodes
5.     ZeroMQ – This is the underlying layer that helps with all the messaging and file transfers

A little bit more about Channels before we get into coding J

Chord has the concept of 2 types of channels for communication:
1.     Public Channel
2.     Private Channel

The Public channel is one one, which any device running the Chord SDk can join.
A private channel is one, which is created by nodes having the same chord SDK based application. 

So, if my mobile has Chord SDK app, I am part of a public channel. If I have an app A and an app B, I can be part of Channel A and Channel B respectively.

In the next article, I will talk about setting up the environment for developing using Chord SDK and later we will develop a simple app using Chord SDK.