2018-SWENG/2018-SWENG-Radius

View on GitHub
app/src/main/java/ch/epfl/sweng/radius/database/ChatlogsUtil.java

Summary

Maintainability
A
2 hrs
Test Coverage
package ch.epfl.sweng.radius.database;

import android.content.Context;
import android.support.v4.util.Pair;

import com.google.firebase.database.DatabaseError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.epfl.sweng.radius.messages.MessageListActivity;
import ch.epfl.sweng.radius.utils.NotificationUtility;

/**
 * This class is an utility to handle conversations
 * Set up the listeners to get Notified when a message is sent
 */
public class ChatlogsUtil implements DBLocationObserver, DBUserObserver{
    private static ChatlogsUtil instance = null;
    private static Map<String, ChatLogs> userChat = new HashMap<>();
    private static Map<String, ChatLogs> topicChat = new HashMap<>();
    private static Map<String, ChatLogs> groupChat = new HashMap<>();
    public int upToDate = -1;

    private Context context;

    /**
     * The singleton constructor
     * @return the only instance of ChatLogsUtil
     */
    public static ChatlogsUtil getInstance(){
        if(instance == null)
            instance = new ChatlogsUtil(null);

        return instance;
    }

    public static ChatlogsUtil getInstance(Context ... context){
        if(instance == null)
            instance = context == null ? new ChatlogsUtil(null) : new ChatlogsUtil(context[0]);

        return instance;
    }

    /**
     * Private contructor, add the class as an observer of OthersInfo and UserInfo
     * @param context
     */
    private ChatlogsUtil(Context context){
        this.context = context;
        OthersInfo.getInstance().addLocationObserver(this);
        UserInfo.getInstance().addUserObserver(this);
        // Read and setUp listener on the ChatList field of current user

        fetchUserChats();
        // Read and setUp listeners on the Group and Topics chats
        fetchChatsAndListen();


    }

    /**
     * Fetch all chats and add listeners for Topics and Groups
     */
    public void fetchChatsAndListen(){
        final List<String> topicChatsID = new ArrayList<>(OthersInfo.getInstance().getTopicsPos().keySet());
        List<String> groupChatsID = new ArrayList<>(OthersInfo.getInstance().getGroupsPos().keySet());
        groupChatsID.addAll(topicChatsID);
        Database.getInstance().readListObjOnce(groupChatsID,
                Database.Tables.CHATLOGS,
                new CallBackDatabase() {
                    @Override
                    public void onFinish(Object value) {
                        for(ChatLogs newChat : (ArrayList<ChatLogs>) value){
                            if(topicChatsID.contains(newChat.getID())){
                                topicChat.put(newChat.getID(), newChat);
                                listenToChatMessages(newChat, 2);

                            }
                            else{
                                groupChat.put(newChat.getID(), newChat);
                                listenToChatMessages(newChat, 1);
                            }
                            listenToChatMembers(newChat); }upToDate++;
                    }

                    @Override
                    public void onError(DatabaseError error) {

                    }
                });

    }

    /**
     * Fetch all chats and add listeners private messages
     */
    public void fetchUserChats(){
        Pair<String,Class> chatlistChild = new Pair<String, Class>("chatList", String.class);
        System.out.print(chatlistChild.first);

        Database.getInstance().listenObjChild(UserInfo.getInstance().getCurrentUser(),
                Database.Tables.USERS,
                chatlistChild,
                new CallBackDatabase() {
                    @Override
                    public void onFinish(Object value) {
                        fetchSingleChatAndListen((String) value, 0);

                    }

                    public void onError(DatabaseError error) {

                    }
                }
        );
    }

    /**
     * Getter for the ChatLogs
     * @param chatID the id of the chatlog we want
     * @param chatType the type of chatlog, GROUP, TOPICS or PRIVATE
     * @return the corresponding chatlog or null if it doesn't exist
     */
    public ChatLogs getChat(String chatID, int chatType){
        ChatLogs ret = null;
        switch (chatType){
            case 0:
                ret = userChat.get(chatID);break;
            case 1:
                ret = groupChat.get(chatID);break;
            case 2:
                ret = topicChat.get(chatID);break;
        }

        if(ret == null && Database.DEBUG_MODE)

            return ((FakeFirebaseUtility) Database.getInstance()).getChat();

        return ret;
    }

    /**
     * Listen to a particular chat session
     * @param chatLogs the chatlogs in witch we want to be notified when a message is sent
     * @param chatType the type of chatlog, GROUP, TOPICS or PRIVATE
     */
    public void listenToChatMessages(final ChatLogs chatLogs, final int chatType){
        Pair<String, Class> child = new Pair<String, Class>("messages", Message.class);
        Database.getInstance().listenObjChild(chatLogs, Database.Tables.CHATLOGS, child, new CallBackDatabase() {
            public void onFinish(Object value) {
                receiveMessage(chatLogs, (Message) value, chatType);
            }

            @Override
            public void onError(DatabaseError error) {

            }
        });
    }

