All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.leancloud.im.v2.AVIMMessageStorage Maven / Gradle / Ivy

package cn.leancloud.im.v2;

import cn.leancloud.AVLogger;
import cn.leancloud.codec.Base64;
import cn.leancloud.im.DatabaseDelegate;
import cn.leancloud.im.DatabaseDelegateFactory;
import cn.leancloud.im.InternalConfiguration;
import cn.leancloud.utils.AVUtils;
import cn.leancloud.utils.LogUtil;
import cn.leancloud.utils.StringUtil;
import com.alibaba.fastjson.JSON;

import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class AVIMMessageStorage {
  private static final AVLogger LOGGER = LogUtil.getLogger(AVIMMessageStorage.class);

  public static final int MESSAGE_INNERTYPE_BIN = 1;
  public static final int MESSAGE_INNERTYPE_PLAIN = 0;
  public static final String DB_NAME_PREFIX = "com.avos.avoscloud.im.v2.";
  public static final String MESSAGE_TABLE = "messages";
  public static final String MESSAGE_INDEX = "message_index";
  public static final int DB_VERSION = 10;
  public static final String COLUMN_MESSAGE_ID = "message_id";
  public static final String COLUMN_TIMESTAMP = "timestamp";
  public static final String COLUMN_CONVERSATION_ID = "conversation_id";
  public static final String COLUMN_FROM_PEER_ID = "from_peer_id";
  public static final String COLUMN_MESSAGE_DELIVEREDAT = "receipt_timestamp";
  public static final String COLUMN_MESSAGE_READAT = "readAt";
  public static final String COLUMN_MESSAGE_UPDATEAT = "updateAt";
  public static final String COLUMN_PAYLOAD = "payload";
  public static final String COLUMN_STATUS = "status";
  public static final String COLUMN_BREAKPOINT = "breakpoint";
  public static final String COLUMN_DEDUPLICATED_TOKEN = "dtoken";
  public static final String COLUMN_MSG_MENTION_ALL = "mentionAll";
  public static final String COLUMN_MSG_MENTION_LIST = "mentionList";
  public static final String COLUMN_MSG_INNERTYPE = "iType";

  public static final String CONVERSATION_TABLE = "conversations";
  public static final String COLUMN_EXPIREAT = "expireAt";
  public static final String COLUMN_ATTRIBUTE = "attr";
  public static final String COLUMN_INSTANCEDATA = "instanceData";
  public static final String COLUMN_UPDATEDAT = "updatedAt";
  public static final String COLUMN_CREATEDAT = "createdAt";
  public static final String COLUMN_CREATOR = "creator";
  public static final String COLUMN_MEMBERS = "members";
  public static final String COLUMN_LM = "lm";
  public static final String COLUMN_LASTMESSAGE = "last_message";
  public static final String COLUMN_TRANSIENT = "isTransient";
  public static final String COLUMN_UNREAD_COUNT = "unread_count";
  public static final String COLUMN_CONV_MENTIONED = "mentioned";
  public static final String COLUMN_CONVERSATION_READAT = "readAt";
  public static final String COLUMN_CONVRESATION_DELIVEREDAT = "deliveredAt";
  public static final String COLUMN_CONV_LASTMESSAGE_INNERTYPE = "last_msg_iType";
  public static final String COLUMN_CONV_TEMP = "temp";
  public static final String COLUMN_CONV_TEMP_TTL = "temp_ttl";
  public static final String COLUMN_CONV_SYSTEM = "sys";

  public static final String NUMBERIC = "NUMBERIC";
  public static final String INTEGER = "INTEGER";
  public static final String BLOB = "BLOB";
  public static final String TEXT = "TEXT";
  public static final String VARCHAR32 = "VARCHAR(32)";

  public static class SQL {
    static final String TIMESTAMP_MORE_OR_TIMESTAMP_EQUAL_BUT_MESSAGE_ID_MORE_AND_CONVERSATION_ID =
            " ( " +
                    COLUMN_TIMESTAMP + " > ? or (" + COLUMN_TIMESTAMP + " = ? and " + COLUMN_MESSAGE_ID
                    + " > ? )) and " +
                    COLUMN_CONVERSATION_ID + " = ? ";

    static final String TIMESTAMP_LESS_AND_CONVERSATION_ID = COLUMN_TIMESTAMP + " < ? and "
            + COLUMN_CONVERSATION_ID + " = ? ";

    // 在时间戳第一排序,MessageId 第二排序的情况下,找到时间戳小于,或者时间戳等于但MessageId小于的消息
    // 三条消息(时间戳/MessageId) 2/a、1/a、1/b, 则用 1/b 来找历史消息的时候,返回1/a
    static final String TIMESTAMP_LESS_OR_TIMESTAMP_EQUAL_BUT_MESSAGE_ID_LESS_AND_CONVERSATION_ID =
            " ( " +
                    COLUMN_TIMESTAMP + " < ? or (" + COLUMN_TIMESTAMP + " = ? and " + COLUMN_MESSAGE_ID
                    + " < ? )) and " +
                    COLUMN_CONVERSATION_ID + " = ? ";

    static final String ORDER_BY_TIMESTAMP_DESC_THEN_MESSAGE_ID_DESC =
            COLUMN_TIMESTAMP + " desc, " + COLUMN_MESSAGE_ID + " desc";

    static final String ORDER_BY_TIMESTAMP_ASC_THEN_MESSAGE_ID_ASC =
            COLUMN_TIMESTAMP + " , " + COLUMN_MESSAGE_ID;

    static final String DELETE_LOCAL_MESSAGE = COLUMN_CONVERSATION_ID + " = ? and " + COLUMN_MESSAGE_ID + " = ? and "
            + COLUMN_STATUS + " = ? and " + COLUMN_DEDUPLICATED_TOKEN + " = ? ";

    static final String SELECT_VALID_CONVS = "("+ COLUMN_CONV_TEMP + " < 1 and " + COLUMN_EXPIREAT + " > ?) or (" + COLUMN_CONV_TEMP + "> 0 and " + COLUMN_CONV_TEMP_TTL + " > ?)";
  }

  public static class MessageQueryResult {
    List messages;
    List breakpoints;
  }

  public interface StorageQueryCallback {
    void done(List messages, List breakpoints);
  }

  public interface StorageMessageCallback {
    void done(AVIMMessage message, boolean breakpoint);
  }

  private static ConcurrentMap storages =
          new ConcurrentHashMap();

  public static AVIMMessageStorage getInstance(String clientId) {
    AVIMMessageStorage storage = storages.get(clientId);
    if (null == storage) {
      storage = new AVIMMessageStorage(clientId);
      AVIMMessageStorage elderStorage = storages.putIfAbsent(clientId, storage);
      if (null != elderStorage) {
        storage = elderStorage;
      }
    }
    return storage;
  }

  private DatabaseDelegate delegate = null;
  private String clientId = null;

  private AVIMMessageStorage(String clientId) {
    this.clientId = clientId;
    DatabaseDelegateFactory factory = InternalConfiguration.getDatabaseDelegateFactory();
    if (null != factory) {
      this.delegate = factory.createInstance(this.clientId);
    }
  }

  public void insertMessage(AVIMMessage message, boolean breakpoint) {
    if (null == message) {
      LOGGER.d("delegate is null, skip insertMessage operation.");
      return;
    }
    insertMessages(Arrays.asList(message), breakpoint);
  }

  private synchronized int insertMessages(List messages, boolean breakpoint) {
    int insertCount = 0;

    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip insertMessages operation.");
      return insertCount;
    }
    for (AVIMMessage message: messages) {
      Map values = new HashMap<>();
      values.put(COLUMN_CONVERSATION_ID, message.getConversationId());
      values.put(COLUMN_MESSAGE_ID, message.getMessageId());
      values.put(COLUMN_TIMESTAMP, message.getTimestamp());
      values.put(COLUMN_FROM_PEER_ID, message.getFrom());
      if (message instanceof AVIMBinaryMessage) {
        values.put(COLUMN_PAYLOAD, ((AVIMBinaryMessage)message).getBytes());
        values.put(COLUMN_MSG_INNERTYPE, MESSAGE_INNERTYPE_BIN);
      } else {
        try {
          values.put(COLUMN_PAYLOAD, message.getContent().getBytes("UTF-8"));
        } catch (UnsupportedEncodingException ex) {
        }
        values.put(COLUMN_MSG_INNERTYPE, MESSAGE_INNERTYPE_PLAIN);
      }
      values.put(COLUMN_MESSAGE_DELIVEREDAT, message.getDeliveredAt());
      values.put(COLUMN_MESSAGE_READAT, message.getReadAt());
      values.put(COLUMN_MESSAGE_UPDATEAT, message.getUpdateAt());
      values.put(COLUMN_STATUS, message.getMessageStatus().getStatusCode());
      values.put(COLUMN_BREAKPOINT, breakpoint ? 1 : 0);

      values.put(COLUMN_MSG_MENTION_ALL, message.isMentionAll()? 1: 0);
      values.put(COLUMN_MSG_MENTION_LIST, message.getMentionListString());
      int insertResult = this.delegate.insert(MESSAGE_TABLE, values);
      if (insertResult >= 0) {
        insertCount++;
      }
    }
    return insertCount;
  }

  public synchronized boolean insertLocalMessage(AVIMMessage message) {
    if (null == message) {
      return false;
    }
    if (!StringUtil.isEmpty(message.getMessageId()) || StringUtil.isEmpty(message.getConversationId())
            || StringUtil.isEmpty(message.getUniqueToken())) {
      return false;
    }
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip insertLocalMessages operation.");
      return true;
    }
    String internalMessageId = generateInternalMessageId(message.getUniqueToken());
    Map values = new HashMap<>();
    values.put(COLUMN_CONVERSATION_ID, message.getConversationId());
    values.put(COLUMN_MESSAGE_ID, internalMessageId);
    values.put(COLUMN_TIMESTAMP, message.getTimestamp());
    values.put(COLUMN_FROM_PEER_ID, message.getFrom());
    if (message instanceof AVIMBinaryMessage) {
      values.put(COLUMN_PAYLOAD, ((AVIMBinaryMessage)message).getBytes());
      values.put(COLUMN_MSG_INNERTYPE, MESSAGE_INNERTYPE_BIN);
    } else {
      try {
        values.put(COLUMN_PAYLOAD, message.getContent().getBytes("UTF-8"));
      } catch (UnsupportedEncodingException ex) {
      }
    }
    values.put(COLUMN_MESSAGE_DELIVEREDAT, message.getDeliveredAt());
    values.put(COLUMN_MESSAGE_READAT, message.getReadAt());
    values.put(COLUMN_MESSAGE_UPDATEAT, message.getUpdateAt());
    values.put(COLUMN_STATUS, AVIMMessage.AVIMMessageStatus.AVIMMessageStatusFailed.getStatusCode());
    values.put(COLUMN_BREAKPOINT, 0);
    values.put(COLUMN_DEDUPLICATED_TOKEN, message.getUniqueToken());

    values.put(COLUMN_MSG_MENTION_ALL, message.isMentionAll()? 1: 0);
    values.put(COLUMN_MSG_MENTION_LIST, message.getMentionListString());
    int ret = this.delegate.insert(MESSAGE_TABLE, values);
    return ret > 0;
  }

  public synchronized boolean removeLocalMessage(AVIMMessage message) {
    if (null == message
            || StringUtil.isEmpty(message.getConversationId())
            || StringUtil.isEmpty(message.getUniqueToken())) {
      return false;
    }
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip removeLocalMessage operation.");
      return true;
    }
    String internalMessageId = generateInternalMessageId(message.getUniqueToken());
    String status = String.valueOf(AVIMMessage.AVIMMessageStatus.AVIMMessageStatusFailed.getStatusCode());
    int ret = this.delegate.delete(MESSAGE_TABLE, SQL.DELETE_LOCAL_MESSAGE,
            new String[]{message.getConversationId(), internalMessageId, status, message.getUniqueToken()});
    return ret > 0;
  }

  public void insertContinuousMessages(List messages, String conversationId) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip insertContinuousMessages operation.");
      return;
    }
    if (null == messages || messages.isEmpty() || StringUtil.isEmpty(conversationId)) {
      return;
    }
    AVIMMessage firstMessage = messages.get(0);
    List tailMessages = messages.subList(1, messages.size());
    AVIMMessage lastMessage = messages.get(messages.size() - 1);
    if (!containMessage(lastMessage)) {
      AVIMMessage eldestMessage = getNextMessage(lastMessage);
      if (eldestMessage != null) {
        updateBreakpoints(Arrays.asList(eldestMessage), true, conversationId);
      }
    }
    if (!tailMessages.isEmpty()) {
      insertMessages(tailMessages, false);
      // remove breakpoints
      updateBreakpoints(tailMessages, false, conversationId);
    }
    insertMessage(firstMessage, true);
  }

  public boolean containMessage(AVIMMessage message) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip containMessage operation.");
      return false;
    }
    int cnt = this.delegate.queryCount(MESSAGE_TABLE, new String[] {},
            getWhereClause(COLUMN_CONVERSATION_ID, COLUMN_MESSAGE_ID),
            new String[] {message.getConversationId(), message.getMessageId()}, null, null, null);
    return cnt > 0;
  }

  protected synchronized void updateBreakpoints(List messages,
                                                boolean breakpoint, String conversationId) {
    int batchSize = 900;
    if (messages.size() > batchSize) {
      updateBreakpointsForBatch(messages.subList(0, batchSize), breakpoint, conversationId);
      updateBreakpoints(messages.subList(batchSize, messages.size()), breakpoint, conversationId);
    } else {
      updateBreakpointsForBatch(messages, breakpoint, conversationId);
    }
  }

  private synchronized int updateBreakpointsForBatch(List messages,
                                                     boolean breakpoint, String conversationId) {
    if (null == this.delegate) {
      return 0;
    }
    String[] arguments = new String[messages.size()];
    List placeholders = new ArrayList();
    int i;
    for (i = 0; i < messages.size(); i++) {
      AVIMMessage message = messages.get(i);
      arguments[i] = message.getMessageId();
      placeholders.add("?");
    }
    Map cv = new HashMap<>();
    cv.put(COLUMN_BREAKPOINT, breakpoint ? 1 : 0);
    String joinedPlaceholders = StringUtil.join(",", placeholders);
    return this.delegate.update(MESSAGE_TABLE, cv, COLUMN_MESSAGE_ID
            + " in (" + joinedPlaceholders + ") ", arguments);
  }

  public synchronized boolean updateMessage(AVIMMessage message, String originalId) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip updateMessage operation.");
      return false;
    }
    Map values = new HashMap<>();
    values.put(COLUMN_TIMESTAMP, message.getTimestamp());
    values.put(COLUMN_STATUS, message.getMessageStatus().getStatusCode());
    values.put(COLUMN_MESSAGE_DELIVEREDAT, message.getDeliveredAt());
    values.put(COLUMN_MESSAGE_READAT, message.getReadAt());
    values.put(COLUMN_MESSAGE_UPDATEAT, message.getUpdateAt());
    values.put(COLUMN_MESSAGE_ID, message.getMessageId());

    values.put(COLUMN_MSG_MENTION_ALL, message.isMentionAll()? 1: 0);
    values.put(COLUMN_MSG_MENTION_LIST, message.getMentionListString());
    int ret = this.delegate.update(MESSAGE_TABLE, values, getWhereClause(COLUMN_MESSAGE_ID), new String[] {originalId});
    return ret > -1;
  }

  synchronized boolean updateMessageForPatch(AVIMMessage message) {
    if (null == this.delegate) {
      return false;
    }
    Map values = new HashMap<>();
    if (message instanceof AVIMBinaryMessage) {
      values.put(COLUMN_PAYLOAD, ((AVIMBinaryMessage)message).getBytes());
      values.put(COLUMN_MSG_INNERTYPE, MESSAGE_INNERTYPE_BIN);
    } else {
      values.put(COLUMN_PAYLOAD, message.getContent());
      values.put(COLUMN_MSG_INNERTYPE, MESSAGE_INNERTYPE_PLAIN);
    }
    values.put(COLUMN_STATUS, message.getMessageStatus().getStatusCode());
    values.put(COLUMN_MESSAGE_UPDATEAT, message.getUpdateAt());
    int ret = this.delegate.update(MESSAGE_TABLE, values, getWhereClause(COLUMN_MESSAGE_ID),
            new String[] {message.getMessageId()});
    return ret > -1;
  }

  public synchronized void deleteMessages(List messages, String conversationId) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip deleteMessages operation.");
      return;
    }
    for (AVIMMessage message : messages) {
      String messageId = message.getMessageId();
      AVIMMessage nextMessage = getNextMessage(message);
      if (nextMessage != null) {
        updateBreakpoints(Arrays.asList(message), true, conversationId);
      }
      this.delegate.delete(MESSAGE_TABLE, getWhereClause(COLUMN_MESSAGE_ID), new String[] {messageId});
    }
  }

  public synchronized void deleteConversationData(String conversationId) {
    if (null == this.delegate) {
      return;
    }
    this.delegate.delete(MESSAGE_TABLE, getWhereClause(COLUMN_CONVERSATION_ID),
            new String[] {conversationId});
    this.delegate.delete(CONVERSATION_TABLE, getWhereClause(COLUMN_CONVERSATION_ID),
            new String[] {conversationId});
  }

  public synchronized void deleteClientData() {
    if (null == this.delegate) {
      return;
    }
    this.delegate.delete(MESSAGE_TABLE, null, null);
    this.delegate.delete(CONVERSATION_TABLE, null, null);
  }

  void getMessage(String msgId, long timestamp, String conversationId,
                  StorageMessageCallback callback) {
    if (timestamp <= 0) {
      callback.done(null, false);
    } else if (null == this.delegate){
      callback.done(null, false);
    } else {
      MessageQueryResult result = null;
      if (msgId == null) {
        result = this.delegate.queryMessages(null, getWhereClause(COLUMN_TIMESTAMP, COLUMN_CONVERSATION_ID),
                new String[] {Long.toString(timestamp), conversationId}, null, null, null, "1");
      } else {
        result = this.delegate.queryMessages(null, getWhereClause(COLUMN_MESSAGE_ID),
                new String[] {msgId}, null, null, null, "1");
      }

      AVIMMessage resultMessage = null;
      boolean resultBreakPoint = false;
      if (null != result) {
        resultMessage = (null != result.messages && result.messages.size() > 0)?result.messages.get(0):null;
        resultBreakPoint = (null != result.breakpoints && result.breakpoints.size() > 0)? result.breakpoints.get(0):false;
      }
      callback.done(resultMessage, resultBreakPoint);
    }
  }

  public void getMessages(String msgId, long timestamp, int limit, String conversationId,
                          StorageQueryCallback callback) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip getMessages operation.");
      callback.done(null, null);
      return;
    }
    String selection;
    String[] selectionArgs;
    if (timestamp > 0) {
      if (msgId == null) {
        selection = SQL.TIMESTAMP_LESS_AND_CONVERSATION_ID;
        selectionArgs = new String[] {Long.toString(timestamp), conversationId};
      } else {
        selection = SQL.TIMESTAMP_LESS_OR_TIMESTAMP_EQUAL_BUT_MESSAGE_ID_LESS_AND_CONVERSATION_ID;
        selectionArgs =
                new String[] {Long.toString(timestamp), Long.toString(timestamp), msgId, conversationId};
      }
    } else {
      selection = getWhereClause(COLUMN_CONVERSATION_ID);
      selectionArgs = new String[] {conversationId};
    }
    MessageQueryResult results = this.delegate.queryMessages(null, selection, selectionArgs, null, null,
            SQL.ORDER_BY_TIMESTAMP_DESC_THEN_MESSAGE_ID_DESC, limit + "");
    if (null == results) {
      callback.done(null, null);
    } else {
      callback.done(results.messages, results.breakpoints);
    }
  }

  public long getMessageCount(String conversationId) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip messageCount operation.");
      return 0l;
    }
    AVIMMessage lastBreakPointMessage = getLatestMessageWithBreakpoint(conversationId, true);
    long messageCount = 0l;
    String query;
    String[] queryArgs;
    if (null == lastBreakPointMessage) {
      query = "select count(*) from " + MESSAGE_TABLE + " where " + COLUMN_CONVERSATION_ID + " = ?";
      queryArgs = new String[] {conversationId};
    } else {
      query = "select count(*) from " + MESSAGE_TABLE + " where "
              + COLUMN_CONVERSATION_ID + " = ? and (" + COLUMN_TIMESTAMP + " > ? or ( "
              + COLUMN_TIMESTAMP + " = ? and "
              + COLUMN_MESSAGE_ID + " >= ? )) order by "
              + SQL.ORDER_BY_TIMESTAMP_DESC_THEN_MESSAGE_ID_DESC;
      queryArgs = new String[] {conversationId, String.valueOf(lastBreakPointMessage.getTimestamp()),
                      String.valueOf(lastBreakPointMessage.getTimestamp()),
                      lastBreakPointMessage.getMessageId()};
    }
    return this.delegate.countForQuery(query, queryArgs);
  }

  protected AVIMMessage getNextMessage(AVIMMessage currentMessage) {
    if (null == this.delegate) {
      return null;
    }
    MessageQueryResult result = this.delegate.queryMessages(null,
            SQL.TIMESTAMP_MORE_OR_TIMESTAMP_EQUAL_BUT_MESSAGE_ID_MORE_AND_CONVERSATION_ID,
            new String[] {Long.toString(currentMessage.getTimestamp()),
                    Long.toString(currentMessage.getTimestamp()),
                    currentMessage.getMessageId(), currentMessage.getConversationId()}, null, null,
            SQL.ORDER_BY_TIMESTAMP_ASC_THEN_MESSAGE_ID_ASC, "1");
    if (null == result || null == result.messages || result.messages.size() < 1) {
      return null;
    }
    return result.messages.get(0);
  }

  AVIMMessage getLatestMessage(String conversationId) {
    if (null == this.delegate) {
      return null;
    }
    MessageQueryResult result = this.delegate.queryMessages(null, getWhereClause(COLUMN_CONVERSATION_ID),
            new String[] {conversationId}, null, null,
            SQL.ORDER_BY_TIMESTAMP_DESC_THEN_MESSAGE_ID_DESC, "1");
    if (null == result || null == result.messages || result.messages.size() < 1) {
      return null;
    }
    return result.messages.get(0);
  }

  AVIMMessage getLatestMessageWithBreakpoint(String conversationId, boolean breakpoint) {
    if (null == this.delegate) {
      return null;
    }
    MessageQueryResult result = this.delegate.queryMessages(null,
            getWhereClause(COLUMN_CONVERSATION_ID, COLUMN_BREAKPOINT),
            new String[] {conversationId, breakpoint ? "1" : "0"}, null, null,
            SQL.ORDER_BY_TIMESTAMP_DESC_THEN_MESSAGE_ID_DESC, "1");
    if (null == result || null == result.messages || result.messages.size() < 1) {
      return null;
    }
    return result.messages.get(0);
  }

  private String generateInternalMessageId(String uniqueToken) {
    if (StringUtil.isEmpty(uniqueToken)) {
      return "";
    }
    return uniqueToken;
  }

  private static String getWhereClause(String... columns) {
    List conditions = new ArrayList();
    for (String column : columns) {
      conditions.add(column + " = ? ");
    }
    return StringUtil.join(" and ", conditions);
  }

  public int insertConversations(List conversations) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip insert Conversations.");
      return 0;
    }
    for (AVIMConversation conversation : conversations) {
      Map values = new HashMap<>();
      values.put(COLUMN_ATTRIBUTE, JSON.toJSONString(conversation.getAttributes()));
      values.put(COLUMN_INSTANCEDATA, JSON.toJSONString(conversation.instanceData));
      values.put(COLUMN_CREATEDAT, conversation.getCreatedAt());
      values.put(COLUMN_UPDATEDAT, conversation.getUpdatedAt());
      values.put(COLUMN_CREATOR, conversation.getCreator());
      values.put(COLUMN_EXPIREAT, System.currentTimeMillis()
              + Conversation.DEFAULT_CONVERSATION_EXPIRE_TIME_IN_MILLS);
      if (conversation.lastMessageAt != null) {
        values.put(COLUMN_LM, conversation.lastMessageAt.getTime());
      }

      final AVIMMessage message = conversation.getLastMessage();
      if (null != message) {
        if (message instanceof AVIMBinaryMessage) {
          byte[] bytes = ((AVIMBinaryMessage)message).getBytes();
          String base64Msg = Base64.encodeToString(bytes, Base64.NO_WRAP);
          values.put(COLUMN_LASTMESSAGE, base64Msg);
          values.put(COLUMN_CONV_LASTMESSAGE_INNERTYPE, MESSAGE_INNERTYPE_BIN);
        } else {
          String lastMessage = JSON.toJSONString(message);
          values.put(COLUMN_LASTMESSAGE, lastMessage);
          values.put(COLUMN_CONV_LASTMESSAGE_INNERTYPE, MESSAGE_INNERTYPE_PLAIN);
        }
      }

      values.put(COLUMN_MEMBERS, JSON.toJSONString(conversation.getMembers()));
      values.put(COLUMN_TRANSIENT, conversation.isTransient() ? 1 : 0);
      values.put(COLUMN_UNREAD_COUNT, conversation.getUnreadMessagesCount());

      values.put(COLUMN_CONV_MENTIONED, conversation.unreadMessagesMentioned()? 1:0);

      values.put(COLUMN_CONVERSATION_READAT, conversation.getLastReadAt());
      values.put(COLUMN_CONVRESATION_DELIVEREDAT, conversation.getLastDeliveredAt());
      values.put(COLUMN_CONVERSATION_ID, conversation.getConversationId());

      // add temporary conversation data.
      values.put(COLUMN_CONV_SYSTEM, conversation.isSystem()? 1 : 0);
      values.put(COLUMN_CONV_TEMP, conversation.isTemporary()? 1 : 0);
      values.put(COLUMN_CONV_TEMP_TTL, conversation.getTemporaryExpiredat());

      int insertResult = this.delegate.insert(CONVERSATION_TABLE, values);
      if (insertResult < 0) {
        LOGGER.d("failed to insert conversation. conversationId=" + conversation.getConversationId());
      }
    }
    return 1;
  }

  public AVIMConversation getConversation(String conversationId) {
    List result = getCachedConversations(Arrays.asList(conversationId));
    if (null == result || result.size() < 1) {
      return null;
    } else {
      return result.get(0);
    }
  }

  public List getCachedConversations(List conversationIds) {
    List conversations = new LinkedList();
    if (null == this.delegate) {
      return conversations;
    }
    return this.delegate.rawQueryConversations("SELECT * FROM " + CONVERSATION_TABLE + " WHERE " + COLUMN_CONVERSATION_ID
            + " in ('" + StringUtil.join("','", conversationIds) + "')", null);
  }

  public void deleteConversation(String conversationId) {
    if (null == this.delegate) {
      LOGGER.d("delegate is null, skip delete operation.");
      return;
    }
    this.delegate.delete(CONVERSATION_TABLE, getWhereClause(COLUMN_CONVERSATION_ID),
            new String[] {conversationId});
  }

  boolean updateConversationTimes(AVIMConversation conversation) {
    if (getConversation(conversation.getConversationId()) != null) {
      Map values = new HashMap<>();
      values.put(COLUMN_CONVERSATION_READAT, conversation.getLastReadAt());
      values.put(COLUMN_CONVRESATION_DELIVEREDAT, conversation.getLastDeliveredAt());
      int ret = this.delegate.update(CONVERSATION_TABLE, values, getWhereClause(COLUMN_CONVERSATION_ID),
              new String[] {conversation.getConversationId()});
      return ret != -1;
    }
    return false;
  }

  boolean updateConversationUreadCount(String conversationId, long unreadCount, boolean mentioned) {
    if (getConversation(conversationId) != null) {
      Map values = new HashMap<>();
      values.put(COLUMN_UNREAD_COUNT, unreadCount);
      values.put(COLUMN_CONV_MENTIONED, mentioned? 1:0);
      int ret = this.delegate.update(CONVERSATION_TABLE, values, getWhereClause(COLUMN_CONVERSATION_ID),
              new String[] {conversationId});
      return ret > -1;
    }
    return false;
  }

  public boolean updateConversationLastMessageAt(AVIMConversation conversation) {
    if (getConversation(conversation.getConversationId()) != null
            && conversation.getLastMessageAt() != null) {
      Map values = new HashMap<>();
      values.put(COLUMN_LM, conversation.getLastMessageAt().getTime());
      int ret = this.delegate.update(CONVERSATION_TABLE, values, getWhereClause(COLUMN_CONVERSATION_ID),
              new String[] {conversation.getConversationId()});
      return ret > -1;
    }
    return false;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy