package com.xabber.android.data.database;
import android.database.Cursor;
import android.os.Looper;
import com.xabber.android.data.Application;
import com.xabber.android.data.database.messagerealm.MessageItem;
import com.xabber.android.data.database.messagerealm.SyncInfo;
import com.xabber.android.data.database.sqlite.MessageTable;
import com.xabber.android.data.entity.AccountJid;
import com.xabber.android.data.entity.UserJid;
import com.xabber.android.data.log.LogManager;
import org.jxmpp.stringprep.XmppStringprepException;
import java.util.Date;
import io.realm.DynamicRealm;
import io.realm.FieldAttribute;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmMigration;
import io.realm.RealmQuery;
import io.realm.RealmResults;
import io.realm.RealmSchema;
import io.realm.Sort;
import io.realm.annotations.RealmModule;
public class MessageDatabaseManager {
private static final String REALM_MESSAGE_DATABASE_NAME = "xabber.realm";
static final int REALM_MESSAGE_DATABASE_VERSION = 13;
private final RealmConfiguration realmConfiguration;
private static MessageDatabaseManager instance;
private Realm realmUiThread;
public static MessageDatabaseManager getInstance() {
if (instance == null) {
instance = new MessageDatabaseManager();
}
return instance;
}
private MessageDatabaseManager() {
Realm.init(Application.getInstance());
realmConfiguration = createRealmConfiguration();
boolean success = Realm.compactRealm(realmConfiguration);
System.out.println("Realm message compact database file result: " + success);
}
/**
* Creates new realm instance for use from background thread.
* Realm should be closed after use.
*
* @return new realm instance
* @throws IllegalStateException if called from UI (main) thread
*/
public Realm getNewBackgroundRealm() {
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalStateException("Request background thread message realm from UI thread");
}
return Realm.getInstance(realmConfiguration);
}
/**
* Returns realm instance for use from UI (main) thread.
* Do not close realm after use!
*
* @return realm instance for UI thread
* @throws IllegalStateException if called from background thread
*/
public Realm getRealmUiThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("Request UI thread message realm from non UI thread");
}
if (realmUiThread == null) {
realmUiThread = Realm.getInstance(realmConfiguration);
}
return realmUiThread;
}
public static RealmResults<MessageItem> getChatMessages(Realm realm, AccountJid accountJid, UserJid userJid) {
return getChatMessagesQuery(realm, accountJid, userJid)
.findAllSorted(MessageItem.Fields.TIMESTAMP, Sort.ASCENDING);
}
public RealmResults<MessageItem> getChatMessagesAsync(AccountJid accountJid, UserJid userJid) {
return getChatMessagesQuery(getRealmUiThread(), accountJid, userJid)
.findAllSortedAsync(MessageItem.Fields.TIMESTAMP, Sort.ASCENDING);
}
public static RealmQuery<MessageItem> getChatMessagesQuery(Realm realm, AccountJid accountJid, UserJid userJid) {
return realm.where(MessageItem.class)
.equalTo(MessageItem.Fields.ACCOUNT, accountJid.toString())
.equalTo(MessageItem.Fields.USER, userJid.toString())
.isNotNull(MessageItem.Fields.TEXT)
.isNotEmpty(MessageItem.Fields.TEXT);
}
void deleteRealm() {
Realm realm = getNewBackgroundRealm();
Realm.deleteRealm(realm.getConfiguration());
realm.close();
}
public void removeAccountMessages(final AccountJid account) {
Realm realm = getNewBackgroundRealm();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.where(MessageItem.class)
.equalTo(MessageItem.Fields.ACCOUNT, account.toString())
.findAll()
.deleteAllFromRealm();
realm.where(SyncInfo.class)
.equalTo(SyncInfo.FIELD_ACCOUNT, account.toString())
.findAll()
.deleteAllFromRealm();
}
});
realm.close();
}
@RealmModule(classes = {MessageItem.class, SyncInfo.class})
static class MessageRealmDatabaseModule {
}
private RealmConfiguration createRealmConfiguration() {
return new RealmConfiguration.Builder()
.name(REALM_MESSAGE_DATABASE_NAME)
.schemaVersion(REALM_MESSAGE_DATABASE_VERSION)
.modules(new MessageRealmDatabaseModule())
.migration(new RealmMigration() {
@Override
public void migrate(DynamicRealm realm1, long oldVersion, long newVersion) {
RealmSchema schema = realm1.getSchema();
if (oldVersion == 1) {
schema.create(SyncInfo.class.getSimpleName())
.addField(SyncInfo.FIELD_ACCOUNT, String.class, FieldAttribute.INDEXED)
.addField(SyncInfo.FIELD_USER, String.class, FieldAttribute.INDEXED)
.addField(SyncInfo.FIELD_FIRST_MAM_MESSAGE_MAM_ID, String.class)
.addField(SyncInfo.FIELD_FIRST_MAM_MESSAGE_STANZA_ID, String.class)
.addField(SyncInfo.FIELD_LAST_MESSAGE_MAM_ID, String.class)
.addField(SyncInfo.FIELD_REMOTE_HISTORY_COMPLETELY_LOADED, boolean.class);
oldVersion++;
}
if (oldVersion == 2) {
schema.create(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.UNIQUE_ID, String.class, FieldAttribute.PRIMARY_KEY)
.addField(MessageItem.Fields.ACCOUNT, String.class, FieldAttribute.INDEXED)
.addField(MessageItem.Fields.USER, String.class, FieldAttribute.INDEXED)
.addField(MessageItem.Fields.RESOURCE, String.class)
.addField(MessageItem.Fields.ACTION, String.class)
.addField(MessageItem.Fields.TEXT, String.class)
.addField(MessageItem.Fields.TIMESTAMP, Long.class, FieldAttribute.INDEXED)
.addField(MessageItem.Fields.DELAY_TIMESTAMP, Long.class)
.addField(MessageItem.Fields.STANZA_ID, String.class)
.addField(MessageItem.Fields.INCOMING, boolean.class)
.addField(MessageItem.Fields.UNENCRYPTED, boolean.class)
.addField(MessageItem.Fields.SENT, boolean.class)
.addField(MessageItem.Fields.READ, boolean.class)
.addField(MessageItem.Fields.DELIVERED, boolean.class)
.addField(MessageItem.Fields.OFFLINE, boolean.class)
.addField(MessageItem.Fields.ERROR, boolean.class)
.addField(MessageItem.Fields.IS_RECEIVED_FROM_MAM, boolean.class)
.addField(MessageItem.Fields.FILE_PATH, String.class)
.addField(MessageItem.Fields.FILE_SIZE, Long.class);
oldVersion++;
}
if (oldVersion == 3) {
schema.get(MessageItem.class.getSimpleName())
.addIndex(MessageItem.Fields.SENT);
oldVersion++;
}
if (oldVersion == 4) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.FORWARDED, boolean.class);
oldVersion++;
}
if (oldVersion == 5) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.ACKNOWLEDGED, boolean.class);
oldVersion++;
}
if (oldVersion == 6) {
schema.create("LogMessage")
.addField("level", int.class)
.addField("tag", Date.class)
.addField("message", String.class)
.addField("datetime", String.class);
oldVersion++;
}
if (oldVersion == 7) {
schema.remove("LogMessage");
oldVersion++;
}
if (oldVersion == 8) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.FILE_URL, String.class);
oldVersion++;
}
if (oldVersion == 9) {
schema.remove("BlockedContactsForAccount");
schema.remove("BlockedContact");
oldVersion++;
}
if (oldVersion == 10) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.IS_IN_PROGRESS, boolean.class);
oldVersion++;
}
if (oldVersion == 11) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.IS_IMAGE, boolean.class);
oldVersion++;
}
if (oldVersion == 12) {
schema.get(MessageItem.class.getSimpleName())
.addField(MessageItem.Fields.IMAGE_WIDTH, Integer.class)
.addField(MessageItem.Fields.IMAGE_HEIGHT, Integer.class);
oldVersion++;
}
}
})
.build();
}
void copyDataFromSqliteToRealm() {
Realm realm = getNewBackgroundRealm();
realm.beginTransaction();
LogManager.i("DatabaseManager", "copying from sqlite to Reaml");
long counter = 0;
Cursor cursor = MessageTable.getInstance().getAllMessages();
while (cursor.moveToNext()) {
try {
MessageItem messageItem = MessageTable.createMessageItem(cursor);
realm.copyToRealm(messageItem);
} catch (XmppStringprepException | UserJid.UserJidCreateException e) {
LogManager.exception(this, e);
}
counter++;
}
cursor.close();
LogManager.i("DatabaseManager", counter + " messages copied to Realm");
LogManager.i("DatabaseManager", "onSuccess. removing messages from sqlite:");
int removedMessages = MessageTable.getInstance().removeAllMessages();
LogManager.i("DatabaseManager", removedMessages + " messages removed from sqlite");
realm.commitTransaction();
realm.close();
}
}