fi.harism.lucidchat.api.ChatConnection.java Source code

Java tutorial

Introduction

Here is the source code for fi.harism.lucidchat.api.ChatConnection.java

Source

/*
   Copyright 2013 Harri Smatt
    
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
    
   http://www.apache.org/licenses/LICENSE-2.0
    
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

package fi.harism.lucidchat.api;

import java.net.URI;
import java.util.List;
import java.util.Vector;

import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;
import fi.harism.lucidchat.websocket.WebSocketClient;

/**
 * Chat connection handler.
 */
public class ChatConnection {

    // Event constants.
    private static final int EVENT_DELETE_USER = 1;
    private static final int EVENT_DESCRIBE_USER = 2;
    private static final int EVENT_JOIN = 3;
    private static final int EVENT_MESSAGE = 4;
    private static final int EVENT_PART_CHANNEL = 5;
    private static final int EVENT_SEARCH = 6;

    // URL and lounge channel constants.
    private static final String NINCHAT_LOUNGE = "05am9kp500g";
    private static final String NINCHAT_URL = "wss://api.ninchat.com/socket";

    private boolean mAutojoin;
    private JSONObject mMessage;
    private String mName, mRealName;
    private Observer mObserver;
    private String mUserId, mUserAuth, mSessionId;
    private WebSocketClient mWSC;

    /**
     * Default constructor.
     */
    public ChatConnection(Observer observer) {
        mObserver = observer;
    }

    /**
     * Connect to server with given parameters.
     */
    public void connect(String name, String realName, boolean autojoin) {
        mName = name;
        mRealName = realName;
        mAutojoin = autojoin;
        mUserId = null;
        mUserAuth = null;
        mSessionId = null;

        Vector<BasicNameValuePair> headers = new Vector<BasicNameValuePair>();
        headers.add(new BasicNameValuePair("Sec-WebSocket-Protocol", "ninchat.com-1"));
        mWSC = new WebSocketClient(URI.create(NINCHAT_URL), new WSCListener(), headers);
        mWSC.connect();
        mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Connecting"));
    }

