Made offline chat send request

This commit is contained in:
ChronosX88 2019-03-23 21:30:24 +04:00
parent 6ee7223fcd
commit 0bca327949
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
10 changed files with 161 additions and 35 deletions

View File

@ -6,10 +6,19 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import net.tomp2p.dht.FutureGet;
import net.tomp2p.dht.FuturePut;
import net.tomp2p.dht.PeerDHT; 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.peers.PeerAddress;
import net.tomp2p.storage.Data;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
import io.github.chronosx88.influence.contracts.observer.NetworkObserver; import io.github.chronosx88.influence.contracts.observer.NetworkObserver;
import io.github.chronosx88.influence.helpers.actions.NetworkActions; import io.github.chronosx88.influence.helpers.actions.NetworkActions;
@ -19,12 +28,11 @@ import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
public class NetworkHandler implements NetworkObserver { public class NetworkHandler implements NetworkObserver {
private final static String LOG_TAG = "NetworkHandler"; private final static String LOG_TAG = "NetworkHandler";
private Gson gson; private static Gson gson = new Gson();
private PeerDHT peerDHT; private static PeerDHT peerDHT = AppHelper.getPeerDHT();
private static KeyPairManager keyPairManager = new KeyPairManager();
public NetworkHandler() { public NetworkHandler() {
gson = new Gson();
peerDHT = AppHelper.getPeerDHT();
AppHelper.getObservable().register(this); AppHelper.getObservable().register(this);
} }
@ -45,9 +53,11 @@ public class NetworkHandler implements NetworkObserver {
case NetworkActions.SUCCESSFULL_CREATE_CHAT: { case NetworkActions.SUCCESSFULL_CREATE_CHAT: {
NewChatRequestMessage newChatRequestMessage = gson.fromJson((String) object, NewChatRequestMessage.class); NewChatRequestMessage newChatRequestMessage = gson.fromJson((String) object, NewChatRequestMessage.class);
createChatEntry(newChatRequestMessage.getChatID(), newChatRequestMessage.getSenderID(), newChatRequestMessage.getSenderPeerAddress()); createChatEntry(newChatRequestMessage.getChatID(), newChatRequestMessage.getSenderID(), newChatRequestMessage.getSenderPeerAddress());
JsonObject jsonObject = new JsonObject(); ObservableUtils.notifyUI(UIActions.SUCCESSFULL_CREATE_CHAT);
jsonObject.addProperty("action", UIActions.NEW_CHAT); peerDHT.remove(Number160.createHash(newChatRequestMessage.getSenderID() + "_pendingChats"))
AppHelper.getObservable().notifyUIObservers(jsonObject); .contentKey(Number160.createHash(newChatRequestMessage.getChatID()))
.start()
.awaitUninterruptibly();
break; break;
} }
} }
@ -59,7 +69,7 @@ public class NetworkHandler implements NetworkObserver {
return jsonObject.get("action").getAsInt(); return jsonObject.get("action").getAsInt();
} }
private void createChatEntry(String chatID, String name, PeerAddress peerAddress) { private static void createChatEntry(String chatID, String name, PeerAddress peerAddress) {
List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID); List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID);
if (chatEntities.size() > 0) { if (chatEntities.size() > 0) {
Log.e(LOG_TAG, "Failed to create chat " + chatID + " because chat exists!"); Log.e(LOG_TAG, "Failed to create chat " + chatID + " because chat exists!");
@ -74,4 +84,96 @@ public class NetworkHandler implements NetworkObserver {
newChatRequestMessage.setAction(NetworkActions.SUCCESSFULL_CREATE_CHAT); newChatRequestMessage.setAction(NetworkActions.SUCCESSFULL_CREATE_CHAT);
AppHelper.getPeerDHT().peer().sendDirect(chatStarterAddress).object(gson.toJson(newChatRequestMessage)).start().awaitUninterruptibly(); AppHelper.getPeerDHT().peer().sendDirect(chatStarterAddress).object(gson.toJson(newChatRequestMessage)).start().awaitUninterruptibly();
} }
public static void handlePendingChats() {
FutureGet futureGetPendingChats = peerDHT
.get(Number160.createHash(AppHelper.getPeerID() + "_pendingChats"))
.all()
.start()
.awaitUninterruptibly();
if(!futureGetPendingChats.isEmpty()) {
for(Map.Entry<Number640, Data> entry : futureGetPendingChats.dataMap().entrySet()) {
NewChatRequestMessage newChatRequestMessage = null;
try {
newChatRequestMessage = gson.fromJson((String) entry.getValue().object(), NewChatRequestMessage.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
createChatEntry(
newChatRequestMessage.getChatID(),
newChatRequestMessage.getSenderID(),
newChatRequestMessage.getSenderPeerAddress()
);
FuturePing ping = peerDHT
.peer()
.ping()
.peerAddress(newChatRequestMessage.getSenderPeerAddress())
.start()
.awaitUninterruptibly();
NewChatRequestMessage newChatRequestReply = new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress());
newChatRequestReply.setAction(NetworkActions.SUCCESSFULL_CREATE_CHAT);
if(ping.isSuccess()) {
peerDHT
.peer()
.sendDirect(newChatRequestMessage.getSenderPeerAddress())
.object(gson.toJson(newChatRequestReply))
.start()
.awaitUninterruptibly();
} else {
try {
FuturePut put = peerDHT.put(Number160.createHash(newChatRequestMessage.getSenderID() + "_pendingAcceptedChats"))
.data(Number160.createHash(UUID.randomUUID().toString()), new Data(gson.toJson(newChatRequestReply))
.protectEntry(keyPairManager.openMainKeyPair()))
.start()
.awaitUninterruptibly();
if(put.isSuccess()) {
Log.i(LOG_TAG, "# Successfully put message SUCCESSFULLY_CREATE_CHAT in " + newChatRequestMessage.getSenderID() + "_pendingAcceptedChats, because receiver is offline.");
} else {
Log.e(LOG_TAG, "# Failed to put message SUCCESSFULLY_CREATE_CHAT in " + newChatRequestMessage.getSenderID() + "_pendingAcceptedChats. Reason: " + put.failedReason());
}
} catch (IOException e) {
e.printStackTrace();
}
}
ObservableUtils.notifyUI(UIActions.NEW_CHAT);
}
}
}
public static void handlePendingAcceptedChats() {
FutureGet futureGetPendingAcceptedChats = peerDHT
.get(Number160.createHash(AppHelper.getPeerID() + "_pendingAcceptedChats"))
.all()
.start()
.awaitUninterruptibly();
if(!futureGetPendingAcceptedChats.isEmpty()) {
for(Map.Entry<Number640, Data> entry : futureGetPendingAcceptedChats.dataMap().entrySet()) {
NewChatRequestMessage newChatRequestMessage = null;
try {
newChatRequestMessage = gson.fromJson((String) entry.getValue().object(), NewChatRequestMessage.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
peerDHT.remove(Number160.createHash(newChatRequestMessage.getSenderID() + "_pendingChats"))
.contentKey(Number160.createHash(newChatRequestMessage.getChatID()))
.start()
.awaitUninterruptibly();
createChatEntry(
newChatRequestMessage.getChatID(),
newChatRequestMessage.getSenderID(),
newChatRequestMessage.getSenderPeerAddress()
);
ObservableUtils.notifyUI(UIActions.NEW_CHAT);
}
}
}
} }

View File

@ -0,0 +1,11 @@
package io.github.chronosx88.influence.helpers;
import com.google.gson.JsonObject;
public class ObservableUtils {
public static void notifyUI(int action) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("action", action);
AppHelper.getObservable().notifyUIObservers(jsonObject);
}
}

View File

@ -10,4 +10,5 @@ public class UIActions {
public static final int NEW_CHAT = 0x6; public static final int NEW_CHAT = 0x6;
public static final int PEER_NOT_EXIST = 0x7; public static final int PEER_NOT_EXIST = 0x7;
public static final int SUCCESSFULL_CREATE_CHAT = 0x8; public static final int SUCCESSFULL_CREATE_CHAT = 0x8;
public static final int SUCCESSFULL_CREATE_OFFLINE_CHAT = 0x9;
} }

