Unclouded Messenger for Android.

Learn how to make a messenger app to chat with your nearby buddies without being connected to the internet.

In this tutorial, we explain how to implement a messenger application employing device-to-device communication. The app provides functionality to chat with nearby people and notifies when a connection is established or broken.

Using the Unclouded Android SDK, the communication layer of this application is as little as only a few lines of code. Check out the full code in our Github repository.

When the messenger app is started, it is first prompted to enter your name. This is the name that will be used during the interactions and that other people will see upon receiving a message from you. After submitting your name, the actual messenger interface is loaded and a connection to the network is established.

To interact between devices, we have the choice to either pass data by reference or by copy. Passing data by copy implies that individual messages are broadcasted across the network as a seperate service, while a pass-by-reference strategy allows a service to hold a reference to a messenger instance to which messages can be sent.

Although both options are perfectly applicable in the case of a messenger app, we have chosen to pass data by reference because this allows to easily notify who joined the conversation and who left. The reason for this is that in this case, services operate at the level of messenger instances and not on the level of single messages as in the case of a pass-by-copy strategy.

The messenger object, that is broadcasted (by reference), is of class Messenger and implements the UObject interface in order to impose this pass-by-reference strategy. It provides two methods:
- getName(): Invoking this method returns the name that was entered upon starting the app; this is the name of the person who is hosting this object.
- receiveMsg(name, msg): Invoking this method causes the given message to be displayed on the screen. The sender of the message is the name that is passed as an argument.

class Messenger implements UObject {

    public String getName(){ return myName; }

    public void receiveMsg(String name, String msg){
        printMessage(name, msg);
    }

}

Upon initializing the messenger interface, an instance of the Messenger class is broadcasted across the network via a MESSENGER type tag.

TypeTag MESSENGER_TYPETAG = new TypeTag("MESSENGER");
Messenger myMessenger = new Messenger();
unclouded.broadcast(MESSENGER_TYPETAG, myMessenger);

To listen for messenger instances in the network, we initialize a service subscription for the MESSENGER type tag. This will trigger a notification when a remote reference to some messenger instance in the network is discovered. Since this also enables to track when the remote reference is disconnected or reconnected, we can very simply monitor who joined the conversation and who left. After obtaining a remote reference to a messenger object, the reference is stored and we asynchronously invoke its getName method. Eventually, this will return the name of the corresponding person, whereafter a message on the screen is shown.

unclouded.whenever(MESSENGER_TYPETAG, new ServiceListener<RemoteReference>(){

    String buddyName;

    @Override
    public void isDiscovered(RemoteReference remoteReference) {
        buddyList.add(remoteReference);
        Promise promise = remoteReference.asyncInvoke("getName");
        promise.when(new PromiseListener<String>(){

            @Override
            public void isResolved(String name) {
                buddyName = name;
                printBuddyJoinedMessage(name);  
            }
        });
    }

    @Override
    public void isDisconnected(RemoteReference remoteReference){ /*...*/ }

    @Override 
    public void isReconnected(RemoteReference remoteReference){ /*...*/ }

});

After submitting a message in the messenger app, the message is sent to all persons part in the conversation. This is done by looping over all stored remote references and asynchronously invoke their receiveMsg method. This causes the message to be sent to the corresponding messenger instance and upon arrival, being printed on the screen.

private void broadcastMessage(String msg){
    for(RemoteReference reference: buddyList){
        reference.asyncInvoke("receiveMsg", myName, msg);
    }
    printMessage(myName, msg);
}

Using the Unclouded SDK, this little code is all it takes to handle the device-to-device communication of a messenger application. The complete source code of this app can be found in our GitHub repository and is available under MIT license.