mirror of
https://github.com/ChronosX88/Influence.git
synced 2024-12-27 08:41:45 +00:00
Implemented Message Archive Management (XEP-313)
This commit is contained in:
parent
bd732fa825
commit
2f454ec05f
@ -2,11 +2,11 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "2409c873b47ccd635ed7d10e4d8604f8",
|
||||
"identityHash": "b15f54b6ea1393c5d45bb222f98ed7d6",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageID` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jid` TEXT, `senderJid` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT, `isSent` INTEGER NOT NULL, `isRead` INTEGER NOT NULL)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageID` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chatID` TEXT, `messageUid` TEXT, `senderJid` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT, `isSent` INTEGER NOT NULL, `isRead` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "messageID",
|
||||
@ -15,8 +15,14 @@
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "jid",
|
||||
"columnName": "jid",
|
||||
"fieldPath": "chatID",
|
||||
"columnName": "chatID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageUid",
|
||||
"columnName": "messageUid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
@ -102,7 +108,7 @@
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"2409c873b47ccd635ed7d10e4d8604f8\")"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"b15f54b6ea1393c5d45bb222f98ed7d6\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "b9b5e578f9222b641c833e4ba5e2378c",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageID` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chatID` TEXT, `messageUid` TEXT, `senderJid` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT, `isSent` INTEGER NOT NULL, `isRead` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "messageID",
|
||||
"columnName": "messageID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatID",
|
||||
"columnName": "chatID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageUid",
|
||||
"columnName": "messageUid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "senderJid",
|
||||
"columnName": "senderJid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSent",
|
||||
"columnName": "isSent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRead",
|
||||
"columnName": "isRead",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"messageID"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jid` TEXT NOT NULL, `chatName` TEXT, `users` TEXT, `unreadMessagesCount` INTEGER NOT NULL, `lastMessageUid` TEXT, PRIMARY KEY(`jid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "jid",
|
||||
"columnName": "jid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatName",
|
||||
"columnName": "chatName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "users",
|
||||
"columnName": "users",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMessagesCount",
|
||||
"columnName": "unreadMessagesCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastMessageUid",
|
||||
"columnName": "lastMessageUid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"jid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"b9b5e578f9222b641c833e4ba5e2378c\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "86a2e626209f2c93a3c961821c967d5c",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageID` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chatID` TEXT, `messageUid` TEXT, `senderJid` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT, `isSent` INTEGER NOT NULL, `isRead` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "messageID",
|
||||
"columnName": "messageID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatID",
|
||||
"columnName": "chatID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageUid",
|
||||
"columnName": "messageUid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "senderJid",
|
||||
"columnName": "senderJid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSent",
|
||||
"columnName": "isSent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRead",
|
||||
"columnName": "isRead",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"messageID"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jid` TEXT NOT NULL, `chatName` TEXT, `users` TEXT, `unreadMessagesCount` INTEGER NOT NULL, `firstMessageUid` TEXT, PRIMARY KEY(`jid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "jid",
|
||||
"columnName": "jid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatName",
|
||||
"columnName": "chatName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "users",
|
||||
"columnName": "users",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMessagesCount",
|
||||
"columnName": "unreadMessagesCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "firstMessageUid",
|
||||
"columnName": "firstMessageUid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"jid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"86a2e626209f2c93a3c961821c967d5c\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ public class XMPPConnection implements ConnectionListener {
|
||||
public XMPPConnection(Context context) {
|
||||
this.prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
this.context = context;
|
||||
String jid = prefs.getString("jid", null);
|
||||
String jid = prefs.getString("chatID", null);
|
||||
String password = prefs.getString("pass", null);
|
||||
if(jid != null && password != null) {
|
||||
String username = jid.split("@")[0];
|
||||
@ -124,6 +124,8 @@ public class XMPPConnection implements ConnectionListener {
|
||||
try {
|
||||
if(mamManager.isSupported()) {
|
||||
MamManager.getInstanceFor(connection).enableMamForAllMessages();
|
||||
} else {
|
||||
mamManager = null;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
@ -170,17 +172,19 @@ public class XMPPConnection implements ConnectionListener {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
public void sendMessage(EntityBareJid recipientJid, String messageText) {
|
||||
public String sendMessage(EntityBareJid recipientJid, String messageText) {
|
||||
Chat chat = ChatManager.getInstanceFor(connection).chatWith(recipientJid);
|
||||
try {
|
||||
Message message = new Message(recipientJid, Message.Type.chat);
|
||||
message.setBody(messageText);
|
||||
chat.send(message);
|
||||
return message.getStanzaId();
|
||||
} catch (SmackException.NotConnectedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public XMPPTCPConnection getConnection() {
|
||||
@ -240,4 +244,11 @@ public class XMPPConnection implements ConnectionListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MamManager getMamManager() {
|
||||
if(isConnectionAlive()) {
|
||||
return mamManager;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,13 @@ import android.content.Intent
|
||||
import android.view.MenuItem
|
||||
import com.stfalcon.chatkit.dialogs.DialogsListAdapter
|
||||
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
||||
|
||||
import io.github.chronosx88.influence.models.GenericDialog
|
||||
import io.github.chronosx88.influence.models.GenericMessage
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import org.jivesoftware.smack.roster.RosterEntry
|
||||
import org.jivesoftware.smackx.mam.MamManager
|
||||
|
||||
interface CoreContracts {
|
||||
|
||||
@ -81,11 +82,16 @@ interface CoreContracts {
|
||||
interface IChatLogicContract {
|
||||
fun sendMessage(text: String): MessageEntity?
|
||||
fun getUserStatus(): Boolean
|
||||
fun loadMessagesFromMAM(): CompletableFuture<MamManager.MamQuery?>
|
||||
fun loadRecentPageMessages(): CompletableFuture<MamManager.MamQuery?>
|
||||
fun loadLocalMessages(): List<MessageEntity>?
|
||||
}
|
||||
|
||||
interface IChatPresenterContract {
|
||||
fun sendMessage(text: String): Boolean
|
||||
fun loadLocalMessages()
|
||||
fun loadMoreMessages()
|
||||
fun loadRecentPageMessages()
|
||||
fun onDestroy()
|
||||
fun onOptionsItemSelected(item: MenuItem)
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class AppHelper extends MultiDexApplication {
|
||||
|
||||
private static void loadLoginCredentials() {
|
||||
currentLoginCredentials = new LoginCredentials();
|
||||
String jid = preferences.getString("jid", null);
|
||||
String jid = preferences.getString("chatID", null);
|
||||
String password = preferences.getString("pass", null);
|
||||
if(jid != null && password != null) {
|
||||
String username = jid.split("@")[0];
|
||||
@ -104,7 +104,7 @@ public class AppHelper extends MultiDexApplication {
|
||||
|
||||
public static void resetLoginCredentials() {
|
||||
currentLoginCredentials = new LoginCredentials();
|
||||
preferences.edit().remove("jid").apply();
|
||||
preferences.edit().remove("chatID").apply();
|
||||
preferences.edit().remove("pass").apply();
|
||||
}
|
||||
|
||||
@ -126,6 +126,7 @@ public class AppHelper extends MultiDexApplication {
|
||||
|
||||
private void initChatDB() {
|
||||
chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB")
|
||||
.fallbackToDestructiveMigration() // FIXME ONLY FOR TEST ENVIRONMENT! DON'T USE THIS IN PRODUCTION!
|
||||
.allowMainThreadQueries()
|
||||
.build();
|
||||
}
|
||||
|
@ -29,16 +29,16 @@ public class LocalDBWrapper {
|
||||
private static RoomHelper dbInstance = AppHelper.getChatDB();
|
||||
|
||||
public static void createChatEntry(String jid, String chatName) {
|
||||
dbInstance.chatDao().addChat(new ChatEntity(jid, chatName, new ArrayList<>(), 0));
|
||||
dbInstance.chatDao().addChat(new ChatEntity(jid, chatName, new ArrayList<>(), 0, ""));
|
||||
}
|
||||
|
||||
public static long createMessageEntry(String jid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) {
|
||||
List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(jid);
|
||||
public static long createMessageEntry(String chatID, String messageUid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) {
|
||||
List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID);
|
||||
if(chatEntities.size() < 1) {
|
||||
Log.e(LOG_TAG, "Failed to create message entry because chat " + jid + " doesn't exists!");
|
||||
Log.e(LOG_TAG, "Failed to create message entry because chat " + chatID + " doesn't exists!");
|
||||
return -1;
|
||||
}
|
||||
MessageEntity message = new MessageEntity(jid, senderJid, timestamp, text, isSent, isRead);
|
||||
MessageEntity message = new MessageEntity(chatID, messageUid, senderJid, timestamp, text, isSent, isRead);
|
||||
long index = dbInstance.messageDao().insertMessage(message);
|
||||
return index;
|
||||
}
|
||||
@ -51,6 +51,14 @@ public class LocalDBWrapper {
|
||||
return messages.get(0);
|
||||
}
|
||||
|
||||
public static MessageEntity getMessageByUID(String messageUID) {
|
||||
List<MessageEntity> messages = dbInstance.messageDao().getMessageByUID(messageUID);
|
||||
if(messages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return messages.get(0);
|
||||
}
|
||||
|
||||
public static List<MessageEntity> getMessagesByChatID(String chatID) {
|
||||
List<MessageEntity> messages = dbInstance.messageDao().getMessagesByChatID(chatID);
|
||||
if(messages.isEmpty()) {
|
||||
@ -85,6 +93,11 @@ public class LocalDBWrapper {
|
||||
return getMessageByID(messageID);
|
||||
}
|
||||
|
||||
public static MessageEntity getFirstMessage(String chatID) {
|
||||
long messageID = dbInstance.messageDao().getFirstMessageByChatID(chatID);
|
||||
return getMessageByID(messageID);
|
||||
}
|
||||
|
||||
public static void updateChatUnreadMessagesCount(String chatID, int unreadMessagesCount) {
|
||||
dbInstance.chatDao().updateUnreadMessagesCount(chatID, unreadMessagesCount);
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package io.github.chronosx88.influence.helpers;
|
||||
import com.instacart.library.truetime.TrueTime;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.jivesoftware.smack.PresenceListener;
|
||||
import org.jivesoftware.smack.chat2.Chat;
|
||||
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
@ -44,7 +43,7 @@ public class NetworkHandler implements IncomingChatMessageListener, PresenceEven
|
||||
if(LocalDBWrapper.getChatByChatID(from.asEntityBareJidString()) == null) {
|
||||
LocalDBWrapper.createChatEntry(chatID, chat.getXmppAddressOfChatPartner().asBareJid().asUnescapedString().split("@")[0]);
|
||||
}
|
||||
long messageID = LocalDBWrapper.createMessageEntry(chatID, from.asUnescapedString(), TrueTime.now().getTime(), message.getBody(), true, false);
|
||||
long messageID = LocalDBWrapper.createMessageEntry(chatID, message.getStanzaId(), from.asUnescapedString(), TrueTime.now().getTime(), message.getBody(), true, false);
|
||||
int newUnreadMessagesCount = LocalDBWrapper.getChatByChatID(chatID).unreadMessagesCount + 1;
|
||||
LocalDBWrapper.updateChatUnreadMessagesCount(chatID, newUnreadMessagesCount);
|
||||
|
||||
|
@ -24,7 +24,7 @@ import io.github.chronosx88.influence.models.daos.MessageDao;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
@Database(entities = { MessageEntity.class, ChatEntity.class }, version = 2)
|
||||
@Database(entities = { MessageEntity.class, ChatEntity.class }, version = 4)
|
||||
|
||||
@TypeConverters({RoomTypeConverter.class})
|
||||
public abstract class RoomHelper extends RoomDatabase {
|
||||
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 ChronosX88
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.github.chronosx88.influence.logic;
|
||||
|
||||
import com.instacart.library.truetime.TrueTime;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatLogic implements CoreContracts.IChatLogicContract {
|
||||
private String chatID;
|
||||
private ChatEntity chatEntity;
|
||||
|
||||
public ChatLogic(ChatEntity chatEntity) {
|
||||
this.chatEntity = chatEntity;
|
||||
this.chatID = chatEntity.jid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageEntity sendMessage(String text) {
|
||||
if (AppHelper.getXmppConnection().isConnectionAlive()) {
|
||||
EntityBareJid jid;
|
||||
try {
|
||||
jid = JidCreate.entityBareFrom(chatEntity.jid);
|
||||
} catch (XmppStringprepException e) {
|
||||
return null;
|
||||
}
|
||||
AppHelper.getXmppConnection().sendMessage(jid, text);
|
||||
while (!TrueTime.isInitialized()) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
TrueTime.build().initialize();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, true, false);
|
||||
return LocalDBWrapper.getMessageByID(messageID);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUserStatus() {
|
||||
if(AppHelper.getXmppConnection() != null) {
|
||||
if(AppHelper.getXmppConnection().isConnectionAlive()) {
|
||||
Presence presence = null;
|
||||
try {
|
||||
presence = AppHelper.getXmppConnection().getUserPresence(JidCreate.bareFrom(chatID));
|
||||
} catch (XmppStringprepException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return presence.isAvailable();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2019 ChronosX88
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.github.chronosx88.influence.logic
|
||||
|
||||
import com.instacart.library.truetime.TrueTime
|
||||
import org.jivesoftware.smack.packet.Presence
|
||||
import org.jxmpp.jid.EntityBareJid
|
||||
import org.jxmpp.jid.impl.JidCreate
|
||||
import org.jxmpp.stringprep.XmppStringprepException
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts
|
||||
import io.github.chronosx88.influence.helpers.AppHelper
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import org.jivesoftware.smackx.mam.MamManager
|
||||
|
||||
class ChatLogic(private val chatEntity: ChatEntity) : CoreContracts.IChatLogicContract {
|
||||
private val chatID: String
|
||||
private var mamManager: MamManager? = null
|
||||
|
||||
init {
|
||||
this.chatID = chatEntity.jid
|
||||
}
|
||||
|
||||
override fun sendMessage(text: String): MessageEntity? {
|
||||
if (AppHelper.getXmppConnection().isConnectionAlive) {
|
||||
val jid: EntityBareJid
|
||||
try {
|
||||
jid = JidCreate.entityBareFrom(chatEntity.jid)
|
||||
} catch (e: XmppStringprepException) {
|
||||
return null
|
||||
}
|
||||
|
||||
val messageUid = AppHelper.getXmppConnection().sendMessage(jid, text)
|
||||
while (!TrueTime.isInitialized()) {
|
||||
Thread {
|
||||
try {
|
||||
TrueTime.build().initialize()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
var timestamp: Long
|
||||
try {
|
||||
timestamp = TrueTime.now().time
|
||||
} catch (e: Exception) {
|
||||
// Fallback to Plain Old Java CurrentTimeMillis
|
||||
timestamp = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
val messageID = LocalDBWrapper.createMessageEntry(chatID, messageUid, AppHelper.getJid(), timestamp, text, true, false)
|
||||
return LocalDBWrapper.getMessageByID(messageID)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUserStatus(): Boolean {
|
||||
if (AppHelper.getXmppConnection() != null) {
|
||||
if (AppHelper.getXmppConnection().isConnectionAlive) {
|
||||
var presence: Presence? = null
|
||||
try {
|
||||
presence = AppHelper.getXmppConnection().getUserPresence(JidCreate.bareFrom(chatID))
|
||||
} catch (e: XmppStringprepException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return presence!!.isAvailable
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun loadMessagesFromMAM(): CompletableFuture<MamManager.MamQuery?> {
|
||||
return CompletableFuture.supplyAsync {
|
||||
if(AppHelper.getXmppConnection() != null) {
|
||||
val mamManager: MamManager? = AppHelper.getXmppConnection().mamManager
|
||||
if(mamManager != null) {
|
||||
val firstMessageUid = LocalDBWrapper.getChatByChatID(chatID).firstMessageUid
|
||||
if(firstMessageUid != "") {
|
||||
return@supplyAsync mamManager.queryArchive(MamManager.MamQueryArgs.builder()
|
||||
.beforeUid(firstMessageUid)
|
||||
.limitResultsToJid(JidCreate.from(chatID))
|
||||
.setResultPageSizeTo(50)
|
||||
.build())
|
||||
} else {
|
||||
return@supplyAsync null
|
||||
}
|
||||
} else {
|
||||
return@supplyAsync null
|
||||
}
|
||||
} else {
|
||||
return@supplyAsync null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadLocalMessages(): List<MessageEntity>? {
|
||||
return LocalDBWrapper.getMessagesByChatID(chatID)
|
||||
}
|
||||
|
||||
override fun loadRecentPageMessages(): CompletableFuture<MamManager.MamQuery?> {
|
||||
return CompletableFuture.supplyAsync {
|
||||
if(AppHelper.getXmppConnection() != null) {
|
||||
val mamManager: MamManager? = AppHelper.getXmppConnection().mamManager
|
||||
if(mamManager != null) {
|
||||
return@supplyAsync mamManager.queryMostRecentPage(JidCreate.from(chatID), 20)
|
||||
} else {
|
||||
return@supplyAsync null
|
||||
}
|
||||
} else {
|
||||
return@supplyAsync null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,12 +25,14 @@ import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class GenericMessage implements IMessage {
|
||||
private long messageID;
|
||||
private String messageUid;
|
||||
private IUser author;
|
||||
private long timestamp;
|
||||
private String text;
|
||||
|
||||
public GenericMessage(MessageEntity messageEntity) {
|
||||
this.messageID = messageEntity.messageID;
|
||||
this.messageUid = messageEntity.messageUid;
|
||||
this.author = new GenericUser(messageEntity.senderJid, messageEntity.senderJid, messageEntity.senderJid);
|
||||
this.timestamp = messageEntity.timestamp;
|
||||
this.text = messageEntity.text;
|
||||
@ -55,4 +57,8 @@ public class GenericMessage implements IMessage {
|
||||
public Date getCreatedAt() {
|
||||
return new Date(timestamp);
|
||||
}
|
||||
|
||||
public String getMessageUid() {
|
||||
return messageUid;
|
||||
}
|
||||
}
|
||||
|
@ -27,16 +27,16 @@ import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
@Dao
|
||||
public interface MessageDao {
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
@Insert
|
||||
long insertMessage(MessageEntity chatModel);
|
||||
|
||||
@Query("DELETE FROM messages WHERE messageID = :messageID")
|
||||
void deleteMessage(String messageID);
|
||||
|
||||
@Query("DELETE FROM messages WHERE jid = :jid")
|
||||
@Query("DELETE FROM messages WHERE chatID = :jid")
|
||||
void deleteMessagesByChatID(String jid);
|
||||
|
||||
@Query("SELECT * FROM messages WHERE jid = :jid")
|
||||
@Query("SELECT * FROM messages WHERE chatID = :jid")
|
||||
List<MessageEntity> getMessagesByChatID(String jid);
|
||||
|
||||
@Query("SELECT * FROM messages WHERE messageID = :messageID")
|
||||
@ -48,9 +48,15 @@ public interface MessageDao {
|
||||
@Query("DELETE FROM messages")
|
||||
void clearMessages();
|
||||
|
||||
@Query("DELETE FROM messages WHERE jid = :chatID")
|
||||
@Query("DELETE FROM messages WHERE chatID = :chatID")
|
||||
void clearMessagesByChatID(String chatID);
|
||||
|
||||
@Query("SELECT messageID FROM messages WHERE jid = :chatID GROUP BY :chatID HAVING MAX(messageID)")
|
||||
@Query("SELECT messageID FROM messages WHERE chatID = :chatID GROUP BY :chatID HAVING MAX(timestamp)")
|
||||
long getLastMessageByChatID(String chatID);
|
||||
|
||||
@Query("SELECT messageID FROM messages WHERE chatID = :chatID GROUP BY :chatID HAVING MIN(timestamp)")
|
||||
long getFirstMessageByChatID(String chatID);
|
||||
|
||||
@Query("SELECT * FROM messages WHERE messageUid = :uid")
|
||||
List<MessageEntity> getMessageByUID(String uid);
|
||||
}
|
||||
|
@ -31,15 +31,17 @@ public class ChatEntity {
|
||||
@ColumnInfo public String chatName;
|
||||
@ColumnInfo public ArrayList<GenericUser> users;
|
||||
@ColumnInfo public int unreadMessagesCount;
|
||||
@ColumnInfo public String firstMessageUid;
|
||||
|
||||
public ChatEntity(@NonNull String jid, String chatName, ArrayList<GenericUser> users, int unreadMessagesCount) {
|
||||
public ChatEntity(@NonNull String jid, String chatName, ArrayList<GenericUser> users, int unreadMessagesCount, String firstMessageUid) {
|
||||
this.jid = jid;
|
||||
this.chatName = chatName;
|
||||
this.users = users;
|
||||
this.unreadMessagesCount = unreadMessagesCount;
|
||||
this.firstMessageUid = firstMessageUid;
|
||||
}
|
||||
|
||||
public boolean isPrivateChat() {
|
||||
return users.size() == 1;
|
||||
return users.size() == 2;
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,17 @@ import androidx.room.PrimaryKey;
|
||||
@Entity(tableName = "messages")
|
||||
public class MessageEntity {
|
||||
@PrimaryKey(autoGenerate = true) public long messageID; // Global message ID
|
||||
@ColumnInfo public String jid; // Chat ID
|
||||
@ColumnInfo public String chatID; // Chat ID
|
||||
@ColumnInfo public String messageUid;
|
||||
@ColumnInfo public String senderJid;
|
||||
@ColumnInfo public long timestamp; // Timestamp
|
||||
@ColumnInfo public String text; // Message text
|
||||
@ColumnInfo public boolean isSent; // Send status indicator
|
||||
@ColumnInfo public boolean isRead; // Message Read Indicator
|
||||
|
||||
public MessageEntity(String jid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) {
|
||||
this.jid = jid;
|
||||
public MessageEntity(String chatID, String messageUid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) {
|
||||
this.chatID = chatID;
|
||||
this.messageUid = messageUid;
|
||||
this.senderJid = senderJid;
|
||||
this.timestamp = timestamp;
|
||||
this.text = text;
|
||||
|
@ -33,18 +33,21 @@ import io.github.chronosx88.influence.models.appEvents.UserPresenceChangedEvent
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import java9.util.stream.StreamSupport
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import kotlin.math.log
|
||||
import org.jivesoftware.smackx.forward.packet.Forwarded
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ChatPresenter(private val view: CoreContracts.IChatViewContract, private val chatID: String) : CoreContracts.IChatPresenterContract {
|
||||
private val logic: CoreContracts.IChatLogicContract
|
||||
private val chatEntity: ChatEntity?
|
||||
private val gson: Gson
|
||||
private val chatAdapter: MessagesListAdapter<GenericMessage>
|
||||
private val messageComparator = Comparator<GenericMessage> { o1, o2 -> o1.createdAt.time.compareTo(o2.createdAt.time) }
|
||||
|
||||
init {
|
||||
this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!)
|
||||
@ -53,6 +56,7 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
|
||||
val holdersConfig = MessageHolders()
|
||||
holdersConfig.setIncomingTextLayout(R.layout.item_incoming_text_message_custom)
|
||||
chatAdapter = MessagesListAdapter(AppHelper.getJid(), holdersConfig, AvatarImageLoader())
|
||||
chatAdapter.setLoadMoreListener { page, _ -> loadMoreMessages() }
|
||||
view.setAdapter(chatAdapter)
|
||||
getUserStatus()
|
||||
EventBus.getDefault().register(this)
|
||||
@ -70,13 +74,14 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
|
||||
}
|
||||
|
||||
override fun loadLocalMessages() {
|
||||
val entities: List<MessageEntity>? = LocalDBWrapper.getMessagesByChatID(chatID)
|
||||
val entities = logic.loadLocalMessages()
|
||||
val messages = ArrayList<GenericMessage>()
|
||||
if(entities != null) {
|
||||
entities.forEach {
|
||||
messages.add(GenericMessage(it))
|
||||
}
|
||||
}
|
||||
messages.sortWith(messageComparator)
|
||||
chatAdapter.addToEnd(messages, true)
|
||||
}
|
||||
|
||||
@ -123,4 +128,58 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadMoreMessages() {
|
||||
logic.loadMessagesFromMAM().thenAccept { query ->
|
||||
if(query != null) {
|
||||
val adapterMessages = ArrayList<GenericMessage>()
|
||||
StreamSupport.stream(query.page.forwarded)
|
||||
.forEach { forwardedMessage ->
|
||||
val message = Forwarded.extractMessagesFrom(Collections.singleton(forwardedMessage))[0]
|
||||
if(message.body != null) {
|
||||
if(LocalDBWrapper.getMessageByUID(message.stanzaId) == null) {
|
||||
val messageID = LocalDBWrapper.createMessageEntry(chatID, message.stanzaId, message.from.asBareJid().asUnescapedString(), forwardedMessage.delayInformation.stamp.time, message.body, true, true)
|
||||
adapterMessages.add(GenericMessage(LocalDBWrapper.getMessageByID(messageID)))
|
||||
}
|
||||
}
|
||||
}
|
||||
AppHelper.getMainUIThread().post {
|
||||
adapterMessages.sortWith(messageComparator)
|
||||
chatAdapter.addToEnd(adapterMessages, true)
|
||||
}
|
||||
if(query.messageCount != 0) {
|
||||
chatEntity!!.firstMessageUid = query.mamResultExtensions[0].id
|
||||
LocalDBWrapper.updateChatEntity(chatEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadRecentPageMessages() {
|
||||
logic.loadRecentPageMessages().thenAccept { query ->
|
||||
if(query != null) {
|
||||
val adapterMessages = ArrayList<GenericMessage>()
|
||||
StreamSupport.stream(query.page.forwarded)
|
||||
.forEach { forwardedMessage ->
|
||||
val message = Forwarded.extractMessagesFrom(Collections.singleton(forwardedMessage))[0]
|
||||
if(message.body != null) {
|
||||
if(LocalDBWrapper.getMessageByUID(message.stanzaId) == null) {
|
||||
val messageID = LocalDBWrapper.createMessageEntry(chatID, message.stanzaId, message.from.asBareJid().asUnescapedString(), forwardedMessage.delayInformation.stamp.time, message.body, true, true)
|
||||
adapterMessages.add(GenericMessage(LocalDBWrapper.getMessageByID(messageID)))
|
||||
}
|
||||
}
|
||||
}
|
||||
AppHelper.getMainUIThread().post {
|
||||
adapterMessages.sortWith(messageComparator)
|
||||
adapterMessages.forEach {
|
||||
chatAdapter.addToStart(it, true)
|
||||
}
|
||||
}
|
||||
if(query.messageCount != 0 && chatEntity!!.firstMessageUid == "") {
|
||||
chatEntity.firstMessageUid = query.mamResultExtensions[0].id
|
||||
LocalDBWrapper.updateChatEntity(chatEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,13 @@ import androidx.appcompat.widget.Toolbar
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.stfalcon.chatkit.commons.ImageLoader
|
||||
import com.stfalcon.chatkit.messages.MessageInput
|
||||
import com.stfalcon.chatkit.messages.MessagesList
|
||||
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
||||
import io.github.chronosx88.influence.R
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts
|
||||
import io.github.chronosx88.influence.helpers.AppHelper
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper
|
||||
import io.github.chronosx88.influence.models.GenericMessage
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
import io.github.chronosx88.influence.presenters.ChatPresenter
|
||||
import kotlinx.android.synthetic.main.activity_chat.view.*
|
||||
import org.jetbrains.anko.find
|
||||
|
||||
class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract {
|
||||
@ -74,6 +69,7 @@ class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract {
|
||||
presenter = ChatPresenter(this, intent.getStringExtra("chatID"))
|
||||
loadAvatarFromIntent(intent)
|
||||
presenter!!.loadLocalMessages()
|
||||
presenter!!.loadRecentPageMessages()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -131,7 +131,7 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
||||
|
||||
private void saveLoginCredentials() {
|
||||
AppHelper.getPreferences().edit()
|
||||
.putString("jid", jidEditText.getText().toString())
|
||||
.putString("chatID", jidEditText.getText().toString())
|
||||
.putString("pass", passwordEditText.getText().toString())
|
||||
.putBoolean("logged_in", true)
|
||||
.apply();
|
||||
|
Loading…
Reference in New Issue
Block a user