    /**
     * Handle server event messages.
     */
    private void handleEvent(String message) {
        try {
            // We receive always JSON messages.
            JSONObject json = new JSONObject(message);
            // Get event type from JSON.
            String event = json.optString("event");

            // Handle session_created event.
            if (event.equals("session_created")) {

                // Here we expect that server sends user_auth only when new
                // session is created. In case where old session was continues
                // user_auth ought not to be present.
                if (!json.has("user_auth")) {
                    // Notify server about connection.
                    mObserver.onServerMessage(new Message(Message.TYPE_LOG,
                            "Session resumed userId=" + mUserId + " sessionId=" + mSessionId));
                    mObserver.onConnect(mUserId, mUserAuth, mSessionId);
                } else {
                    // New session was created.
                    mUserId = json.getString("user_id");
                    mUserAuth = json.getString("user_auth");
                    mSessionId = json.getString("session_id");
                    mObserver.onServerMessage(new Message(Message.TYPE_LOG,
                            "Session created userId=" + mUserId + " sessionId=" + mSessionId));
                    mObserver.onConnect(mUserId, mUserAuth, mSessionId);
                    // If autojoin is enabled send join message.
                    if (mAutojoin) {
                        sendJoinChannel(NINCHAT_LOUNGE);
                    }
                }
            }

            // Handle user_updated and user_found events.
            if (event.equals("user_updated") || event.equals("user_found")) {
                String userId = json.getString("user_id");
                // If update was sent for ourself.
                if (userId.equals(mUserId)) {
                    // We do nothing on updates sent to self at the moment.
                } else {
                    // Notify observer about user updated event.
                    JSONObject userAttrs = json.getJSONObject("user_attrs");
                    String name = userAttrs.optString("name");
                    String realName = userAttrs.optString("realname");
                    boolean connected = userAttrs.optBoolean("connected");
                    mObserver.onUserUpdated(new User(userId, name, realName, connected));
                }
            }

            // Handle channel_updated event.
            if (event.equals("channel_updated")) {
                String channelId = json.getString("channel_id");
                JSONObject channelAttrs = json.getJSONObject("channel_attrs");
                String name = channelAttrs.optString("name");
                String topic = channelAttrs.optString("topic");
                mObserver.onChannelUpdated(new Channel(channelId, name, topic));
            }

            // Handle search_results event.
            if (event.equals("search_results")) {
                mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Search results event"));
                Vector<Channel> channelList = new Vector<Channel>();
                // Parse channels from search results.
                JSONObject channels = json.optJSONObject("channels");
                if (channels != null) {
                    JSONArray names = channels.names();
                    for (int i = 0; i < names.length(); ++i) {
                        String channelId = names.getString(i);
                        JSONObject channelAttrs = channels.getJSONObject(channelId).getJSONObject("channel_attrs");
                        String name = channelAttrs.optString("name");
                        String topic = channelAttrs.optString("topic");
                        channelList.add(new Channel(channelId, name, topic));
                    }
                }
                // Parse users from search results.
                Vector<User> userList = new Vector<User>();
                JSONObject users = json.optJSONObject("users");
                if (users != null) {
                    JSONArray names = users.names();
                    for (int i = 0; i < names.length(); ++i) {
                        String userId = names.getString(i);
                        if (!userId.equals(mUserId)) {
                            JSONObject user = users.getJSONObject(userId);
                            String name = user.optString("name");
                            String realName = user.optString("realname");
                            boolean connected = user.optBoolean("connected");
                            userList.add(new User(userId, name, realName, connected));
                        }
                    }
                }
                mObserver.onSearchResults(channelList, userList);
            }

            // Handle channel_joined event.
            if (event.equals("channel_joined")) {
                mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Channel joined event"));

                String channelId = json.getString("channel_id");
                Vector<User> userList = new Vector<User>();
                JSONObject channelMembers = json.getJSONObject("channel_members");
                JSONArray names = channelMembers.names();
                for (int i = 0; i < names.length(); ++i) {
                    String userId = names.getString(i);
                    JSONObject userAttrs = channelMembers.getJSONObject(userId).getJSONObject("user_attrs");
                    if (!userId.equals(mUserId)) {
                        String name = userAttrs.optString("name");
                        String realName = userAttrs.optString("realname");
                        boolean connected = userAttrs.optBoolean("connected");
                        userList.add(new User(userId, name, realName, connected));
                    }
                }

                JSONObject channelAttrs = json.getJSONObject("channel_attrs");
                String name = channelAttrs.getString("name");
                String topic = channelAttrs.getString("topic");

                mObserver.onChannelJoined(new Channel(channelId, name, topic), userList);
            }

            // Message receiving happens with 2 callbacks. First
            // message_received is sent from server and then an empty JSON with
            // message only.
            if (event.equals("message_received")) {
                // Check message_type. We handle only text messages.
                if (json.optString("message_type").equals("ninchat.com/text")) {
                    mMessage = json;
                }
                if (json.optString("message_type").equals("ninchat.com/info")) {
                    mMessage = json;
                }
            }
            // Second part of text message receiving.
            if (mMessage != null && mMessage.optString("message_type").equals("ninchat.com/text")
                    && json.has("text")) {
                // Message time is in seconds after epoch.
                long messageTime = mMessage.getLong("message_time") * 1000;
                String messageUserName = mMessage.getString("message_user_name");
                String channelId = mMessage.optString("channel_id");
                String userId = mMessage.optString("user_id");
                String text = json.getString("text");
                mMessage = null;

                // If channel_id exists notify observer about channel message.
                if (channelId.length() > 0) {
                    mObserver.onChannelMessage(channelId,
                            new Message(Message.TYPE_CONVERSATION, messageTime, messageUserName, text));
                }
                // If user_id exists notify observer about private message.
                if (userId.length() > 0) {
                    User user = new User(userId, messageUserName, "", true);
                    mObserver.onUserMessage(user,
                            new Message(Message.TYPE_CONVERSATION, messageTime, messageUserName, text));
                }
            }
            if (mMessage != null && mMessage.optString("message_type").equals("ninchat.com/info")
                    && json.has("info")) {
                String info = json.getString("info");
                if (info.equals("join")) {
                    String channelId = mMessage.getString("channel_id");
                    String userId = json.getString("user_id");
                    String userName = json.getString("user_name");
                    mObserver.onInfoJoin(channelId, userId, userName);
                }
                if (info.equals("part")) {
                    String channelId = mMessage.getString("channel_id");
                    String userId = json.getString("user_id");
                    String userName = json.getString("user_name");
                    mObserver.onInfoPart(channelId, userId, userName);
                }

                mMessage = null;
            }

            // Handle channel_parted event.
            if (event.equals("channel_parted")) {
                String channelId = json.getString("channel_id");
                mObserver.onChannelParted(channelId);
            }

            // Handle channel_member_joined.
            if (event.equals("channel_member_joined")) {
                String channelId = json.getString("channel_id");
                String userId = json.getString("user_id");
                JSONObject userAttrs = json.getJSONObject("user_attrs");
                String name = userAttrs.getString("name");
                String realname = userAttrs.optString("realname");
                boolean connected = userAttrs.optBoolean("connected");
                mObserver.onUserJoin(channelId, new User(userId, name, realname, connected));
            }

            // Handle channel_member_parted.
            if (event.equals("channel_member_parted")) {
                String channelId = json.getString("channel_id");
                String userId = json.getString("user_id");
                mObserver.onUserPart(channelId, userId);
            }

            // Handle second phase of delete_user.
            if (event.equals("user_deleted")) {
                mWSC.disconnect();
            }

        } catch (JSONException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Reconnects to server with given user id and session id.
     */
    public void reconnect(String userId, String userAuth, String sessionId) {
        mName = null;
        mRealName = null;
        mAutojoin = false;
        mUserId = userId;
        mUserAuth = userAuth;
        mSessionId = sessionId;

        Vector<BasicNameValuePair> headers = new Vector<BasicNameValuePair>();
        headers.add(new BasicNameValuePair("Sec-WebSocket-Protocol", "ninchat.com-1"));
        mWSC = new WebSocketClient(URI.create(NINCHAT_URL), new WSCListener(), headers);
        mWSC.connect();
        mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Connecting"));
    }

    /**
     * Disconnect from server.
     */
    public void sendCloseSession() {
        if (mWSC != null) {
            mWSC.send("{ \"action\": \"close_session\" }");
        }
    }

    /**
     * Send create_session message.
     */
    private void sendCreateSession() {
        try {
            mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Send create session"));
            JSONObject createSession = new JSONObject();
            createSession.put("action", "create_session");

            // Setting user_id and user_auth will continue existing session.
            // This happens only when client disconnects unexpectedly. By
            // default new session is created when application starts.
            if (mUserId != null && mUserAuth != null) {
                createSession.put("user_id", mUserId);
                createSession.put("user_auth", mUserAuth);
            } else {
                JSONObject userAttrs = new JSONObject();
                userAttrs.put("name", mName);
                userAttrs.put("realname", mRealName);
                createSession.put("user_attrs", userAttrs);
            }

            JSONArray message_types = new JSONArray();
            message_types.put("*");
            createSession.put("message_types", message_types);

            mWSC.send(createSession.toString());
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Sends delete_user message for currently logged in user.
     */
    public void sendDeleteUser() {
        if (mWSC != null && mUserAuth != null) {
            try {
                JSONObject deleteUser = new JSONObject();
                deleteUser.put("action", "delete_user");
                deleteUser.put("action_id", EVENT_DELETE_USER);
                deleteUser.put("user_auth", mUserAuth);
                mWSC.send(deleteUser.toString());
            } catch (JSONException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Send describe_user message. On completion client will be notified via
     * observer.
     */
    public void sendDescribeUser(String userId) {
        try {
            JSONObject describeUser = new JSONObject();
            describeUser.put("action", "describe_user");
            describeUser.put("action_id", EVENT_DESCRIBE_USER);
            describeUser.put("user_id", userId);
            mWSC.send(describeUser.toString());
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Send join_channel message. On completion client will be notified via on
     * joined callback.
     */
    public void sendJoinChannel(String channelId) {
        try {
            mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Send join channelId=" + channelId));
            JSONObject join_channel = new JSONObject();
            join_channel.put("action", "join_channel");
            join_channel.put("action_id", EVENT_JOIN);
            join_channel.put("channel_id", channelId);
            mWSC.send(join_channel.toString());
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Send message to channel. On completion client will be notified via on
     * channel callback.
     */
    public void sendMessageChannel(String channelId, String text) {
        if (text.length() > 0) {
            try {
                JSONObject sendMessage = new JSONObject();
                sendMessage.put("action", "send_message");
                sendMessage.put("action_id", EVENT_MESSAGE);
                sendMessage.put("channel_id", channelId);
                sendMessage.put("message_type", "ninchat.com/text");
                sendMessage.put("frames", 1);
                mWSC.send(sendMessage.toString());
                JSONObject message = new JSONObject();
                message.put("text", text);
                mWSC.send(message.toString());
            } catch (JSONException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Send message to user. On completion client will be notified via user
     * message callback.
     */
    public void sendMessageUser(String userId, String text) {
        if (text.length() > 0) {
            try {
                JSONObject sendMessage = new JSONObject();
                sendMessage.put("action", "send_message");
                sendMessage.put("action_id", EVENT_MESSAGE);
                sendMessage.put("user_id", userId);
                sendMessage.put("message_type", "ninchat.com/text");
                sendMessage.put("frames", 1);
                mWSC.send(sendMessage.toString());
                JSONObject message = new JSONObject();
                message.put("text", text);
                mWSC.send(message.toString());
            } catch (JSONException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Send part_channel message. Callback will be invoked on completion.
     */
    public void sendPartChannel(String channelId) {
        try {
            JSONObject partChannel = new JSONObject();
            partChannel.put("action", "part_channel");
            partChannel.put("action_id", EVENT_PART_CHANNEL);
            partChannel.put("channel_id", channelId);
            mWSC.send(partChannel.toString());
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Send search message. Results will be sent via callback.
     */
    public void sendSearch(String searchText) {
        if (searchText.length() > 0) {
            try {
                mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Send search \"" + searchText + "\""));
                JSONObject search = new JSONObject();
                search.put("action", "search");
                search.put("action_id", EVENT_SEARCH);
                search.put("search_term", searchText);
                mWSC.send(search.toString());
            } catch (JSONException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * ChatListener interface for handling chat events.
     */
    public interface Observer {

        /**
         * Sent once user joined a channel.
         */
        public void onChannelJoined(Channel channel, List<User> participants);

        /**
         * Sent once user receives a channel message.
         */
        public void onChannelMessage(String channelId, Message message);

        /**
         * Sent once user has parted a channel.
         */
        public void onChannelParted(String channelId);

        /**
         * Sent once channel attributes change.
         */
        public void onChannelUpdated(Channel channel);

        /**
         * Sent once client has connected successfully to server.
         */
        public void onConnect(String userId, String userAuth, String sessionId);

        /**
         * Sent on error situations and once finished disconnecting.
         */
        public void onDisconnect();

        /**
         * Sent once a new user joins channel.
         */
        public void onInfoJoin(String channelId, String userId, String name);

        /**
         * Sent once a user parts a channel.
         */
        public void onInfoPart(String channelId, String userId, String name);

        /**
         * Sent on search results received. Please do note that one search
         * request can trigger multiple search finished callbacks.
         */
        public void onSearchResults(List<Channel> channels, List<User> users);

        /**
         * Sent for server messages.
         */
        public void onServerMessage(Message message);

        /**
         * Sent once a new user joins channel.
         */
        public void onUserJoin(String channelId, User user);

        /**
         * Sent on private messages.
         */
        public void onUserMessage(User user, Message message);

        /**
         * Sent once a user parts a channel.
         */
        public void onUserPart(String channelId, String userId);

        /**
         * Sent once user update event occurs.
         */
        public void onUserUpdated(User user);
    }

    /**
     * WebSocketClient listener implementation.
     */
    private class WSCListener implements WebSocketClient.Listener {
        @Override
        public void onConnect() {
            Log.d("ChatHandler", "onConnect");
            mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Connected"));
            sendCreateSession();
        }

        @Override
        public void onDisconnect(int code, String reason) {
            Log.d("ChatHandler", "onDisconnect : " + code + " : " + reason);
            mWSC = null;
            mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Disconnected"));
            mObserver.onDisconnect();
        }

        @Override
        public void onError(Exception error) {
            Log.d("ChatHandler", "onError : " + error.getMessage());
            mObserver.onServerMessage(new Message(Message.TYPE_LOG, "Error : " + error.getMessage()));
            mObserver.onDisconnect();
        }

        @Override
        public void onMessage(byte[] data) {
        }

        @Override
        public void onMessage(String message) {
            if (message.length() > 0) {
                Log.d("ChatHandler", "onMessage : " + message);
                handleEvent(message);
            }
        }
    }

}