View File

@ -37,6 +37,7 @@ import io.github.chronosx88.influence.contracts.main.MainLogicContract;
import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.AppHelper;
import io.github.chronosx88.influence.helpers.DSAKey; import io.github.chronosx88.influence.helpers.DSAKey;
import io.github.chronosx88.influence.helpers.KeyPairManager; import io.github.chronosx88.influence.helpers.KeyPairManager;
import io.github.chronosx88.influence.helpers.NetworkHandler;
import io.github.chronosx88.influence.helpers.StorageMVStore; import io.github.chronosx88.influence.helpers.StorageMVStore;
import io.github.chronosx88.influence.helpers.actions.UIActions; import io.github.chronosx88.influence.helpers.actions.UIActions;
import io.github.chronosx88.influence.models.PublicUserProfile; import io.github.chronosx88.influence.models.PublicUserProfile;
@ -138,6 +139,8 @@ public class MainLogic implements MainLogicContract {
setReceiveHandler(); setReceiveHandler();
gson = new Gson(); gson = new Gson();
publicProfileToDHT(); publicProfileToDHT();
NetworkHandler.handlePendingChats();
NetworkHandler.handlePendingAcceptedChats();
replication = new AutoReplication(peerDHT.peer()).start(); replication = new AutoReplication(peerDHT.peer()).start();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -19,6 +19,7 @@ import java.util.UUID;
import io.github.chronosx88.influence.contracts.startchat.StartChatLogicContract; import io.github.chronosx88.influence.contracts.startchat.StartChatLogicContract;
import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.AppHelper;
import io.github.chronosx88.influence.helpers.KeyPairManager; import io.github.chronosx88.influence.helpers.KeyPairManager;
import io.github.chronosx88.influence.helpers.ObservableUtils;
import io.github.chronosx88.influence.helpers.actions.UIActions; import io.github.chronosx88.influence.helpers.actions.UIActions;
import io.github.chronosx88.influence.models.NewChatRequestMessage; import io.github.chronosx88.influence.models.NewChatRequestMessage;
import io.github.chronosx88.influence.models.PublicUserProfile; import io.github.chronosx88.influence.models.PublicUserProfile;
@ -54,14 +55,17 @@ public class StartChatLogic implements StartChatLogicContract {
try { try {
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress()); NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress());
FuturePut futurePut = peerDHT FuturePut futurePut = peerDHT
.put(Number160.createHash(peerID)) .put(Number160.createHash(peerID + "_pendingChats"))
.data(Number160.createHash(UUID.randomUUID().toString()), new Data(gson.toJson(newChatRequestMessage)) .data(Number160.createHash(newChatRequestMessage.getChatID()), new Data(gson.toJson(newChatRequestMessage))
.protectEntry(keyPairManager.openMainKeyPair())).start().awaitUninterruptibly(); .protectEntry(keyPairManager.openMainKeyPair()))
.start()
.awaitUninterruptibly();
if(futurePut.isSuccess()) { if(futurePut.isSuccess()) {
Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + newChatRequestMessage.getChatID()); Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + newChatRequestMessage.getChatID());
} else { } else {
Log.e(LOG_TAG, "# Failed to create chat: " + futurePut.failedReason()); Log.e(LOG_TAG, "# Failed to create chat: " + futurePut.failedReason());
} }
ObservableUtils.notifyUI(UIActions.SUCCESSFULL_CREATE_OFFLINE_CHAT);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -2,18 +2,18 @@ package io.github.chronosx88.influence.presenters;
import android.view.MenuItem; import android.view.MenuItem;
import com.google.gson.JsonObject; import net.tomp2p.dht.FutureRemove;
import net.tomp2p.peers.Number160;
import io.github.chronosx88.influence.contracts.chatlist.ChatListLogicContract; import io.github.chronosx88.influence.contracts.chatlist.ChatListLogicContract;
import io.github.chronosx88.influence.contracts.chatlist.ChatListPresenterContract; import io.github.chronosx88.influence.contracts.chatlist.ChatListPresenterContract;
import io.github.chronosx88.influence.contracts.chatlist.ChatListViewContract; import io.github.chronosx88.influence.contracts.chatlist.ChatListViewContract;
import io.github.chronosx88.influence.contracts.observer.Observer;
import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.AppHelper;
import io.github.chronosx88.influence.helpers.ChatListAdapter; import io.github.chronosx88.influence.helpers.ChatListAdapter;
import io.github.chronosx88.influence.helpers.actions.UIActions;
import io.github.chronosx88.influence.logic.ChatListLogic; import io.github.chronosx88.influence.logic.ChatListLogic;
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
public class ChatListPresenter implements ChatListPresenterContract, Observer { public class ChatListPresenter implements ChatListPresenterContract {
private ChatListViewContract view; private ChatListViewContract view;
private ChatListLogicContract logic; private ChatListLogicContract logic;
private ChatListAdapter chatListAdapter; private ChatListAdapter chatListAdapter;
@ -23,7 +23,6 @@ public class ChatListPresenter implements ChatListPresenterContract, Observer {
chatListAdapter = new ChatListAdapter(); chatListAdapter = new ChatListAdapter();
this.logic = new ChatListLogic(); this.logic = new ChatListLogic();
this.view.setRecycleAdapter(chatListAdapter); this.view.setRecycleAdapter(chatListAdapter);
AppHelper.getObservable().register(this);
} }
@Override @Override
@ -41,19 +40,13 @@ public class ChatListPresenter implements ChatListPresenterContract, Observer {
switch(item.getItemId()) { switch(item.getItemId()) {
case 0: { case 0: {
if(chatListAdapter.onClickPosition != -1) { if(chatListAdapter.onClickPosition != -1) {
AppHelper.getChatDB().chatDao().deleteChat(chatListAdapter.getItem(chatListAdapter.onClickPosition).chatID); new Thread(() -> {
view.updateChatList(chatListAdapter, logic.loadAllChats()); ChatEntity chat = chatListAdapter.getItem(chatListAdapter.onClickPosition);
AppHelper.getChatDB().chatDao().deleteChat(chat.chatID);
view.updateChatList(chatListAdapter, logic.loadAllChats());
}).start();
} }
} }
} }
} }
@Override
public void handleEvent(JsonObject object) {
switch (object.get("action").getAsInt()) {
case UIActions.NEW_CHAT: {
updateChatList();
}
}
}
} }

