mirror of
https://github.com/ChronosX88/Influence-P2P.git
synced 2024-11-21 23:02:18 +00:00
Now chats are creating!!! Changed many things in app.
This commit is contained in:
parent
2680a86ba8
commit
347e195909
@ -3,6 +3,9 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<compositeConfiguration>
|
||||
<compositeBuild compositeDefinitionSource="SCRIPT" />
|
||||
</compositeConfiguration>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
|
@ -9,6 +9,11 @@ android {
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -44,5 +49,5 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha04'
|
||||
implementation 'androidx.preference:preference:1.1.0-alpha03'
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
//implementation group: 'com.github.demidenko05', name: 'a-javabeans', version: '1.0.4'
|
||||
implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: '3.1.0.RELEASE'
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "1fdccce4cf398929958d6bf1f0ccfea6",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `chatID` TEXT, `sender` TEXT, `date` TEXT, `text` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatID",
|
||||
"columnName": "chatID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sender",
|
||||
"columnName": "sender",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "date",
|
||||
"columnName": "date",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chatID` TEXT, `name` TEXT, `peerAddresses` BLOB, `keyPairID` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatID",
|
||||
"columnName": "chatID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "peerAddresses",
|
||||
"columnName": "peerAddresses",
|
||||
"affinity": "BLOB",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyPairID",
|
||||
"columnName": "keyPairID",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"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, \"1fdccce4cf398929958d6bf1f0ccfea6\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
package io.github.chronosx88.influence.contracts.chatlist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public interface ChatListViewContract {
|
||||
void setRecycleAdapter(ChatListAdapter adapter);
|
||||
void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats);
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package io.github.chronosx88.influence.contracts.observer;
|
||||
|
||||
public interface NetworkObserver {
|
||||
void handleEvent(Object object);
|
||||
}
|
@ -3,7 +3,10 @@ package io.github.chronosx88.influence.contracts.observer;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface Observable {
|
||||
void register(Observer observer, int channelID);
|
||||
void unregister(Observer observer, int channelID);
|
||||
void notifyObservers(JsonObject jsonObject, int channelID);
|
||||
void register(Observer observer);
|
||||
void register(NetworkObserver networkObserver);
|
||||
void unregister(Observer observer);
|
||||
void unregister(NetworkObserver networkObserver);
|
||||
void notifyUIObservers(JsonObject jsonObject);
|
||||
void notifyNetworkObservers(Object object);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ public class AppHelper extends Application {
|
||||
private static String peerID;
|
||||
private static PeerDHT peerDHT;
|
||||
private static RoomHelper chatDB;
|
||||
private static NetworkHandler networkHandler;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@ -27,7 +28,6 @@ public class AppHelper extends Application {
|
||||
chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB")
|
||||
.allowMainThreadQueries()
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static void storePeerID(String peerID1) { peerID = peerID1; }
|
||||
@ -45,4 +45,6 @@ public class AppHelper extends Application {
|
||||
public static PeerDHT getPeerDHT() { return peerDHT; }
|
||||
|
||||
public static RoomHelper getChatDB() { return chatDB; }
|
||||
|
||||
public static void initNetworkHandler() { networkHandler = new NetworkHandler(); }
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class DSAKey implements Serializable {
|
||||
private BigInteger Q;
|
||||
private BigInteger P;
|
||||
private BigInteger Y;
|
||||
private BigInteger G;
|
||||
|
||||
public DSAKey(BigInteger Q, BigInteger P, BigInteger Y, BigInteger G) {
|
||||
this.Q = Q;
|
||||
this.P = P;
|
||||
this.Y = Y;
|
||||
this.G = G;
|
||||
}
|
||||
|
||||
public BigInteger getY() {
|
||||
return Y;
|
||||
}
|
||||
|
||||
public BigInteger getG() {
|
||||
return G;
|
||||
}
|
||||
|
||||
public BigInteger getP() {
|
||||
return P;
|
||||
}
|
||||
|
||||
public BigInteger getQ() {
|
||||
return Q;
|
||||
}
|
||||
}
|
@ -14,6 +14,9 @@ public class KeyPairManager {
|
||||
|
||||
public KeyPairManager() {
|
||||
this.keyPairDir = new File(AppHelper.getContext().getFilesDir().getAbsoluteFile(), "keyPairs");
|
||||
if(!this.keyPairDir.exists()) {
|
||||
this.keyPairDir.mkdir();
|
||||
}
|
||||
}
|
||||
|
||||
public KeyPair openMainKeyPair() {
|
||||
@ -37,7 +40,7 @@ public class KeyPairManager {
|
||||
byte[] serializedKeyPair = new byte[(int) keyPairFile.length()];
|
||||
inputStream.read(serializedKeyPair);
|
||||
inputStream.close();
|
||||
keyPair = Serializer.deserializeObject(new String(serializedKeyPair, StandardCharsets.UTF_8));
|
||||
keyPair = (KeyPair) Serializer.deserialize(serializedKeyPair);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -48,9 +51,9 @@ public class KeyPairManager {
|
||||
KeyPair keyPair = null;
|
||||
try {
|
||||
keyPairFile.createNewFile();
|
||||
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
keyPair = KeyPairGenerator.getInstance("DSA").generateKeyPair();
|
||||
FileOutputStream outputStream = new FileOutputStream(keyPairFile);
|
||||
outputStream.write(Serializer.serializeObject(keyPair).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.write(Serializer.serialize(keyPair));
|
||||
outputStream.close();
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
@ -63,7 +66,7 @@ public class KeyPairManager {
|
||||
if(!keyPairFile.exists()) {
|
||||
try {
|
||||
FileOutputStream outputStream = new FileOutputStream(keyPairFile);
|
||||
outputStream.write(Serializer.serializeObject(keyPair).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.write(Serializer.serialize(keyPair));
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -1,62 +1,77 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.observer.Observer;
|
||||
import java.util.List;
|
||||
|
||||
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.observable.MainObservable;
|
||||
|
||||
public class NetworkHandler implements Observer {
|
||||
public class NetworkHandler implements NetworkObserver {
|
||||
private final static String LOG_TAG = "NetworkHandler";
|
||||
private Gson gson;
|
||||
private PeerDHT peerDHT;
|
||||
|
||||
public NetworkHandler() {
|
||||
gson = new Gson();
|
||||
peerDHT = AppHelper.getPeerDHT();
|
||||
AppHelper.getObservable().register(this, MainObservable.OTHER_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(JsonObject object) {
|
||||
public void handleEvent(Object object) {
|
||||
new Thread(() -> {
|
||||
switch (object.get("action").getAsInt()) {
|
||||
case NetworkActions.START_CHAT: {
|
||||
String chatStarterPlainAddress = object.get("senderAddress").getAsString();
|
||||
createChatEntry(object.get("chatID").getAsString(), object.get("senderID").getAsString(), chatStarterPlainAddress);
|
||||
handleIncomingChat(object.get("chatID").getAsString(), PrepareData.prepareFromStore(chatStarterPlainAddress));
|
||||
switch (getMessageAction((String) object)) {
|
||||
case NetworkActions.CREATE_CHAT: {
|
||||
NewChatRequestMessage newChatRequestMessage = gson.fromJson((String) object, NewChatRequestMessage.class);
|
||||
createChatEntry(newChatRequestMessage.getChatID(), newChatRequestMessage.getChatID(), newChatRequestMessage.getSenderPeerAddress());
|
||||
handleIncomingChat(newChatRequestMessage.getChatID(), newChatRequestMessage.getSenderPeerAddress());
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.NEW_CHAT);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkActions.SUCCESSFULL_CREATE_CHAT: {
|
||||
createChatEntry(object.get("chatID").getAsString(), object.get("senderID").getAsString(), object.get("senderAddress").getAsString());
|
||||
NewChatRequestMessage newChatRequestMessage = gson.fromJson((String) object, NewChatRequestMessage.class);
|
||||
createChatEntry(newChatRequestMessage.getChatID(), newChatRequestMessage.getSenderID(), newChatRequestMessage.getSenderPeerAddress());
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.NEW_CHAT);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void createChatEntry(String chatID, String name, String peerAddress) {
|
||||
AppHelper.getChatDB().chatDao().addChat(new ChatEntity(chatID, name, peerAddress, ""));
|
||||
private int getMessageAction(String json) {
|
||||
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
|
||||
return jsonObject.get("action").getAsInt();
|
||||
}
|
||||
|
||||
private void createChatEntry(String chatID, String name, PeerAddress peerAddress) {
|
||||
List<ChatEntity> chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID);
|
||||
if (chatEntities.size() > 0) {
|
||||
Log.e(LOG_TAG, "Failed to create chat " + chatID + " because chat exists!");
|
||||
return;
|
||||
}
|
||||
AppHelper.getChatDB().chatDao().addChat(new ChatEntity(chatID, name, "", Serializer.serialize(peerAddress)));
|
||||
}
|
||||
|
||||
private void handleIncomingChat(String chatID, PeerAddress chatStarterAddress) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", NetworkActions.SUCCESSFULL_CREATE_CHAT);
|
||||
jsonObject.addProperty("chatID", chatID);
|
||||
jsonObject.addProperty("senderID", AppHelper.getPeerID());
|
||||
jsonObject.addProperty("senderAddress", PrepareData.prepareToStore(peerDHT.peerAddress()));
|
||||
AppHelper.getPeerDHT().peer().sendDirect(chatStarterAddress).object(gson.toJson(jsonObject)).start().awaitUninterruptibly();
|
||||
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress());
|
||||
newChatRequestMessage.setChatID(chatID);
|
||||
newChatRequestMessage.setAction(NetworkActions.SUCCESSFULL_CREATE_CHAT);
|
||||
AppHelper.getPeerDHT().peer().sendDirect(chatStarterAddress).object(gson.toJson(newChatRequestMessage)).start().awaitUninterruptibly();
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import android.util.Base64;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PrepareData {
|
||||
public static <T> String prepareToStore(T object) {
|
||||
String serializedObject = Serializer.serializeObject(object);
|
||||
return Base64.encodeToString(serializedObject.getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE);
|
||||
if(object instanceof Serializable) {
|
||||
byte[] serializedObject = Serializer.serialize(object);
|
||||
return new String(Base64.encode(serializedObject));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T prepareFromStore(String object) {
|
||||
String decodedString = new String(Base64.decode(object, Base64.URL_SAFE), StandardCharsets.UTF_8);
|
||||
return Serializer.deserializeObject(decodedString);
|
||||
public static Object prepareFromStore(String object) {
|
||||
return Serializer.deserialize(Base64.decode(object.getBytes()));
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,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 = 1)
|
||||
@Database(entities = { MessageEntity.class, ChatEntity.class }, version = 2)
|
||||
public abstract class RoomHelper extends RoomDatabase {
|
||||
public abstract ChatDao chatDao();
|
||||
public abstract MessageDao messageDao();
|
||||
|
@ -1,45 +1,48 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import org.jboss.serial.io.JBossObjectInputStream;
|
||||
import org.jboss.serial.io.JBossObjectOutputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class Serializer {
|
||||
public static <T> String serializeObject(T t) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
public static byte[] serialize(Object object) {
|
||||
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
|
||||
try {
|
||||
JBossObjectOutputStream out = new JBossObjectOutputStream(outputStream);
|
||||
out.writeObject(t);
|
||||
out.close();
|
||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArray);
|
||||
objectOutputStream.writeObject(object);;
|
||||
objectOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
return outputStream.toString("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
return byteArray.toByteArray();
|
||||
}
|
||||
|
||||
public static <T> T deserializeObject(String str) {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
|
||||
T obj = null;
|
||||
public static Object deserialize(String serializedObject) {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedObject.getBytes());
|
||||
Object object = null;
|
||||
try {
|
||||
JBossObjectInputStream in = new JBossObjectInputStream(inputStream);
|
||||
obj = (T) in.readObject();
|
||||
in.close();
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
|
||||
object = objectInputStream.readObject();
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return obj;
|
||||
return object;
|
||||
}
|
||||
|
||||
public static Object deserialize(byte[] serializedObject) {
|
||||
if(serializedObject == null)
|
||||
return null;
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedObject);
|
||||
Object object = null;
|
||||
try {
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
|
||||
object = objectInputStream.readObject();
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,30 @@
|
||||
package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import net.tomp2p.connection.DSASignatureFactory;
|
||||
import net.tomp2p.connection.SignatureFactory;
|
||||
import net.tomp2p.dht.Storage;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number320;
|
||||
import net.tomp2p.peers.Number480;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.storage.AlternativeCompositeByteBuf;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import org.h2.mvstore.MVMap;
|
||||
import org.h2.mvstore.MVStore;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -24,26 +37,37 @@ import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import static io.github.chronosx88.influence.helpers.Serializer.deserialize;
|
||||
import static io.github.chronosx88.influence.helpers.Serializer.serialize;
|
||||
|
||||
public class StorageMVStore implements Storage {
|
||||
private final String LOG_TAG = "StorageMVStore";
|
||||
|
||||
private MVStore db;
|
||||
|
||||
// Core
|
||||
final private MVMap<String, String> dataMap; // <Number 640, Data>
|
||||
final private MVMap<byte[], byte[]> dataMap; // <Number 640, Data>
|
||||
|
||||
// Maintenance
|
||||
final private MVMap<String, String> timeoutMap; // <Number640, Long>
|
||||
final private MVMap<String, String> timeoutMapRev; // <Long, Set<Number640>>
|
||||
final private MVMap<byte[], byte[]> timeoutMap; // <Number640, Long>
|
||||
final private MVMap<byte[], byte[]> timeoutMapRev; // <Long, Set<Number640>>
|
||||
|
||||
// Protection
|
||||
final private MVMap<String, String> protectedDomainMap; // <Number320, PublicKey>
|
||||
final private MVMap<String, String> protectedEntryMap; // <Number480, PublicKey>
|
||||
final private MVMap<byte[], byte[]> protectedDomainMap; // <Number320, PublicKey>
|
||||
final private MVMap<byte[], byte[]> protectedEntryMap; // <Number480, PublicKey>
|
||||
|
||||
// Responsibility
|
||||
final private MVMap<String, String> responsibilityMap; // <Number160, Number160>
|
||||
final private MVMap<String, String> responsibilityMapRev; // <Number160, Set<Number160>>
|
||||
final private MVMap<byte[], byte[]> responsibilityMap; // <Number160, Number160>
|
||||
final private MVMap<byte[], byte[]> responsibilityMapRev; // <Number160, Set<Number160>>
|
||||
|
||||
final private int storageCheckIntervalMillis;
|
||||
|
||||
final private SignatureFactory signatureFactory;
|
||||
final private KeyPairManager keyPairManager;
|
||||
|
||||
public StorageMVStore(Number160 peerID, File path) {
|
||||
db = new MVStore.Builder()
|
||||
.fileName(path.getAbsolutePath() + "/coreDB.db")
|
||||
@ -56,31 +80,33 @@ public class StorageMVStore implements Storage {
|
||||
responsibilityMap = db.openMap("responsibilityMap_" + peerID.toString());
|
||||
responsibilityMapRev = db.openMap("responsibilityMapRev_ " + peerID.toString());
|
||||
storageCheckIntervalMillis = 60 * 1000;
|
||||
signatureFactory = new DSASignatureFactory();
|
||||
keyPairManager = new KeyPairManager();
|
||||
Runtime.getRuntime().addShutdownHook(new JVMShutdownHook(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data put(Number640 key, Data value) {
|
||||
Data oldData = Serializer.deserializeObject(dataMap.put(Serializer.serializeObject(key), Serializer.serializeObject(value)));
|
||||
Data oldData = deserializeData(dataMap.put(serialize(key), serializeData(value)));
|
||||
db.commit();
|
||||
return oldData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data get(Number640 key) {
|
||||
return Serializer.deserializeObject(dataMap.get(Serializer.serializeObject(key)));
|
||||
return deserializeData(dataMap.get(serialize(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Number640 key) {
|
||||
return dataMap.containsKey(Serializer.serializeObject(key));
|
||||
return dataMap.containsKey(serialize(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int contains(Number640 from, Number640 to) {
|
||||
TreeMap<Number640, Data> tmp = new TreeMap<>();
|
||||
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
|
||||
tmp.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
|
||||
for (Map.Entry<byte[], byte[]> entry: dataMap.entrySet()) {
|
||||
tmp.put((Number640) deserialize(entry.getKey()), deserializeData(entry.getValue()));
|
||||
}
|
||||
|
||||
return tmp.subMap(from, true, to, true).size();
|
||||
@ -88,7 +114,7 @@ public class StorageMVStore implements Storage {
|
||||
|
||||
@Override
|
||||
public Data remove(Number640 key, boolean returnData) {
|
||||
Data retVal = Serializer.deserializeObject(dataMap.remove(Serializer.serializeObject(key)));
|
||||
Data retVal = deserializeData(dataMap.remove(serialize(key)));
|
||||
db.commit();
|
||||
return retVal;
|
||||
}
|
||||
@ -96,13 +122,13 @@ public class StorageMVStore implements Storage {
|
||||
@Override
|
||||
public NavigableMap<Number640, Data> remove(Number640 from, Number640 to) {
|
||||
TreeMap<Number640, Data> tmp = new TreeMap<>();
|
||||
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
|
||||
tmp.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
|
||||
for (Map.Entry<byte[], byte[]> entry: dataMap.entrySet()) {
|
||||
tmp.put((Number640) deserialize(entry.getKey()), deserializeData(entry.getValue()));
|
||||
}
|
||||
|
||||
NavigableMap<Number640, Data> tmpSubMap = tmp.subMap(from, true, to, true);
|
||||
for (Map.Entry<Number640, Data> entry : tmpSubMap.entrySet()) {
|
||||
dataMap.remove(Serializer.serializeObject(entry.getKey()));
|
||||
dataMap.remove(serialize(entry.getKey()));
|
||||
}
|
||||
db.commit();
|
||||
|
||||
@ -112,8 +138,8 @@ public class StorageMVStore implements Storage {
|
||||
@Override
|
||||
public NavigableMap<Number640, Data> subMap(Number640 from, Number640 to, int limit, boolean ascending) {
|
||||
TreeMap<Number640, Data> tmpDataMap = new TreeMap<>();
|
||||
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
|
||||
tmpDataMap.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
|
||||
for (Map.Entry<byte[], byte[]> entry: dataMap.entrySet()) {
|
||||
tmpDataMap.put((Number640) deserialize(entry.getKey()), deserializeData(entry.getValue()));
|
||||
}
|
||||
|
||||
NavigableMap<Number640, Data> tmp = tmpDataMap.subMap(from, true, to, true);
|
||||
@ -138,8 +164,8 @@ public class StorageMVStore implements Storage {
|
||||
@Override
|
||||
public NavigableMap<Number640, Data> map() {
|
||||
final NavigableMap<Number640, Data> retVal = new TreeMap<>();
|
||||
for(final Map.Entry<String, String> entry:dataMap.entrySet()) {
|
||||
retVal.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
|
||||
for(final Map.Entry<byte[], byte[]> entry : dataMap.entrySet()) {
|
||||
retVal.put((Number640) deserialize(entry.getKey()), deserializeData(entry.getValue()));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
@ -152,7 +178,7 @@ public class StorageMVStore implements Storage {
|
||||
|
||||
@Override
|
||||
public void addTimeout(Number640 key, long expiration) {
|
||||
Long oldExpiration = Serializer.deserializeObject(timeoutMap.put(Serializer.serializeObject(key), Serializer.serializeObject(expiration)));
|
||||
Long oldExpiration = (Long) deserialize(timeoutMap.put(serialize(key), serialize(expiration)));
|
||||
putIfAbsent2(expiration, key);
|
||||
if (oldExpiration == null) {
|
||||
return;
|
||||
@ -162,29 +188,29 @@ public class StorageMVStore implements Storage {
|
||||
}
|
||||
|
||||
private void putIfAbsent2(long expiration, Number640 key) {
|
||||
Set<Number640> timeouts = Serializer.deserializeObject(timeoutMapRev.get(Serializer.serializeObject(expiration)));
|
||||
Set<Number640> timeouts = (Set<Number640>) deserialize(timeoutMapRev.get(serialize(expiration)));
|
||||
if(timeouts == null) {
|
||||
timeouts = Collections.newSetFromMap(new ConcurrentHashMap<Number640, Boolean>());
|
||||
timeouts = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
}
|
||||
timeouts.add(key);
|
||||
timeoutMapRev.put(Serializer.serializeObject(expiration), Serializer.serializeObject(timeouts));
|
||||
timeoutMapRev.put(serialize(expiration), serialize(timeouts));
|
||||
}
|
||||
|
||||
private void removeRevTimeout(Number640 key, Long expiration) {
|
||||
Set<Number640> tmp = Serializer.deserializeObject(timeoutMapRev.get(Serializer.serializeObject(expiration)));
|
||||
Set<Number640> tmp = (Set<Number640>) deserialize(timeoutMapRev.get(serialize(expiration)));
|
||||
if (tmp != null) {
|
||||
tmp.remove(key);
|
||||
if (tmp.isEmpty()) {
|
||||
timeoutMapRev.remove(expiration);
|
||||
timeoutMapRev.remove(serialize(expiration));
|
||||
} else {
|
||||
timeoutMapRev.put(Serializer.serializeObject(expiration), Serializer.serializeObject(tmp));
|
||||
timeoutMapRev.put(serialize(expiration), serialize(tmp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTimeout(Number640 key) {
|
||||
Long expiration = Serializer.deserializeObject(timeoutMap.remove(Serializer.serializeObject(key)));
|
||||
Long expiration = (Long) deserialize(timeoutMap.remove(serialize(key)));
|
||||
if (expiration == null) {
|
||||
return;
|
||||
}
|
||||
@ -195,8 +221,8 @@ public class StorageMVStore implements Storage {
|
||||
@Override
|
||||
public Collection<Number640> subMapTimeout(long to) {
|
||||
TreeMap<Long, Set<Number640>> tmpTimeoutMapRev = new TreeMap<>();
|
||||
for (Map.Entry<String, String> entry: timeoutMapRev.entrySet()) {
|
||||
tmpTimeoutMapRev.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
|
||||
for (Map.Entry<byte[], byte[]> entry: timeoutMapRev.entrySet()) {
|
||||
tmpTimeoutMapRev.put( (Long) deserialize(entry.getKey()), (Set<Number640>) deserialize(entry.getValue()));
|
||||
}
|
||||
|
||||
SortedMap<Long, Set<Number640>> tmp = tmpTimeoutMapRev.subMap(0L, to);
|
||||
@ -214,13 +240,13 @@ public class StorageMVStore implements Storage {
|
||||
|
||||
@Override
|
||||
public boolean protectDomain(Number320 key, PublicKey publicKey) {
|
||||
protectedDomainMap.put(Serializer.serializeObject(key), Serializer.serializeObject(publicKey));
|
||||
protectedDomainMap.put(serialize(key), serialize(publicKey));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDomainProtectedByOthers(Number320 key, PublicKey publicKey) {
|
||||
PublicKey other = Serializer.deserializeObject(protectedDomainMap.get(Serializer.serializeObject(key)));
|
||||
PublicKey other = (PublicKey) deserialize(protectedDomainMap.get(serialize(key)));
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
@ -229,13 +255,13 @@ public class StorageMVStore implements Storage {
|
||||
|
||||
@Override
|
||||
public boolean protectEntry(Number480 key, PublicKey publicKey) {
|
||||
protectedEntryMap.put(Serializer.serializeObject(key), Serializer.serializeObject(publicKey));
|
||||
protectedEntryMap.put(serialize(key), serialize(publicKey));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntryProtectedByOthers(Number480 key, PublicKey publicKey) {
|
||||
PublicKey other = Serializer.deserializeObject(protectedEntryMap.get(Serializer.serializeObject(key)));
|
||||
PublicKey other = (PublicKey) deserialize(protectedEntryMap.get(serialize(key)));
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
@ -244,17 +270,17 @@ public class StorageMVStore implements Storage {
|
||||
|
||||
@Override
|
||||
public Number160 findPeerIDsForResponsibleContent(Number160 locationKey) {
|
||||
return Serializer.deserializeObject(responsibilityMap.get(Serializer.serializeObject(locationKey)));
|
||||
return (Number160) deserialize(responsibilityMap.get(serialize(locationKey)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Number160> findContentForResponsiblePeerID(Number160 peerID) {
|
||||
return Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerID)));
|
||||
return (Collection<Number160>) deserialize(responsibilityMapRev.get(serialize(peerID)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateResponsibilities(Number160 locationKey, Number160 peerId) {
|
||||
final Number160 oldPeerID = Serializer.deserializeObject(responsibilityMap.put(Serializer.serializeObject(locationKey), Serializer.serializeObject(peerId)));
|
||||
final Number160 oldPeerID = (Number160) deserialize(responsibilityMap.put(serialize(locationKey), serialize(peerId)));
|
||||
final boolean hasChanged;
|
||||
if(oldPeerID != null) {
|
||||
if(oldPeerID.equals(peerId)) {
|
||||
@ -266,30 +292,103 @@ public class StorageMVStore implements Storage {
|
||||
} else {
|
||||
hasChanged = true;
|
||||
}
|
||||
Set<Number160> contentIDs = Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerId)));
|
||||
Set<Number160> contentIDs = (Set<Number160>) deserialize(responsibilityMapRev.get(serialize(peerId)));
|
||||
if(contentIDs == null) {
|
||||
contentIDs = new HashSet<Number160>();
|
||||
contentIDs = new HashSet<>();
|
||||
}
|
||||
contentIDs.add(locationKey);
|
||||
responsibilityMapRev.put(Serializer.serializeObject(peerId), Serializer.serializeObject(contentIDs));
|
||||
responsibilityMapRev.put(serialize(peerId), serialize(contentIDs));
|
||||
db.commit();
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResponsibility(Number160 locationKey) {
|
||||
|
||||
final Number160 peerId = new Number160(responsibilityMap.remove(serialize(locationKey)));
|
||||
if(peerId != null) {
|
||||
removeRevResponsibility(peerId, locationKey);
|
||||
}
|
||||
db.commit();
|
||||
}
|
||||
|
||||
private void removeRevResponsibility(Number160 peerId, Number160 locationKey) {
|
||||
Set<Number160> contentIDs = Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerId)));
|
||||
Set<Number160> contentIDs = (Set<Number160>) deserialize(responsibilityMapRev.get(serialize(peerId)));
|
||||
if (contentIDs != null) {
|
||||
contentIDs.remove(locationKey);
|
||||
if (contentIDs.isEmpty()) {
|
||||
responsibilityMapRev.remove(peerId);
|
||||
responsibilityMapRev.remove(serialize(peerId));
|
||||
} else {
|
||||
responsibilityMapRev.put(Serializer.serializeObject(peerId), Serializer.serializeObject(contentIDs));
|
||||
responsibilityMapRev.put(serialize(peerId), serialize(contentIDs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private byte[] serializeData(Data data) {
|
||||
KeyPair mainKeyPair = keyPairManager.openMainKeyPair();
|
||||
KeyPair forSigningKP = keyPairManager.getKeyPair("mainSigningKeyPair");
|
||||
data.sign(forSigningKP).protectEntry(mainKeyPair);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
AlternativeCompositeByteBuf acb = AlternativeCompositeByteBuf.compBuffer(AlternativeCompositeByteBuf.UNPOOLED_HEAP);
|
||||
try {
|
||||
// header first
|
||||
data.encodeHeader(acb, signatureFactory);
|
||||
writeData(out, acb.nioBuffers());
|
||||
acb.skipBytes(acb.writerIndex());
|
||||
// next data - no need to copy to another buffer, just take the data
|
||||
// from memory
|
||||
writeData(out, data.toByteBuffers());
|
||||
// rest
|
||||
data.encodeDone(acb, signatureFactory);
|
||||
writeData(out, acb.nioBuffers());
|
||||
} catch (SignatureException | InvalidKeyException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private void writeData(OutputStream out, ByteBuffer[] nioBuffers) throws IOException {
|
||||
final int length = nioBuffers.length;
|
||||
for(int i=0;i < length; i++) {
|
||||
int remaining = nioBuffers[i].remaining();
|
||||
if(nioBuffers[i].hasArray()) {
|
||||
out.write(nioBuffers[i].array(), nioBuffers[i].arrayOffset(), remaining);
|
||||
} else {
|
||||
byte[] me = new byte[remaining];
|
||||
nioBuffers[i].get(me);
|
||||
out.write(me);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Data deserializeData(byte[] serializedData) {
|
||||
if(serializedData == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(serializedData);
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Data data = null;
|
||||
while(data == null) {
|
||||
buf.writeByte(in.read());
|
||||
data = Data.decodeHeader(buf, signatureFactory);
|
||||
}
|
||||
int len = data.length();
|
||||
byte me[] = new byte[len];
|
||||
try {
|
||||
in.read(me);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
buf = Unpooled.wrappedBuffer(me);
|
||||
boolean retVal = data.decodeBuffer(buf);
|
||||
if(!retVal) {
|
||||
Log.e(LOG_TAG, "# ERROR: Data could not be deserialized!");
|
||||
}
|
||||
retVal = data.decodeDone(buf, signatureFactory);
|
||||
if(!retVal) {
|
||||
Log.e(LOG_TAG, "# ERROR: Signature could not be read!");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.github.chronosx88.influence.helpers.actions;
|
||||
|
||||
public class NetworkActions {
|
||||
public static final int START_CHAT = 0x0;
|
||||
public static final int CREATE_CHAT = 0x0;
|
||||
public static final int NEW_MESSAGE = 0x1;
|
||||
public static final int MESSAGE_SENT = 0x2;
|
||||
public static final int PING = 0x3;
|
||||
|
@ -9,6 +9,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.PeerBuilderDHT;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FutureBootstrap;
|
||||
@ -30,15 +31,25 @@ import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
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.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.DSAKey;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
import io.github.chronosx88.influence.helpers.PrepareData;
|
||||
import io.github.chronosx88.influence.helpers.Serializer;
|
||||
import io.github.chronosx88.influence.helpers.StorageMVStore;
|
||||
import io.github.chronosx88.influence.helpers.actions.NetworkActions;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.models.BasicNetworkMessage;
|
||||
import io.github.chronosx88.influence.models.NewChatRequestMessage;
|
||||
import io.github.chronosx88.influence.models.PublicUserProfile;
|
||||
import io.github.chronosx88.influence.observable.MainObservable;
|
||||
|
||||
public class MainLogic implements MainLogicContract {
|
||||
@ -92,47 +103,52 @@ public class MainLogic implements MainLogicContract {
|
||||
} catch (NullPointerException e) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.BOOTSTRAP_NOT_SPECIFIED);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
peerDHT.shutdown();
|
||||
return;
|
||||
} catch (UnknownHostException e) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.NETWORK_ERROR);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
peerDHT.shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean discoveredExternalAddress = false;
|
||||
|
||||
if(!discoverExternalAddress()) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.PORT_FORWARDING_ERROR);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
} else {
|
||||
discoveredExternalAddress = true;
|
||||
}
|
||||
|
||||
if(!discoveredExternalAddress) {
|
||||
if(!setupConnectionToRelay()) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.RELAY_CONNECTION_ERROR);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bootstrapPeer()) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.BOOTSTRAP_ERROR);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.BOOTSTRAP_SUCCESS);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
AppHelper.storePeerID(preferences.getString("peerID", null));
|
||||
AppHelper.storePeerDHT(peerDHT);
|
||||
AppHelper.initNetworkHandler();
|
||||
setReceiveHandler();
|
||||
Gson gson = new Gson();
|
||||
JsonObject publicProfile = new JsonObject();
|
||||
publicProfile.addProperty("peerAddress", Base64.encodeToString(Serializer.serializeObject(peerDHT.peerAddress()).getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE));
|
||||
peerDHT.put(Number160.createHash(preferences.getString("peerID", null) + "_profile")).data(new Data(gson.toJson(publicProfile)).protectEntry(keyPairManager.openMainKeyPair())).start().awaitUninterruptibly();
|
||||
gson = new Gson();
|
||||
publicProfileToDHT();
|
||||
replication = new AutoReplication(peerDHT.peer()).start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@ -185,21 +201,20 @@ public class MainLogic implements MainLogicContract {
|
||||
private void setReceiveHandler() {
|
||||
AppHelper.getPeerDHT().peer().objectDataReply((s, r) -> {
|
||||
Log.i(LOG_TAG, "# Incoming message: " + r);
|
||||
JSONObject incomingObject = new JSONObject((String) r);
|
||||
if(incomingObject.getInt("action") == NetworkActions.PING) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", NetworkActions.PONG);
|
||||
return gson.toJson(jsonObject);
|
||||
}
|
||||
AppHelper.getObservable().notifyObservers(new JsonParser().parse((String) r).getAsJsonObject(), MainObservable.OTHER_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyNetworkObservers(r);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownPeer() {
|
||||
new Thread(() -> {
|
||||
if(replication != null) {
|
||||
replication.shutdown().start();
|
||||
}
|
||||
peerDHT.peer().announceShutdown().start().awaitUninterruptibly();
|
||||
peerDHT.peer().shutdown();
|
||||
}).start();
|
||||
}
|
||||
|
||||
private boolean checkFirstRun() {
|
||||
@ -211,4 +226,34 @@ public class MainLogic implements MainLogicContract {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void publicProfileToDHT() {
|
||||
KeyPair mainKeyPair = keyPairManager.openMainKeyPair();
|
||||
KeyFactory factory = null;
|
||||
try {
|
||||
factory = KeyFactory.getInstance("DSA");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
PublicUserProfile userProfile = null;
|
||||
try {
|
||||
DSAPublicKeySpec dsaKey = factory.getKeySpec(mainKeyPair.getPublic(), DSAPublicKeySpec.class);
|
||||
userProfile = new PublicUserProfile(AppHelper.getPeerID(), peerDHT.peerAddress(), new DSAKey(dsaKey.getQ(), dsaKey.getP(), dsaKey.getY(), dsaKey.getG()));
|
||||
} catch (InvalidKeySpecException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Data serializedUserProfile = null;
|
||||
try {
|
||||
serializedUserProfile = new Data(gson.toJson(userProfile))
|
||||
.protectEntry(mainKeyPair.getPrivate())
|
||||
.sign(keyPairManager.getKeyPair("mainSigningKeyPair"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
FuturePut futurePut = peerDHT.put(Number160.createHash(AppHelper.getPeerID() + "_profile"))
|
||||
.data(serializedUserProfile)
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
Log.i(LOG_TAG, futurePut.isSuccess() ? "# Profile successfully published!" : "# Profile publishing failed!");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.chronosx88.influence.logic;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -16,16 +15,16 @@ import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.startchat.StartChatLogicContract;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
import io.github.chronosx88.influence.helpers.PrepareData;
|
||||
import io.github.chronosx88.influence.helpers.Serializer;
|
||||
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.PublicUserProfile;
|
||||
import io.github.chronosx88.influence.observable.MainObservable;
|
||||
|
||||
public class StartChatLogic implements StartChatLogicContract {
|
||||
@ -43,35 +42,27 @@ public class StartChatLogic implements StartChatLogicContract {
|
||||
@Override
|
||||
public void sendStartChatMessage(String peerID) {
|
||||
new Thread(() -> {
|
||||
JsonObject recipientPublicProfile = getPublicProfile(peerID);
|
||||
PublicUserProfile recipientPublicProfile = getPublicProfile(peerID);
|
||||
if(recipientPublicProfile == null) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", UIActions.PEER_NOT_EXIST);
|
||||
AppHelper.getObservable().notifyObservers(jsonObject, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
return;
|
||||
}
|
||||
String peerAddressString = recipientPublicProfile.get("peerAddress").getAsString();
|
||||
PeerAddress peerAddress = Serializer.deserializeObject(new String(Base64.decode(peerAddressString, Base64.URL_SAFE), StandardCharsets.UTF_8));
|
||||
String chatID = UUID.randomUUID().toString();
|
||||
PeerAddress recipientPeerAddress = getPublicProfile(peerID).getPeerAddress();
|
||||
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("action", NetworkActions.START_CHAT);
|
||||
jsonObject.addProperty("chatID", chatID);
|
||||
jsonObject.addProperty("senderID", AppHelper.getPeerID());
|
||||
// TODO: Append public key to new chat request (for encryption)
|
||||
jsonObject.addProperty("senderAddress", PrepareData.prepareToStore(peerDHT.peerAddress()));
|
||||
|
||||
FuturePing ping = peerDHT.peer().ping().peerAddress(peerAddress).start().awaitUninterruptibly();
|
||||
FuturePing ping = peerDHT.peer().ping().peerAddress(recipientPeerAddress).start().awaitUninterruptibly();
|
||||
if(ping.isSuccess()) {
|
||||
peerDHT.peer().sendDirect(peerAddress).object(gson.toJson(jsonObject)).start();
|
||||
peerDHT.peer().sendDirect(recipientPeerAddress).object(gson.toJson(new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress()))).start();
|
||||
} else {
|
||||
try {
|
||||
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(AppHelper.getPeerID(), peerDHT.peerAddress());
|
||||
FuturePut futurePut = peerDHT
|
||||
.put(Number160.createHash(peerID))
|
||||
.data(Number160.createHash(UUID.randomUUID().toString()), new Data(gson.toJson(jsonObject))
|
||||
.data(Number160.createHash(UUID.randomUUID().toString()), new Data(gson.toJson(newChatRequestMessage))
|
||||
.protectEntry(keyPairManager.openMainKeyPair())).start().awaitUninterruptibly();
|
||||
if(futurePut.isSuccess()) {
|
||||
Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + chatID);
|
||||
Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + newChatRequestMessage.getChatID());
|
||||
} else {
|
||||
Log.e(LOG_TAG, "# Failed to create chat: " + futurePut.failedReason());
|
||||
}
|
||||
@ -82,8 +73,8 @@ public class StartChatLogic implements StartChatLogicContract {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private JsonObject getPublicProfile(String peerID) {
|
||||
JsonObject publicProfile;
|
||||
private PublicUserProfile getPublicProfile(String peerID) {
|
||||
PublicUserProfile publicProfile = null;
|
||||
FutureGet futureGetProfile = peerDHT.get(Number160.createHash(peerID + "_profile")).start().awaitUninterruptibly();
|
||||
if (futureGetProfile.isSuccess()) {
|
||||
String jsonString = null;
|
||||
@ -94,7 +85,13 @@ public class StartChatLogic implements StartChatLogicContract {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
publicProfile = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
try {
|
||||
publicProfile = gson.fromJson((String) futureGetProfile.data().object(), PublicUserProfile.class);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return publicProfile;
|
||||
}
|
||||
return null;
|
||||
|
@ -0,0 +1,48 @@
|
||||
package io.github.chronosx88.influence.models;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Абстрактный класс-модель для любых сообщений, которые передаются по DHT-сети
|
||||
*/
|
||||
public class BasicNetworkMessage implements Serializable {
|
||||
private int action;
|
||||
private String senderID;
|
||||
private PeerAddress senderPeerAddress;
|
||||
|
||||
public BasicNetworkMessage() {
|
||||
//
|
||||
}
|
||||
|
||||
public BasicNetworkMessage(int action, String senderID, PeerAddress senderPeerAddress) {
|
||||
this.action = action;
|
||||
this.senderID = senderID;
|
||||
this.senderPeerAddress = senderPeerAddress;
|
||||
}
|
||||
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public String getSenderID() {
|
||||
return senderID;
|
||||
}
|
||||
|
||||
public PeerAddress getSenderPeerAddress() {
|
||||
return senderPeerAddress;
|
||||
}
|
||||
|
||||
public void setAction(int action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public void setSenderID(String senderID) {
|
||||
this.senderID = senderID;
|
||||
}
|
||||
|
||||
public void setSenderPeerAddress(PeerAddress senderPeerAddress) {
|
||||
this.senderPeerAddress = senderPeerAddress;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.github.chronosx88.influence.models;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.actions.NetworkActions;
|
||||
|
||||
public class NewChatRequestMessage extends BasicNetworkMessage implements Serializable {
|
||||
private String chatID;
|
||||
|
||||
public NewChatRequestMessage(String senderID, PeerAddress senderPeerAddress) {
|
||||
super(NetworkActions.CREATE_CHAT, senderID, senderPeerAddress);
|
||||
this.chatID = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public String getChatID() {
|
||||
return chatID;
|
||||
}
|
||||
|
||||
public void setChatID(String chatID) {
|
||||
this.chatID = chatID;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package io.github.chronosx88.influence.models;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.DSAKey;
|
||||
|
||||
/**
|
||||
* Класс-модель публичного профиля для размещения в DHT-сети
|
||||
*/
|
||||
public class PublicUserProfile implements Serializable {
|
||||
private String userName;
|
||||
private PeerAddress peerAddress;
|
||||
private DSAKey publicKey;
|
||||
|
||||
public PublicUserProfile(String userName, PeerAddress peerAddress, DSAKey publicKey) {
|
||||
this.userName = userName;
|
||||
this.peerAddress = peerAddress;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public PeerAddress getPeerAddress() {
|
||||
return peerAddress;
|
||||
}
|
||||
|
||||
public void setPeerAddress(PeerAddress peerAddress) {
|
||||
this.peerAddress = peerAddress;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public DSAKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(DSAKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@ public interface ChatDao {
|
||||
@Query("SELECT * FROM chats")
|
||||
List<ChatEntity> getAllChats();
|
||||
|
||||
@Query("SELECT * FROM chats WHERE id = :chatID")
|
||||
List<ChatEntity> getChatByID(String chatID);
|
||||
@Query("SELECT * FROM chats WHERE chatID = :chatID")
|
||||
List<ChatEntity> getChatByChatID(String chatID);
|
||||
|
||||
@Query("SELECT * FROM chats WHERE id = :id")
|
||||
List<ChatEntity> getChatByID(String id);
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package io.github.chronosx88.influence.models.roomEntities;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
@ -7,19 +12,20 @@ import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "chats")
|
||||
public class ChatEntity {
|
||||
@NonNull @PrimaryKey String id;
|
||||
@PrimaryKey(autoGenerate = true) public int id;
|
||||
@ColumnInfo public String chatID;
|
||||
@ColumnInfo public String name;
|
||||
@ColumnInfo public String peerAddresses;
|
||||
@ColumnInfo public byte[] peerAddresses;
|
||||
@ColumnInfo public String keyPairID;
|
||||
|
||||
public ChatEntity(String id, String name, String peerAddresses, String keyPairID) {
|
||||
this.id = id;
|
||||
public ChatEntity(String chatID, String name, String keyPairID, byte[] peerAddresses) {
|
||||
this.chatID = chatID;
|
||||
this.name = name;
|
||||
this.peerAddresses = peerAddresses;
|
||||
this.keyPairID = keyPairID;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -27,9 +33,13 @@ public class ChatEntity {
|
||||
return keyPairID;
|
||||
}
|
||||
|
||||
public String getPeerAddress() { return peerAddresses; }
|
||||
public byte[] getPeerAddress() { return peerAddresses; }
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getChatID() {
|
||||
return chatID;
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ import androidx.room.PrimaryKey;
|
||||
@Entity(tableName = "messages")
|
||||
public class MessageEntity {
|
||||
@NonNull @PrimaryKey String id;
|
||||
@ColumnInfo String chatID;
|
||||
@ColumnInfo String sender;
|
||||
@ColumnInfo String text;
|
||||
@ColumnInfo public String chatID;
|
||||
@ColumnInfo public String sender;
|
||||
@ColumnInfo public String date;
|
||||
@ColumnInfo public String text;
|
||||
|
||||
public MessageEntity(String id, String chatID, String sender, String text) {
|
||||
public MessageEntity(String id, String chatID, String sender, String date, String text) {
|
||||
this.id = id;
|
||||
this.chatID = chatID;
|
||||
this.sender = sender;
|
||||
this.date = date;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.observer.NetworkObserver;
|
||||
import io.github.chronosx88.influence.contracts.observer.Observable;
|
||||
import io.github.chronosx88.influence.contracts.observer.Observer;
|
||||
|
||||
@ -12,70 +13,44 @@ public class MainObservable implements Observable {
|
||||
public static final int OTHER_ACTIONS_CHANNEL = 1;
|
||||
|
||||
private ArrayList<Observer> uiObservers;
|
||||
private ArrayList<Observer> otherObservers;
|
||||
private ArrayList<NetworkObserver> networkObservers;
|
||||
|
||||
public MainObservable() {
|
||||
this.uiObservers = new ArrayList<>();
|
||||
this.otherObservers = new ArrayList<>();
|
||||
this.networkObservers = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Observer observer, int channelID) {
|
||||
switch (channelID) {
|
||||
case UI_ACTIONS_CHANNEL: {
|
||||
public void register(Observer observer) {
|
||||
uiObservers.add(observer);
|
||||
break;
|
||||
}
|
||||
|
||||
case OTHER_ACTIONS_CHANNEL: {
|
||||
otherObservers.add(observer);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
otherObservers.add(observer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(Observer observer, int channelID) {
|
||||
switch (channelID) {
|
||||
case UI_ACTIONS_CHANNEL: {
|
||||
public void register(NetworkObserver observer) {
|
||||
networkObservers.add(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(Observer observer) {
|
||||
uiObservers.remove(observer);
|
||||
break;
|
||||
}
|
||||
|
||||
case OTHER_ACTIONS_CHANNEL: {
|
||||
otherObservers.remove(observer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyObservers(JsonObject jsonObject, int channelID) {
|
||||
switch (channelID) {
|
||||
case UI_ACTIONS_CHANNEL: {
|
||||
public void unregister(NetworkObserver observer) {
|
||||
networkObservers.remove(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUIObservers(JsonObject jsonObject) {
|
||||
for (Observer observer : uiObservers) {
|
||||
observer.handleEvent(jsonObject);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OTHER_ACTIONS_CHANNEL: {
|
||||
for (Observer observer : otherObservers) {
|
||||
observer.handleEvent(jsonObject);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
for (Observer observer : otherObservers) {
|
||||
observer.handleEvent(jsonObject);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@Override
|
||||
public void notifyNetworkObservers(Object object) {
|
||||
for (NetworkObserver observer : networkObservers) {
|
||||
observer.handleEvent(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,13 +22,12 @@ public class ChatListPresenter implements ChatListPresenterContract, Observer {
|
||||
chatListAdapter = new ChatListAdapter();
|
||||
this.logic = new ChatListLogic();
|
||||
this.view.setRecycleAdapter(chatListAdapter);
|
||||
AppHelper.getObservable().register(this, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateChatList() {
|
||||
chatListAdapter.setChatList(logic.loadAllChats());
|
||||
chatListAdapter.notifyDataSetChanged();
|
||||
view.updateChatList(chatListAdapter, logic.loadAllChats());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,7 +18,7 @@ public class StartChatPresenter implements StartChatPresenterContract, Observer
|
||||
public StartChatPresenter(StartChatViewContract view) {
|
||||
this.view = view;
|
||||
this.logic = new StartChatLogic();
|
||||
AppHelper.getObservable().register(this, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +70,7 @@ public class MainActivity extends AppCompatActivity implements Observer, MainVie
|
||||
.commit();
|
||||
|
||||
presenter = new MainPresenter(this);
|
||||
AppHelper.getObservable().register(this, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().register(this);
|
||||
|
||||
progressDialog = new ProgressDialog(MainActivity.this, R.style.AlertDialogTheme);
|
||||
progressDialog.setCancelable(false);
|
||||
@ -83,7 +83,7 @@ public class MainActivity extends AppCompatActivity implements Observer, MainVie
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
presenter.onDestroy();
|
||||
AppHelper.getObservable().unregister(this, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -7,6 +7,8 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@ -19,6 +21,7 @@ import io.github.chronosx88.influence.contracts.observer.Observer;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.observable.MainObservable;
|
||||
import io.github.chronosx88.influence.presenters.ChatListPresenter;
|
||||
|
||||
@ -29,7 +32,7 @@ public class ChatListFragment extends Fragment implements ChatListViewContract,
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
AppHelper.getObservable().register(this, MainObservable.UI_ACTIONS_CHANNEL);
|
||||
AppHelper.getObservable().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,4 +69,12 @@ public class ChatListFragment extends Fragment implements ChatListViewContract,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
adapter.setChatList(chats);
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
@ -14,10 +15,13 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.startchat.StartChatViewContract;
|
||||
import io.github.chronosx88.influence.presenters.StartChatPresenter;
|
||||
|
||||
public class StartChatFragment extends Fragment implements StartChatViewContract {
|
||||
private TextInputLayout textInputPeerID;
|
||||
private ProgressDialog progressDialog;
|
||||
private Button createChatButton;
|
||||
private StartChatPresenter presenter;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@ -28,10 +32,15 @@ public class StartChatFragment extends Fragment implements StartChatViewContract
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
presenter = new StartChatPresenter(this);
|
||||
textInputPeerID = view.findViewById(R.id.textInputPeerID);
|
||||
progressDialog = new ProgressDialog(getActivity(), R.style.AlertDialogTheme);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
||||
createChatButton = view.findViewById(R.id.create_chat_button);
|
||||
createChatButton.setOnClickListener((v) -> {
|
||||
presenter.startChatWithPeer(textInputPeerID.getEditText().getText().toString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,6 +24,7 @@
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/create_chat_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Создать чат"/>
|
||||
|
Loading…
Reference in New Issue
Block a user