package com.mogujie.tt.imlib.db;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import com.mogujie.tt.config.ProtocolConstant;
import com.mogujie.tt.config.SysConstant;
import com.mogujie.tt.entity.MessageInfo;
import com.mogujie.tt.imlib.IMLoginManager;
import com.mogujie.tt.imlib.IMUnAckMsgManager;
import com.mogujie.tt.imlib.proto.MessageEntity;
import com.mogujie.tt.log.Logger;
public class IMDbManager extends SQLiteOpenHelper {
private static final int DB_VERSION = 4;
private static final String TABLE_SESSION_MSG = "session_msg";
private Logger logger = Logger.getLogger(IMDbManager.class);
private static IMDbManager inst;
public static synchronized IMDbManager instance(Context ctx) {
if (inst == null) {
inst = new IMDbManager(ctx, "tt.db", null, DB_VERSION);
}
return inst;
}
public IMDbManager(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
private void createMsgTable(SQLiteDatabase db) {
logger.d("db#createMsgTable");
String sql = "create table if not exists session_msg ("
+ "id int auto increment primary key,"
+ "login_id varchar(50) not null,"
+ "msg_id varchar(50) not null,"
+ "gmt_created datetime not null,"
+ "gmt_modified datetime not null,"
+ "session_id varchar(50) not null,"
+ "session_type int not null,"
+ "from_id varchar(50) not null,"
+ "to_id varchar(50) not null," + "time int not null,"
+ "type int not null," + "display_type int not null,"
+ "status int default 2 not null,"
+ "int_reserved1 int default 0,"
+ "int_reserved2 int default 0,"
+ "string_reserved1 text default '',"
+ "string_reserved2 text default ''," + "content text)";
logger.d("db#create session_msg table -> sql:%s", sql);
db.execSQL(sql);
}
private void createLoginTable(SQLiteDatabase db) {
logger.d("db#createLoginTable");
String sql = "create table if not exists login_identity ("
+ "id int auto increment primary key,"
+ "login_id varchar(50) not null,"
+ "pwd varchar(50) not null,"
+ "gmt_created datetime not null,"
+ "gmt_modified datetime not null)";
logger.d("db#create login_identity table -> sql:%s", sql);
// todo eric check ret value
db.execSQL(sql);
}
private void createConfigurationTable(SQLiteDatabase db) {
logger.d("db#config#createConfigurationTable");
String sql = "create table if not exists configuration ("
+ "id int auto increment primary key,"
+ "category varchar(50) not null,"
+ "key varchar(50) not null," + "value text not null,"
+ "gmt_created datetime not null,"
+ "gmt_modified datetime not null)";
logger.d("db#create configuration table -> sql:%s", sql);
// todo eric check ret value
db.execSQL(sql);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
logger.d("db#db onCreate");
createMsgTable(db);
createLoginTable(db);
createConfigurationTable(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
public synchronized void deleteMsg(String msgId) {
logger.d("db#deleteMsg, msgId:%s", msgId);
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
logger.e("db#getWritableDatabase failed");
return;
}
String sql = String.format("delete from session_msg where msg_id == '%s'", msgId);
logger.d("db#sql:%s", sql);
db.execSQL(sql);
}
public synchronized void saveMsg(MessageInfo msg, boolean sending) {
logger.d("db#saveMsg, msg:%s", msg);
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
logger.e("db#getWritableDatabase failed");
return;
}
String sessionId = msg.getSessionId(sending);
logger.d("db#sessionId:%s", sessionId);
// String sql =
// String.format("insert into %s (login_id, msg_id, gmt_created, gmt_modified, session_id, session_type, from_id, to_id, time, type, display_type, status, content) values "
// +
// "('%s', '%s', datetime('now'), datetime('now'), '%s', %d, '%s', '%s', %d, %d, %d, %d, '%s')",
// TABLE_SESSION_MSG, IMLoginManager.instance().getLoginId(), msg.msgId,
// sessionId, msg.sessionType, msg.fromId, msg.toId, msg.createTime,
// msg.type, msg.getDisplayType(), msg.getMsgLoadState(),
// msg.getContent());
//
// logger.d("db#saveMsg -> sql:%s", sql);
//
// db.execSQL(sql);
// todo eric, consider sql injection risk thoroughly
String sql = "insert into session_msg (login_id, msg_id, gmt_created, gmt_modified, session_id, session_type, from_id, to_id, time, type, display_type, status, content) values "
+ "(?, ?, datetime('now'), datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?)";
logger.d("db#saveMsg -> sql:%s", sql);
//todo eric use id to reference login id
db.execSQL(sql, new Object[]{IMLoginManager.instance().getLoginId(),
msg.msgId, sessionId, msg.sessionType, msg.fromId, msg.toId,
msg.createTime, msg.type, msg.getDisplayType(),
msg.getMsgLoadState(), msg.getContent()});
}
public synchronized void updatePictureMessagePath(MessageInfo msg) {
if (!msg.isImage()) {
logger.e("db#msg is not picture");
return;
}
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
return;
}
// todo eric use bind
String sqlFormat = "update session_msg set content='%s', status=%d where msg_id='%s'";
String sql = String.format(sqlFormat, msg.getContent(), msg.getMsgLoadState(), msg.msgId);
logger.d("db#updatePictureMessagePath sql:%s", sql);
db.execSQL(sql);
}
public synchronized void updateMessageStatus(MessageInfo msg) {
logger.d("db#updateMessageStatus msg:%s", msg);
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
return;
}
String sqlFormat = "update session_msg set status=%d where msg_id='%s'";
String sql = String.format(sqlFormat, msg.getMsgLoadState(), msg.msgId);
logger.d("db#upateMessageStatus sql:%s", sql);
db.execSQL(sql);
}
public synchronized void updateMessageContent(MessageInfo msg) {
logger.d("db#updateMessageContent msg:%s", msg);
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
return;
}
String sqlFormat = "update session_msg set content=? where msg_id=?";
logger.d("db#upateMessageStatus sql:%s", sqlFormat);
db.execSQL(sqlFormat, new Object[]{msg.getContent(), msg.msgId});
}
public synchronized void saveLoginIdentity(String loginId, String pwd) {
logger.d("db#loginId:%s", loginId);
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
return;
}
// todo eric don't use string +, use preparestament
String sql = String.format("insert into login_identity (login_id, pwd, gmt_created, gmt_modified) values "
+ "('%s', '%s', datetime('now'), datetime('now'))", loginId, pwd);
logger.d("db#saveLoginIdentity -> sql:%s", sql);
// todo eric sql injection
db.execSQL(sql);
}
public synchronized void updateConfiguration(String category, String key,
String value) {
logger.d("db#config#updateConfiguration -> category:%s, key:%s, value:%s", category, key, value);
if (category == null || key == null) {
logger.e("db#config#invalid args");
return;
}
if (value == null) {
value = "";
}
SQLiteDatabase db = getWritableDatabase();
if (db == null) {
return;
}
db.beginTransaction();
try {
String sqlFmt = "delete from configuration where category = ? and key = ?";
logger.d("db#config#sqlFmt:%s", sqlFmt);
db.execSQL(sqlFmt, new Object[]{category, key});
sqlFmt = "insert into configuration (category, key, value, gmt_created, gmt_modified) values (?, ?, ?, datetime('now'), datetime('now'))";
logger.d("db#config#sqlFmt:%s", sqlFmt);
db.execSQL(sqlFmt, new Object[]{category, key, value});
db.setTransactionSuccessful();
} finally {
db.endTransaction();
logger.i("db#config#updateConfiguration transaction ok");
}
}
public synchronized String getConfiguration(String category, String key) {
logger.d("db#config#getConfiguration category:%s, key:%s", category, key);
String sql = "select value from configuration where key = ? order by gmt_created desc limit 1";
logger.d("db#config#sql:%s", sql);
SQLiteDatabase db = getReadableDatabase();
if (db == null) {
logger.e("db#db is null");
return null;
}
Cursor cursor = db.rawQuery(sql, new String[]{key});
for (cursor.moveToFirst(); !cursor.isAfterLast(); /* cursor.moveToNext() */) {
String value = cursor.getString(0);
logger.d("db#config#value:%s", value);
return value;
}
return null;
}
public class LoginIdentity {
public String loginId;
public String pwd;
}
public synchronized LoginIdentity loadLoginIdentity() {
// todo eric optimization
String sql = "select login_id, pwd from login_identity order by gmt_created desc limit 1";
logger.d("db#loadLoginIdentity -> sql:%s", sql);
SQLiteDatabase db = getReadableDatabase();
if (db == null) {
logger.e("db#db is null");
return null;
}
Cursor cursor = db.rawQuery(sql, null);
for (cursor.moveToFirst(); !cursor.isAfterLast(); /* cursor.moveToNext() */) {
LoginIdentity loginIdentity = new LoginIdentity();
loginIdentity.loginId = cursor.getString(0);
loginIdentity.pwd = cursor.getString(1);
logger.d("db#loginId:%s", loginIdentity.loginId);
return loginIdentity;
}
return null;
}
private int refreshMessageStatus(String msgId, int dbStatus) {
if (dbStatus != SysConstant.MESSAGE_STATE_FINISH_SUCCESSED) {
// if it's loading status, but not in unack list, make it failed
// status
// todo eric, picture loading status has 2 steps, handle the
// first step(uploading step)
// status = SysConstant.MESSAGE_STATE_FINISH_FAILED;
MessageInfo unackMsg = IMUnAckMsgManager.instance().get(msgId);
if (unackMsg != null) {
dbStatus = unackMsg.getMsgLoadState();
} else {
if (dbStatus == SysConstant.MESSAGE_STATE_LOADDING) {
// for image, the status is unload
// for unfinished audio or text messages, make the status
// failed
dbStatus = SysConstant.MESSAGE_STATE_FINISH_FAILED;
} else if (dbStatus == SysConstant.MESSAGE_STATE_UNLOAD) {
//do nothing, so image message can still be fetched from the network
}
}
}
return dbStatus;
}
public synchronized List<MessageInfo> getHistoryMsg(String sessionId,
int sessionType, int offset, int count, int firstHistoryMsgTime) {
logger.d("db#getMsg sessionid:%s, sessionType:%d, offset:%d, count:%d, firstHistoryMsgTime:%d", sessionId, sessionType, offset, count, firstHistoryMsgTime);
// todo eric use bind
String sql;
if (firstHistoryMsgTime <= 0) {
sql = String.format("select time, from_id, to_id, type, display_type, status, content, msg_id from %s where login_id = '%s' and session_id = '%s' and session_type = %d order by time desc limit %d offset %d", TABLE_SESSION_MSG, IMLoginManager.instance().getLoginId(), sessionId, sessionType, count, offset);
} else {
sql = String.format("select time, from_id, to_id, type, display_type, status, content, msg_id from %s where login_id = '%s' and session_id = '%s' and session_type = %d and time < %d order by time desc limit %d offset %d", TABLE_SESSION_MSG, IMLoginManager.instance().getLoginId(), sessionId, sessionType, firstHistoryMsgTime, count, offset);
}
logger.d("db#sql %s", sql);
SQLiteDatabase db = getReadableDatabase();
if (db == null) {
logger.e("db#db is null");
return null;
}
Cursor cursor = db.rawQuery(sql, null);
List<MessageInfo> msgList = new ArrayList<MessageInfo>();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
int time = cursor.getInt(0);
String fromId = cursor.getString(1);
String toId = cursor.getString(2);
int msgType = cursor.getInt(3);
int displayType = cursor.getInt(4);
int status = cursor.getInt(5);
String content = cursor.getString(6);
String msgId = cursor.getString(7);
status = refreshMessageStatus(msgId, status);
logger.d("db#fetch msg from db -> time:%d, fromId:%s, toId:%s, msgType:%d, renderType:%d, status:%d, content:%s, msgId:%s", time, fromId, toId, msgType, displayType, status, content, msgId);
MessageInfo msgInfo = new MessageInfo();
msgInfo.msgId = msgId;
msgInfo.setCreated(time);
msgInfo.setMsgFromUserId(fromId);
msgInfo.setTargetId(toId);
msgInfo.setMsgType((byte) msgType); // todo eric make it byte
msgInfo.setDisplayType((byte) displayType);
msgInfo.setMsgLoadState(status);
msgInfo.setMsgContent(content);
msgInfo.sessionId = sessionId;
msgInfo.sessionType = sessionType;
tryRecoverAudioMsg(msgInfo, content);
tryRecoverPicMsg(msgInfo, content, displayType);
msgList.add(msgInfo);
}
// todo eric
Collections.reverse(msgList);
return msgList;
}
private void tryRecoverAudioMsg(MessageInfo msgInfo, String content) {
if (msgInfo.getMsgType() == ProtocolConstant.MSG_TYPE_P2P_AUDIO
|| msgInfo.getMsgType() == ProtocolConstant.MSG_TYPE_GROUP_AUDIO) {
logger.d("db#audio#tryRecoverAudioMsg");
MessageEntity.AudioInfo audioInfo = MessageEntity.AudioInfo.create(content);
msgInfo.setPlayTime(audioInfo.getLength());
msgInfo.setSavePath(audioInfo.getPath());
msgInfo.setMsgReadStatus(audioInfo.getReadStatus());
}
}
private void tryRecoverPicMsg(MessageInfo msgInfo, String content,
int displayType) {
if (displayType == SysConstant.DISPLAY_TYPE_IMAGE) {
logger.d("pic#DISPLAY_TYPE_IMAGE");
MessageEntity.PicInfo picInfo = MessageEntity.PicInfo.create(content);
if (picInfo != null) {
msgInfo.setSavePath(picInfo.getPath());
msgInfo.setUrl(picInfo.getUrl());
}
}
}
}