View File

@ -22,6 +22,7 @@ public class StartChatPresenter implements StartChatPresenterContract, Observer
@Override @Override
public void startChatWithPeer(String peerID) { public void startChatWithPeer(String peerID) {
view.showProgressDialog(true);
logic.sendStartChatMessage(peerID); logic.sendStartChatMessage(peerID);
} }
@ -39,6 +40,11 @@ public class StartChatPresenter implements StartChatPresenterContract, Observer
view.showMessage("Чат успешно создан"); view.showMessage("Чат успешно создан");
break; break;
} }
case UIActions.SUCCESSFULL_CREATE_OFFLINE_CHAT: {
view.showProgressDialog(false);
view.showMessage("В сеть отправлен запрос на создание чата, так как получатель не в сети.");
break;
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
package io.github.chronosx88.influence.views.fragments; package io.github.chronosx88.influence.views.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -28,11 +29,13 @@ import io.github.chronosx88.influence.presenters.ChatListPresenter;
public class ChatListFragment extends Fragment implements ChatListViewContract, Observer { public class ChatListFragment extends Fragment implements ChatListViewContract, Observer {
private ChatListPresenterContract presenter; private ChatListPresenterContract presenter;
private RecyclerView chatList; private RecyclerView chatList;
private Handler mainThreadHandler;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
AppHelper.getObservable().register(this); AppHelper.getObservable().register(this);
this.mainThreadHandler = new Handler(getContext().getMainLooper());
} }
@Override @Override
@ -73,7 +76,7 @@ public class ChatListFragment extends Fragment implements ChatListViewContract,
@Override @Override
public void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats) { public void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats) {
requireActivity().runOnUiThread(() -> { mainThreadHandler.post(() -> {
adapter.setChatList(chats); adapter.setChatList(chats);
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
}); });

View File

@ -52,11 +52,14 @@ public class StartChatFragment extends Fragment implements StartChatViewContract
@Override @Override
public void showProgressDialog(boolean enabled) { public void showProgressDialog(boolean enabled) {
if(enabled) { // TODO: make run on mainHandlerThread
progressDialog.show(); requireActivity().runOnUiThread(() -> {
} else { if(enabled) {
progressDialog.dismiss(); progressDialog.show();
} } else {
progressDialog.dismiss();
}
});
} }
// TODO: clear text input // TODO: clear text input

View File

@ -13,11 +13,11 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputPeerID" android:id="@+id/textInputPeerID"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"> android:layout_gravity="center">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="346dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:hint="Peer ID"/> android:hint="Peer ID"/>