    /**
     * Handle Notifications when a message is receive
     * @param chatLogs the chatlogs in witch a message is sent
     * @param message the message itself
     * @param chatType the type of chatlog, GROUP, TOPICS or PRIVATE
     */
    public void receiveMessage(ChatLogs chatLogs, Message message, int chatType){
        MessageListActivity messageActivity = MessageListActivity.getChatInstance(chatLogs.getID());
        if(chatLogs.getMessages().contains(message)) return;
        // Add message to local chatlog
        chatLogs.addMessage(message);

        // Setup Sender name to display
        String senderData = NotificationUtility.getChatTitleNotification(chatLogs, message, chatType);

        // Get ChatActivity instance if it exists
        // If return Activity is null, Chat was never opened in the past
        if(messageActivity == null) {
            messageActivity = new MessageListActivity(chatLogs, context, chatType);
        }

        handleActvity(messageActivity, message, senderData, chatLogs);
        // If Chat is running, there's nothing to do here
    }

    private void handleActvity(MessageListActivity mla, Message message, String senderData, ChatLogs chatLogs) {
        if(mla.uiReady) mla.receiveMessage(message);

        if(!mla.getIsChatRunning().isRunning() && upToDate >= 0){
            // Show notification as chat is not running
            mla.showNotification(message.getContentMessage(), senderData, chatLogs.getID());
        }
    }

    /**
     * Create a new ChatLog between 2 users
     * @param otherUserId the id of the other user
     * @return the is of the chatlog that we just created
     */
    public String getNewChat(String otherUserId){
        ChatLogs newChat = new ChatLogs();
        newChat.addMembersId(UserInfo.getInstance().getCurrentUser().getID());
        newChat.addMembersId(otherUserId);

        // Update local data
        userChat.put(newChat.getID(), newChat);
        UserInfo.getInstance().getCurrentUser().addChat(otherUserId, newChat.getID());
        OthersInfo.getInstance().getUsers().get(otherUserId).addChat(UserInfo.getInstance().getCurrentUser().getID(),
                                                                    newChat.getID()); // Should always be there

        // Update database
        Database.getInstance().writeInstanceObj(newChat, Database.Tables.CHATLOGS);
        UserInfo.getInstance().updateUserInDB();
        Database.getInstance().writeInstanceObj(OthersInfo.getInstance().getUsers().get(otherUserId), Database.Tables.USERS);

        return newChat.getID();
    }

    /**
     * Listen every member of a chat to make sure there are still in radius
     * @param chatLogs the chatlog id we want to listen
     */
    public void listenToChatMembers(final ChatLogs chatLogs){
        Pair<String, Class> child = new Pair<String, Class>("membersId", String.class);
        Database.getInstance().listenObjChild(chatLogs, Database.Tables.CHATLOGS, child, new CallBackDatabase() {
            public void onFinish(Object value) {
                String newMemberId = (String) value;
                chatLogs.addMembersId(newMemberId);
                if(MessageListActivity.getChatInstance(chatLogs.getID()) != null){
                    MessageListActivity.getChatInstance(chatLogs.getID()).addMembersInfo(newMemberId);
                }

            }
            @Override
            public void onError(DatabaseError error) {

            }
        });
    }

    /**
     * Fetch a chat and listen for new messages and users modifications
     * @param chatID the chatlog id we want to listen
     * @param chatType chatType the type of chatlog, GROUP, TOPICS or PRIVATE
     */
    public void fetchSingleChatAndListen(final String chatID, final int chatType){
        Database.getInstance().readObjOnce(new ChatLogs(chatID),
                Database.Tables.CHATLOGS,
                new CallBackDatabase() {
                    @Override
                    public void onFinish(Object value) {
                        ChatLogs newChat = (ChatLogs) value;
                        switch (chatType){
                            case 0:
                                if(userChat.containsKey(newChat.getID())) return;
                                userChat.put(chatID, newChat); break;
                            case 1:
                                if(groupChat.containsKey(newChat.getID())) return;
                                groupChat.put(newChat.getID(), newChat); break;
                            case 2:
                                if(topicChat.containsKey(newChat.getID())) return;
                                topicChat.put(newChat.getID(), newChat);
                        }
                        listenToChatMessages(newChat, chatType);
                        listenToChatMembers(newChat);
                    }

                    @Override
                    public void onError(DatabaseError error) {

                    }
                });
    }

    @Override
    public void onLocationChange(String id) {
        if(groupChat.size() == 0){
            fetchChatsAndListen();
            return;
        }
        for(String s : new ArrayList<>(OthersInfo.getInstance().getGroupsPos().keySet()))
            fetchSingleChatAndListen(s, 1);
        for(String s : new ArrayList<>(OthersInfo.getInstance().getTopicsPos().keySet()))
            fetchSingleChatAndListen(s, 2);
    }

    @Override
    public void onUserChange(String id) {
        for(Map.Entry<String, String> s : UserInfo.getInstance().getCurrentUser().getChatList().entrySet())
            if(!userChat.keySet().contains(s.getValue()))
                fetchSingleChatAndListen(s.getValue(), 0);
    }
}