mirror of
https://github.com/ChronosX88/Influence-P2P.git
synced 2024-11-22 07:12:19 +00:00
[WIP] Made chat works
This commit is contained in:
parent
61e660329d
commit
6a5fdcb733
@ -2,11 +2,11 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "aa6543fc56b99224739cb0b53a63d48e",
|
||||
"identityHash": "a24b31a8e1f482a72f55843041945d5b",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `chatID` TEXT, `sender` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `chatID` TEXT, `sender` TEXT, `timestamp` INTEGER NOT NULL, `text` TEXT, `isSent` INTEGER NOT NULL, `isRead` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@ -43,6 +43,18 @@
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSent",
|
||||
"columnName": "isSent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRead",
|
||||
"columnName": "isRead",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
@ -102,7 +114,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, \"aa6543fc56b99224739cb0b53a63d48e\")"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a24b31a8e1f482a72f55843041945d5b\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".views.ChatActivity"
|
||||
android:theme="@style/NoWindowActionBar"/>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
@ -0,0 +1,7 @@
|
||||
package io.github.chronosx88.influence.contracts;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public interface ChatLogicContract {
|
||||
void sendMessage(PeerAddress address, MessageEntity message);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
public interface ChatPresenterContract {
|
||||
void sendMessage(String text);
|
||||
void updateAdapter();
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public interface ChatViewContract {
|
||||
void updateMessageList(MessageEntity message);
|
||||
void updateMessageList(List<MessageEntity> messages);
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package io.github.chronosx88.influence.contracts.chatlist;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
@ -7,5 +9,6 @@ import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public interface ChatListViewContract {
|
||||
void setRecycleAdapter(ChatListAdapter adapter);
|
||||
void startActivity(Intent intent);
|
||||
void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats);
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.main;
|
||||
|
||||
public interface MainViewContract {
|
||||
//
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.github.chronosx88.influence.contracts.main;
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface MainLogicContract {
|
||||
void initPeer();
|
@ -1,4 +1,4 @@
|
||||
package io.github.chronosx88.influence.contracts.main;
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface MainPresenterContract {
|
||||
void initPeer();
|
@ -0,0 +1,5 @@
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface MainViewContract {
|
||||
//
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {
|
||||
private final static int RIGHT_ITEM = 0;
|
||||
private final static int LEFT_ITEM = 1;
|
||||
private final static int TECHNICAL_MESSAGE = 2; // TODO
|
||||
|
||||
private final static Context context = AppHelper.getContext();
|
||||
private ArrayList<MessageEntity> messages = new ArrayList<>();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ChatAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if(viewType == RIGHT_ITEM) {
|
||||
return new ChatAdapter.ViewHolder(LayoutInflater.from(context).inflate(R.layout.message_right_item, parent, false));
|
||||
} else {
|
||||
return new ChatAdapter.ViewHolder(LayoutInflater.from(context).inflate(R.layout.message_left_item, parent, false));
|
||||
}
|
||||
}
|
||||
|
||||
public void addMessage(MessageEntity message) {
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
public void addMessages(List<MessageEntity> messages) {
|
||||
this.messages.addAll(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatAdapter.ViewHolder holder, int position) {
|
||||
// Setting message text
|
||||
holder.messageText.setText(messages.get(position).text);
|
||||
|
||||
// Setting message time (HOUR:MINUTE)
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(new Date(messages.get(position).timestamp));
|
||||
String time = calendar.get(Calendar.HOUR) + ":" + calendar.get(Calendar.MINUTE);
|
||||
holder.messageTime.setText(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if(messages.get(position).sender.equals(AppHelper.getPeerID())) {
|
||||
return RIGHT_ITEM;
|
||||
} else {
|
||||
return LEFT_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView messageText;
|
||||
CircleImageView profileImage;
|
||||
TextView messageTime;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
messageText = itemView.findViewById(R.id.message_text);
|
||||
profileImage = itemView.findViewById(R.id.profile_image);
|
||||
messageTime = itemView.findViewById(R.id.message_time);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,11 +12,17 @@ import java.util.List;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.ItemClickListener;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public class ChatListAdapter extends RecyclerView.Adapter<ChatListAdapter.ChatListViewHolder> {
|
||||
List<ChatEntity> chatList = new ArrayList<>();
|
||||
public int onClickPosition = -1;
|
||||
private ItemClickListener itemClickListener;
|
||||
|
||||
public ChatListAdapter(ItemClickListener itemClickListener) {
|
||||
this.itemClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@ -41,6 +47,10 @@ public class ChatListAdapter extends RecyclerView.Adapter<ChatListAdapter.ChatLi
|
||||
holder.onLongClick(position);
|
||||
}
|
||||
|
||||
public ChatEntity getChatEntity(int position) {
|
||||
return chatList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return chatList.size();
|
||||
@ -53,6 +63,9 @@ public class ChatListAdapter extends RecyclerView.Adapter<ChatListAdapter.ChatLi
|
||||
super(itemView);
|
||||
chatName = itemView.findViewById(R.id.chat_name);
|
||||
itemView.setOnCreateContextMenuListener(this);
|
||||
itemView.setOnClickListener((v) -> {
|
||||
itemClickListener.onItemClick(v, getAdapterPosition());
|
||||
});
|
||||
}
|
||||
|
||||
public void onLongClick(int position) {
|
||||
|
@ -37,15 +37,43 @@ public class LocalDBWrapper {
|
||||
* @param chatID ID of the chat in which need to create a message
|
||||
* @param sender Message sender (username)
|
||||
* @param text Message text (or technical info if technical message type)
|
||||
* @return
|
||||
* @return Message ID (in local DB)
|
||||
*/
|
||||
public static boolean createMessageEntry(int type, String chatID, String sender, String text) {
|
||||
public static long createMessageEntry(int type, String chatID, String sender, String text) {
|
||||
List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID);
|
||||
if(chatEntities.size() < 1) {
|
||||
Log.e(LOG_TAG, "Failed to create message entry because chat " + chatID + " doesn't exists!");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
dbInstance.messageDao().insertMessage(new MessageEntity(type, chatID, sender, new Date().getTime(), text));
|
||||
return true;
|
||||
MessageEntity message = new MessageEntity(type, chatID, sender, new Date().getTime(), text, false, false);
|
||||
return dbInstance.messageDao().insertMessage(message);
|
||||
}
|
||||
|
||||
public static MessageEntity getMessageByID(long id) {
|
||||
List<MessageEntity> messages = dbInstance.messageDao().getMessageByID(id);
|
||||
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()) {
|
||||
return null;
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public static ChatEntity getChatByChatID(String chatID) {
|
||||
List<ChatEntity> chats = dbInstance.chatDao().getChatByChatID(chatID);
|
||||
if(chats.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return chats.get(0);
|
||||
}
|
||||
|
||||
public static void updateChatEntry(long id, boolean isSent) {
|
||||
dbInstance.messageDao().updateMessage(id, isSent);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
public class MessageTypes {
|
||||
public static final int USUAL_MESSAGE = 0x0;
|
||||
}
|
@ -6,25 +6,21 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import net.tomp2p.dht.FutureGet;
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.FutureRemove;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FuturePing;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.observer.NetworkObserver;
|
||||
import io.github.chronosx88.influence.helpers.actions.NetworkActions;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.models.NewChatRequestMessage;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.models.SendMessage;
|
||||
import io.github.chronosx88.influence.models.SuccessfullySentMessage;
|
||||
|
||||
public class NetworkHandler implements NetworkObserver {
|
||||
private final static String LOG_TAG = "NetworkHandler";
|
||||
@ -54,6 +50,19 @@ public class NetworkHandler implements NetworkObserver {
|
||||
ObservableUtils.notifyUI(UIActions.SUCCESSFUL_CREATE_CHAT);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkActions.NEW_MESSAGE: {
|
||||
SendMessage sendMessage = gson.fromJson((String) object, SendMessage.class);
|
||||
long messageID = LocalDBWrapper.createMessageEntry(sendMessage.getMessageType(), sendMessage.getChatID(), sendMessage.getSenderID(), sendMessage.getText());
|
||||
ObservableUtils.notifyUI(UIActions.MESSAGE_RECEIVED, messageID);
|
||||
sendMessageReceived(sendMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkActions.MESSAGE_SENT: {
|
||||
SuccessfullySentMessage successfullySentMessage = gson.fromJson((String) object, SuccessfullySentMessage.class);
|
||||
LocalDBWrapper.updateChatEntry(successfullySentMessage.getMessageID(), true);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
@ -64,7 +73,6 @@ public class NetworkHandler implements NetworkObserver {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void handleIncomingChatRequest(String chatID, PeerAddress chatStarterAddress) {
|
||||
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(chatID, AppHelper.getPeerID(), peerDHT.peerAddress());
|
||||
newChatRequestMessage.setAction(NetworkActions.SUCCESSFULL_CREATE_CHAT);
|
||||
@ -115,7 +123,7 @@ public class NetworkHandler implements NetworkObserver {
|
||||
.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
ObservableUtils.notifyUI(UIActions.NEW_CHAT);
|
||||
ObservableUtils.notifyUI(UIActions.SUCCESSFUL_CREATE_CHAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,19 +140,25 @@ public class NetworkHandler implements NetworkObserver {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
LocalDBWrapper.createChatEntry(
|
||||
newChatRequestMessage.getChatID(),
|
||||
/*LocalDBWrapper.createChatEntry(
|
||||
newChatRequestMessage.getMessageID(),
|
||||
newChatRequestMessage.getSenderID(),
|
||||
newChatRequestMessage.getSenderPeerAddress()
|
||||
);
|
||||
);*/
|
||||
|
||||
Log.i(LOG_TAG, "Chat " + newChatRequestMessage.getChatID() + " successfully accepted!");
|
||||
|
||||
peerDHT.remove(Number160.createHash(AppHelper.getPeerID() + "_pendingAcceptedChats"))
|
||||
.contentKey(Number160.createHash(newChatRequestMessage.getChatID()))
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
|
||||
ObservableUtils.notifyUI(UIActions.NEW_CHAT);
|
||||
ObservableUtils.notifyUI(UIActions.SUCCESSFUL_CREATE_CHAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessageReceived(SendMessage sendMessage) {
|
||||
P2PUtils.send(sendMessage.getSenderPeerAddress(), gson.toJson(new SuccessfullySentMessage(AppHelper.getPeerID(), AppHelper.getPeerDHT().peerAddress(), sendMessage.getMessageID())));
|
||||
}
|
||||
}
|
||||
|
@ -8,4 +8,18 @@ public class ObservableUtils {
|
||||
jsonObject.addProperty("action", action);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
}
|
||||
|
||||
public static void notifyUI(int action, String additional) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", action);
|
||||
jsonObject.addProperty("additional", additional);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
}
|
||||
|
||||
public static void notifyUI(int action, long additional) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", action);
|
||||
jsonObject.addProperty("additional", additional);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.google.gson.Gson;
|
||||
import net.tomp2p.dht.FutureGet;
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FutureDirect;
|
||||
import net.tomp2p.futures.FuturePing;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
@ -59,4 +60,14 @@ public class P2PUtils {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean send(PeerAddress address, String data) {
|
||||
FutureDirect futureDirect = peerDHT
|
||||
.peer()
|
||||
.sendDirect(address)
|
||||
.object(data)
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
return futureDirect.isSuccess();
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,5 @@ public class UIActions {
|
||||
public static final int NEW_CHAT = 0x6;
|
||||
public static final int PEER_NOT_EXIST = 0x7;
|
||||
public static final int SUCCESSFUL_CREATE_CHAT = 0x8;
|
||||
public static final int MESSAGE_RECEIVED = 0x9;
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package io.github.chronosx88.influence.logic;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.ChatLogicContract;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.P2PUtils;
|
||||
import io.github.chronosx88.influence.models.SendMessage;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatLogic implements ChatLogicContract {
|
||||
private static Gson gson = new Gson();
|
||||
@Override
|
||||
public void sendMessage(PeerAddress address, MessageEntity message) {
|
||||
new Thread(() -> {
|
||||
P2PUtils
|
||||
.send(address, gson.toJson(
|
||||
new SendMessage(
|
||||
AppHelper.getPeerID(),
|
||||
AppHelper.getPeerDHT().peerAddress(),
|
||||
message.id,
|
||||
message.timestamp,
|
||||
message.type,
|
||||
message.chatID,
|
||||
message.text
|
||||
)
|
||||
));
|
||||
}).start();
|
||||
// TODO: put message into DHT if user is offline
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import android.util.Log;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.PeerBuilderDHT;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FutureBootstrap;
|
||||
@ -33,7 +32,7 @@ import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.main.MainLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainLogicContract;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.DSAKey;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
|
@ -0,0 +1,64 @@
|
||||
package io.github.chronosx88.influence.models;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.actions.NetworkActions;
|
||||
|
||||
public class SendMessage extends BasicNetworkMessage implements Serializable {
|
||||
private long messageID;
|
||||
private long timestamp;
|
||||
private int messageType;
|
||||
private String chatID;
|
||||
private String text;
|
||||
|
||||
public SendMessage(String senderID, PeerAddress senderPeerAddress, long messageID,long timestamp, int messageType, String chatID, String text) {
|
||||
super(NetworkActions.NEW_MESSAGE, senderID, senderPeerAddress);
|
||||
this.messageID = messageID;
|
||||
this.timestamp = timestamp;
|
||||
this.messageType = messageType;
|
||||
this.chatID = chatID;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public int getMessageType() {
|
||||
return messageType;
|
||||
}
|
||||
|
||||
public String getChatID() {
|
||||
return chatID;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setMessageType(int messageType) {
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
public void setChatID(String chatID) {
|
||||
this.chatID = chatID;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public long getMessageID() {
|
||||
return messageID;
|
||||
}
|
||||
|
||||
public void setMessageID(long messageID) {
|
||||
this.messageID = messageID;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.chronosx88.influence.models;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.actions.NetworkActions;
|
||||
|
||||
public class SuccessfullySentMessage extends BasicNetworkMessage implements Serializable {
|
||||
private long messageID;
|
||||
|
||||
public SuccessfullySentMessage(String senderID, PeerAddress senderPeerAddress, long messageID) {
|
||||
super(NetworkActions.MESSAGE_SENT, senderID, senderPeerAddress);
|
||||
this.messageID = messageID;
|
||||
}
|
||||
|
||||
public long getMessageID() {
|
||||
return messageID;
|
||||
}
|
||||
|
||||
public void setMessageID(long messageID) {
|
||||
this.messageID = messageID;
|
||||
}
|
||||
}
|
@ -10,10 +10,10 @@ import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
@Dao
|
||||
public interface MessageDao {
|
||||
@Insert
|
||||
void insertMessage(MessageEntity chatModel);
|
||||
long insertMessage(MessageEntity chatModel);
|
||||
|
||||
@Query("DELETE FROM messages WHERE id = :msgID")
|
||||
void deleteMessage(String msgID);
|
||||
void deleteMessage(long msgID);
|
||||
|
||||
@Query("DELETE FROM messages WHERE chatID = :chatID")
|
||||
void deleteMessagesByChatID(String chatID);
|
||||
@ -22,5 +22,11 @@ public interface MessageDao {
|
||||
List<MessageEntity> getMessagesByChatID(String chatID);
|
||||
|
||||
@Query("SELECT * FROM messages WHERE id = :id")
|
||||
List<MessageEntity> getMessageByID(String id);
|
||||
List<MessageEntity> getMessageByID(long id);
|
||||
|
||||
@Query("UPDATE messages SET isSent = :isSent WHERE id = :msgID")
|
||||
void updateMessage(long msgID, boolean isSent);
|
||||
|
||||
@Query("UPDATE messages SET text = :text WHERE id = :msgID")
|
||||
void updateMessage(long msgID, String text);
|
||||
}
|
||||
|
@ -7,41 +7,23 @@ import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "messages")
|
||||
public class MessageEntity {
|
||||
@PrimaryKey(autoGenerate = true) public int id;
|
||||
@PrimaryKey(autoGenerate = true) public long id;
|
||||
@ColumnInfo public int type;
|
||||
@ColumnInfo public String chatID;
|
||||
@ColumnInfo public String sender;
|
||||
@ColumnInfo public long timestamp;
|
||||
@ColumnInfo public String text;
|
||||
@ColumnInfo public boolean isSent;
|
||||
@ColumnInfo public boolean isRead;
|
||||
|
||||
public MessageEntity(int type, String chatID, String sender, long timestamp, String text) {
|
||||
public MessageEntity(int type, String chatID, String sender, long timestamp, String text, boolean isSent, boolean isRead) {
|
||||
this.type = type;
|
||||
this.chatID = chatID;
|
||||
this.sender = sender;
|
||||
this.timestamp = timestamp;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getType() { return type; }
|
||||
|
||||
public String getChatID() {
|
||||
return chatID;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
this.isSent = isSent;
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.chronosx88.influence.presenters;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import net.tomp2p.dht.FutureRemove;
|
||||
@ -10,8 +11,10 @@ import io.github.chronosx88.influence.contracts.chatlist.ChatListPresenterContra
|
||||
import io.github.chronosx88.influence.contracts.chatlist.ChatListViewContract;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
import io.github.chronosx88.influence.logic.ChatListLogic;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.views.ChatActivity;
|
||||
|
||||
public class ChatListPresenter implements ChatListPresenterContract {
|
||||
private ChatListViewContract view;
|
||||
@ -20,7 +23,9 @@ public class ChatListPresenter implements ChatListPresenterContract {
|
||||
|
||||
public ChatListPresenter(ChatListViewContract view) {
|
||||
this.view = view;
|
||||
chatListAdapter = new ChatListAdapter();
|
||||
chatListAdapter = new ChatListAdapter((v, p)-> {
|
||||
openChat(chatListAdapter.getChatEntity(p).chatID);
|
||||
});
|
||||
this.logic = new ChatListLogic();
|
||||
this.view.setRecycleAdapter(chatListAdapter);
|
||||
}
|
||||
@ -32,7 +37,10 @@ public class ChatListPresenter implements ChatListPresenterContract {
|
||||
|
||||
@Override
|
||||
public void openChat(String chatID) {
|
||||
// TODO
|
||||
Intent intent = new Intent(AppHelper.getContext(), ChatActivity.class);
|
||||
intent.putExtra("chatID", chatID);
|
||||
intent.putExtra("contactUsername", LocalDBWrapper.getChatByChatID(chatID).name);
|
||||
view.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,6 +51,7 @@ public class ChatListPresenter implements ChatListPresenterContract {
|
||||
new Thread(() -> {
|
||||
ChatEntity chat = chatListAdapter.getItem(chatListAdapter.onClickPosition);
|
||||
AppHelper.getChatDB().chatDao().deleteChat(chat.chatID);
|
||||
AppHelper.getChatDB().messageDao().deleteMessagesByChatID(chat.chatID);
|
||||
view.updateChatList(chatListAdapter, logic.loadAllChats());
|
||||
}).start();
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package io.github.chronosx88.influence.presenters;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.ChatLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.ChatPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.ChatViewContract;
|
||||
import io.github.chronosx88.influence.contracts.observer.Observer;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
import io.github.chronosx88.influence.helpers.MessageTypes;
|
||||
import io.github.chronosx88.influence.helpers.Serializer;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.logic.ChatLogic;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatPresenter implements ChatPresenterContract, Observer {
|
||||
private ChatLogicContract logic;
|
||||
private ChatViewContract view;
|
||||
private PeerAddress receiverAddress;
|
||||
private String chatID;
|
||||
private Gson gson;
|
||||
|
||||
public ChatPresenter(ChatViewContract view, String chatID) {
|
||||
this.logic = new ChatLogic();
|
||||
this.view = view;
|
||||
this.chatID = chatID;
|
||||
this.receiverAddress = (PeerAddress) Serializer.deserialize(LocalDBWrapper.getChatByChatID(chatID).getPeerAddress());
|
||||
AppHelper.getObservable().register(this);
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String text) {
|
||||
long messageID = LocalDBWrapper.createMessageEntry(MessageTypes.USUAL_MESSAGE, chatID, AppHelper.getPeerID(), text);
|
||||
MessageEntity message = LocalDBWrapper.getMessageByID(messageID);
|
||||
logic.sendMessage(receiverAddress, message);
|
||||
view.updateMessageList(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(JsonObject object) {
|
||||
switch (object.get("action").getAsInt()) {
|
||||
case UIActions.MESSAGE_RECEIVED: {
|
||||
MessageEntity messageEntity = LocalDBWrapper.getMessageByID(object.get("additional").getAsInt());
|
||||
view.updateMessageList(messageEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAdapter() {
|
||||
List<MessageEntity> entities = LocalDBWrapper.getMessagesByChatID(chatID);
|
||||
view.updateMessageList(entities == null ? new ArrayList<>() : entities);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package io.github.chronosx88.influence.presenters;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.main.MainLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.main.MainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.main.MainViewContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainViewContract;
|
||||
import io.github.chronosx88.influence.logic.MainLogic;
|
||||
|
||||
public class MainPresenter implements MainPresenterContract {
|
||||
|
@ -0,0 +1,72 @@
|
||||
package io.github.chronosx88.influence.views;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.ChatViewContract;
|
||||
import io.github.chronosx88.influence.helpers.ChatAdapter;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
import io.github.chronosx88.influence.presenters.ChatPresenter;
|
||||
|
||||
public class ChatActivity extends AppCompatActivity implements ChatViewContract {
|
||||
private ChatAdapter chatAdapter;
|
||||
private RecyclerView messageList;
|
||||
private ImageButton sendMessageButton;
|
||||
private EditText messageTextEdit;
|
||||
private TextView contactUsernameTextView;
|
||||
private ChatPresenter presenter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_chat);
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
presenter = new ChatPresenter(this, intent.getStringExtra("chatID"));
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar_chat_activity);
|
||||
setSupportActionBar(toolbar);
|
||||
messageList = findViewById(R.id.message_list);
|
||||
chatAdapter = new ChatAdapter();
|
||||
presenter.updateAdapter();
|
||||
messageList.setAdapter(chatAdapter);
|
||||
messageList.setLayoutManager(new LinearLayoutManager(this));
|
||||
contactUsernameTextView = findViewById(R.id.appbar_username);
|
||||
messageTextEdit = findViewById(R.id.message_input);
|
||||
sendMessageButton = findViewById(R.id.send_button);
|
||||
sendMessageButton.setOnClickListener((v) -> {
|
||||
presenter.sendMessage(messageTextEdit.getText().toString());
|
||||
});
|
||||
contactUsernameTextView.setText(intent.getStringExtra("contactUsername"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageList(MessageEntity message) {
|
||||
runOnUiThread(() -> {
|
||||
chatAdapter.addMessage(message);
|
||||
chatAdapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageList(List<MessageEntity> messages) {
|
||||
runOnUiThread(() -> {
|
||||
chatAdapter.addMessages(messages);
|
||||
chatAdapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}
|
@ -13,8 +13,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.main.MainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.main.MainViewContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.MainViewContract;
|
||||
import io.github.chronosx88.influence.contracts.observer.Observer;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
|
@ -68,8 +68,10 @@ public class ChatListFragment extends Fragment implements ChatListViewContract,
|
||||
@Override
|
||||
public void handleEvent(JsonObject object) {
|
||||
switch (object.get("action").getAsInt()) {
|
||||
case UIActions.SUCCESSFUL_CREATE_CHAT:
|
||||
case UIActions.NEW_CHAT: {
|
||||
presenter.updateChatList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
5
app/src/main/res/drawable/ic_send_green_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_send_green_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#008577"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
||||
</vector>
|
75
app/src/main/res/layout/activity_chat.xml
Normal file
75
app/src/main/res/layout/activity_chat.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="#e6e6e6">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_chat_activity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar_chat_activity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimary"
|
||||
android:theme="@style/Base.ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:popupTheme="@style/MenuStyle">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:id="@+id/profile_image_chat_activity"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/appbar_username"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginLeft="25dp"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="25dp" />
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/message_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/appbar_chat_activity"
|
||||
android:layout_above="@+id/chat_activity_bottom_container"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/chat_activity_bottom_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="#FFFFFF"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/message_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toLeftOf="@+id/send_button"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="Type a message..." />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/send_button"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="@drawable/ic_send_green_24dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -11,27 +11,27 @@
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:id="@+id/contact_profile_image"
|
||||
android:id="@+id/profile_image"
|
||||
android:src="@mipmap/ic_launcher"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/message_item_left_background"
|
||||
android:layout_toRightOf="@id/contact_profile_image"
|
||||
android:layout_toRightOf="@id/profile_image"
|
||||
android:text="Null"
|
||||
android:paddingEnd="50dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:id="@+id/message_text_left"
|
||||
android:id="@+id/message_text"
|
||||
android:textSize="18sp"
|
||||
android:padding="8dp"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/message_time_left"
|
||||
android:layout_alignBottom="@+id/message_text_left"
|
||||
android:layout_alignRight="@+id/message_text_left"
|
||||
android:id="@+id/message_time"
|
||||
android:layout_alignBottom="@+id/message_text"
|
||||
android:layout_alignRight="@+id/message_text"
|
||||
android:paddingBottom="3dp"
|
||||
android:paddingRight="4dp"
|
||||
android:text="12:00"/>
|
||||
|
@ -25,7 +25,7 @@
|
||||
android:layout_toLeftOf="@id/profile_image"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:id="@+id/message_text_right"
|
||||
android:id="@+id/message_text"
|
||||
android:textSize="18sp"
|
||||
android:paddingEnd="50dp"
|
||||
android:textColor="#ffffff"
|
||||
@ -33,9 +33,9 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/message_time_right"
|
||||
android:layout_alignBottom="@+id/message_text_right"
|
||||
android:layout_alignRight="@+id/message_text_right"
|
||||
android:id="@+id/message_time"
|
||||
android:layout_alignBottom="@+id/message_text"
|
||||
android:layout_alignRight="@+id/message_text"
|
||||
android:paddingBottom="3dp"
|
||||
android:textColor="#ffffff"
|
||||
android:paddingRight="4dp"
|
||||
|
@ -32,4 +32,12 @@
|
||||
<item name="android:topDark">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="MenuStyle" parent="Theme.AppCompat.Light">
|
||||
<item name="android:background">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="NoWindowActionBar" parent="AppTheme">
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user