mirror of
https://github.com/ChronosX88/Influence-P2P.git
synced 2024-11-25 00:22:17 +00:00
[BIG COMMIT] Made a username system and slightly fixed the signature system. Refactor.
This commit is contained in:
parent
1eb1f0c0bc
commit
99409e1a13
@ -29,6 +29,7 @@ android {
|
||||
exclude 'META-INF/io.netty.versions.properties'
|
||||
exclude 'LICENSE-EPL-1.0.txt'
|
||||
exclude 'LICENSE-EDL-1.0.txt'
|
||||
exclude 'META-INF/atomicfu.kotlin_module'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -40,20 +41,19 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-alpha02'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha04"
|
||||
annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04"
|
||||
implementation 'org.slf4j:slf4j-log4j12:1.7.26'
|
||||
implementation group: 'com.h2database', name: 'h2-mvstore', version: '1.4.197'
|
||||
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8'
|
||||
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: 'org.springframework.security', name: 'spring-security-crypto', version: '3.1.0.RELEASE'
|
||||
implementation 'de.hdodenhof:circleimageview:3.0.0'
|
||||
implementation group: 'org.objenesis', name: 'objenesis', version: '2.6'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
implementation "org.jetbrains.anko:anko:0.10.8"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.0'
|
||||
implementation 'com.esotericsoftware:kryo:5.0.0-RC1'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -3,14 +3,16 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.github.chronosx88.influence">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".helpers.AppHelper"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".helpers.AppHelper">
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".views.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -22,5 +24,5 @@
|
||||
android:name=".views.ChatActivity"
|
||||
android:theme="@style/NoWindowActionBar" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,91 @@
|
||||
package io.github.chronosx88.influence.contracts
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.MenuItem
|
||||
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
|
||||
interface CoreContracts {
|
||||
|
||||
// -----ChatList-----
|
||||
|
||||
interface IChatListLogicContract {
|
||||
fun loadAllChats(): List<ChatEntity>
|
||||
}
|
||||
|
||||
interface IChatListPresenterContract {
|
||||
fun updateChatList()
|
||||
fun openChat(chatID: String)
|
||||
fun onContextItemSelected(item: MenuItem)
|
||||
}
|
||||
|
||||
interface IChatListViewContract {
|
||||
fun setRecycleAdapter(adapter: ChatListAdapter)
|
||||
fun startActivity(intent: Intent)
|
||||
fun updateChatList(adapter: ChatListAdapter, chats: List<ChatEntity>)
|
||||
}
|
||||
|
||||
// -----StartChat-----
|
||||
|
||||
interface IStartChatLogicContract {
|
||||
fun sendStartChatMessage(peerID: String)
|
||||
}
|
||||
|
||||
interface IStartChatPresenterContract {
|
||||
fun startChatWithPeer(peerID: String)
|
||||
}
|
||||
|
||||
interface IStartChatViewContract {
|
||||
fun showMessage(message: String)
|
||||
fun showProgressDialog(enabled: Boolean)
|
||||
}
|
||||
|
||||
// -----MainActivity-----
|
||||
|
||||
interface IMainLogicContract {
|
||||
fun initPeer()
|
||||
fun shutdownPeer()
|
||||
}
|
||||
|
||||
interface IMainPresenterContract {
|
||||
fun initPeer()
|
||||
fun onDestroy()
|
||||
}
|
||||
|
||||
interface IMainViewContract//
|
||||
|
||||
// -----ChatActivity-----
|
||||
|
||||
interface IChatLogicContract {
|
||||
fun sendMessage(message: MessageEntity)
|
||||
fun stopTrackingForNewMsgs()
|
||||
}
|
||||
|
||||
interface IChatPresenterContract {
|
||||
fun sendMessage(text: String)
|
||||
fun updateAdapter()
|
||||
fun onDestroy()
|
||||
}
|
||||
|
||||
interface IChatViewContract {
|
||||
fun updateMessageList(message: MessageEntity)
|
||||
fun updateMessageList(messages: List<MessageEntity>)
|
||||
}
|
||||
|
||||
// -----SettingsFragment-----
|
||||
|
||||
interface ISettingsLogic {
|
||||
fun checkUsernameExists(username: String) : Boolean
|
||||
}
|
||||
|
||||
interface ISettingsPresenter {
|
||||
fun updateUsername(username: String)
|
||||
}
|
||||
|
||||
interface ISettingsView {
|
||||
fun loadingScreen(state: Boolean)
|
||||
fun showMessage(message: String)
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public interface IChatLogicContract {
|
||||
void sendMessage(MessageEntity message);
|
||||
void stopTrackingForNewMsgs();
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
public interface IChatPresenterContract {
|
||||
void sendMessage(String text);
|
||||
void updateAdapter();
|
||||
void onDestroy();
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatactivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public interface IChatViewContract {
|
||||
void updateMessageList(MessageEntity message);
|
||||
void updateMessageList(List<MessageEntity> messages);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatlist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public interface IChatListLogicContract {
|
||||
List<ChatEntity> loadAllChats();
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatlist;
|
||||
|
||||
import android.view.MenuItem;
|
||||
|
||||
public interface IChatListPresenterContract {
|
||||
void updateChatList();
|
||||
void openChat(String chatID);
|
||||
void onContextItemSelected(MenuItem item);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.chatlist;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public interface IChatListViewContract {
|
||||
void setRecycleAdapter(ChatListAdapter adapter);
|
||||
void startActivity(Intent intent);
|
||||
void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface IMainLogicContract {
|
||||
void initPeer();
|
||||
void shutdownPeer();
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface IMainPresenterContract {
|
||||
void initPeer();
|
||||
void onDestroy();
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.mainactivity;
|
||||
|
||||
public interface IMainViewContract {
|
||||
//
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.startchat;
|
||||
|
||||
public interface IStartChatLogicContract {
|
||||
void sendStartChatMessage(String peerID);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.startchat;
|
||||
|
||||
public interface IStartChatPresenterContract {
|
||||
void startChatWithPeer(String peerID);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.startchat;
|
||||
|
||||
public interface IStartChatViewContract {
|
||||
void showMessage(String message);
|
||||
void showProgressDialog(boolean enabled);
|
||||
}
|
@ -2,6 +2,7 @@ package io.github.chronosx88.influence.helpers;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
|
||||
@ -10,7 +11,7 @@ import androidx.room.Room;
|
||||
import io.github.chronosx88.influence.observable.MainObservable;
|
||||
|
||||
/**
|
||||
* Extended Application class which designed for getting various objects from anywhere in the application.
|
||||
* Extended Application class which designed for centralized getting various objects from anywhere in the application.
|
||||
*/
|
||||
|
||||
public class AppHelper extends MultiDexApplication {
|
||||
@ -20,6 +21,8 @@ public class AppHelper extends MultiDexApplication {
|
||||
private static PeerDHT peerDHT;
|
||||
private static RoomHelper chatDB;
|
||||
private static NetworkHandler networkHandler;
|
||||
private static String username = "";
|
||||
private static SharedPreferences preferences;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@ -29,10 +32,13 @@ public class AppHelper extends MultiDexApplication {
|
||||
chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB")
|
||||
.allowMainThreadQueries()
|
||||
.build();
|
||||
preferences = getApplicationContext().getSharedPreferences("io.github.chronosx88.influence_preferences", MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static void storePeerID(String peerID1) { peerID = peerID1; }
|
||||
|
||||
public static void updateUsername(String username1) { username = username1; }
|
||||
|
||||
public static void storePeerDHT(PeerDHT peerDHT1) { peerDHT = peerDHT1; }
|
||||
|
||||
public static Context getContext() {
|
||||
@ -43,9 +49,15 @@ public class AppHelper extends MultiDexApplication {
|
||||
|
||||
public static String getPeerID() { return peerID; }
|
||||
|
||||
public static String getUsername() { return username; }
|
||||
|
||||
public static PeerDHT getPeerDHT() { return peerDHT; }
|
||||
|
||||
public static RoomHelper getChatDB() { return chatDB; }
|
||||
|
||||
public static void initNetworkHandler() { networkHandler = new NetworkHandler(); }
|
||||
|
||||
public static SharedPreferences getPreferences() {
|
||||
return preferences;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import io.netty.buffer.Unpooled
|
||||
import net.tomp2p.connection.SignatureFactory
|
||||
import net.tomp2p.storage.AlternativeCompositeByteBuf
|
||||
import net.tomp2p.storage.Data
|
||||
import org.mapdb.DataInput2
|
||||
import java.io.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.InvalidKeyException
|
||||
@ -27,7 +28,7 @@ class DataSerializerEx(private val signatureFactory: SignatureFactory) : EntryBi
|
||||
data = Data.decodeHeader(buf, signatureFactory)
|
||||
}
|
||||
val len = data.length()
|
||||
val me = ByteArray(len)
|
||||
var me = ByteArray(len)
|
||||
try {
|
||||
inputStream.read(me)
|
||||
} catch (e: IOException) {
|
||||
@ -39,6 +40,14 @@ class DataSerializerEx(private val signatureFactory: SignatureFactory) : EntryBi
|
||||
if (!retVal) {
|
||||
Log.e(LOG_TAG, "# ERROR: Data could not be deserialized!")
|
||||
}
|
||||
val dataInput = DataInputStream(inputStream)
|
||||
me = ByteArray(signatureFactory.signatureSize())
|
||||
dataInput.readFully(me)
|
||||
buf = Unpooled.wrappedBuffer(me)
|
||||
retVal = data.decodeDone(buf, signatureFactory);
|
||||
if(!retVal) {
|
||||
throw IOException("signature could not be read")
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ public class KeyPairManager {
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair(String keyPairName) {
|
||||
KeyPair keyPair = null;
|
||||
keyPairName = keyPairName + ".kp";
|
||||
File keyPairFile = new File(keyPairDir, keyPairName);
|
||||
if (!keyPairFile.exists()) {
|
||||
@ -52,7 +51,9 @@ public class KeyPairManager {
|
||||
KeyPair keyPair = null;
|
||||
try {
|
||||
keyPairFile.createNewFile();
|
||||
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(1024);
|
||||
keyPair = keyPairGenerator.generateKeyPair();
|
||||
FileOutputStream outputStream = new FileOutputStream(keyPairFile);
|
||||
outputStream.write(serializer.serialize(keyPair));
|
||||
outputStream.close();
|
||||
|
@ -58,7 +58,7 @@ public class NetworkHandler implements INetworkObserver {
|
||||
|
||||
LocalDBWrapper.createChatEntry(
|
||||
newChatRequestMessage.getChatID(),
|
||||
newChatRequestMessage.getSenderID(),
|
||||
newChatRequestMessage.getUsername(),
|
||||
newChatRequestMessage.getChatID() + "_metadata",
|
||||
newChatRequestMessage.getChatID() + "_members",
|
||||
newChatRequestMessage.getChunkID()
|
||||
@ -67,7 +67,7 @@ public class NetworkHandler implements INetworkObserver {
|
||||
P2PUtils.remove(AppHelper.getPeerID() + "_pendingChats", newChatRequestMessage.getChatID());
|
||||
String messageID = UUID.randomUUID().toString();
|
||||
try {
|
||||
P2PUtils.put(newChatRequestMessage.getChatID() + "_messages", messageID, new Data(gson.toJson(new JoinChatMessage(AppHelper.getPeerID(), AppHelper.getPeerID(), newChatRequestMessage.getChatID(), System.currentTimeMillis()))).protectEntry(keyPairManager.openMainKeyPair()));
|
||||
P2PUtils.put(newChatRequestMessage.getChatID() + "_messages", messageID, new Data(gson.toJson(new JoinChatMessage(AppHelper.getPeerID(), AppHelper.getUsername() == null ? AppHelper.getPeerID() : AppHelper.getUsername(), newChatRequestMessage.getChatID(), System.currentTimeMillis()))).protectEntry(keyPairManager.openMainKeyPair()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -75,6 +75,4 @@ public class NetworkHandler implements INetworkObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -13,34 +13,14 @@ import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.Map;
|
||||
|
||||
public class P2PUtils {
|
||||
private static Gson gson = new Gson();
|
||||
private static PeerDHT peerDHT = AppHelper.getPeerDHT();
|
||||
|
||||
public static boolean ping(PeerAddress recipientPeerAddress) {
|
||||
// For connection opening
|
||||
for (int i = 0; i < 5; i++) {
|
||||
peerDHT
|
||||
.peer()
|
||||
.ping()
|
||||
.peerAddress(recipientPeerAddress)
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
FuturePing ping = peerDHT
|
||||
.peer()
|
||||
.ping()
|
||||
.peerAddress(recipientPeerAddress)
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
return ping.isSuccess();
|
||||
}
|
||||
|
||||
public static boolean put(String locationKey, String contentKey, Data data) {
|
||||
data.signed(false);
|
||||
FuturePut futurePut = peerDHT
|
||||
.put(Number160.createHash(locationKey))
|
||||
.data(contentKey == null ? Number160.ZERO : Number160.createHash(contentKey), data)
|
||||
@ -49,6 +29,16 @@ public class P2PUtils {
|
||||
return futurePut.isSuccess();
|
||||
}
|
||||
|
||||
public static boolean put(String locationKey, String contentKey, Data data, KeyPair keyPair) {
|
||||
FuturePut futurePut = peerDHT
|
||||
.put(Number160.createHash(locationKey))
|
||||
.data(contentKey == null ? Number160.ZERO : Number160.createHash(contentKey), data)
|
||||
.keyPair(keyPair)
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
return futurePut.isSuccess();
|
||||
}
|
||||
|
||||
public static Map<Number640, Data> get(String locationKey) {
|
||||
FutureGet futureGet = peerDHT
|
||||
.get(Number160.createHash(locationKey))
|
||||
@ -61,16 +51,6 @@ 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();
|
||||
}
|
||||
|
||||
public static boolean remove(String locationKey, String contentKey) {
|
||||
FutureRemove futureRemove = peerDHT
|
||||
.remove(Number160.createHash(locationKey))
|
||||
@ -79,4 +59,14 @@ public class P2PUtils {
|
||||
.awaitUninterruptibly();
|
||||
return futureRemove.isRemoved();
|
||||
}
|
||||
|
||||
public static boolean remove(String locationKey, String contentKey, KeyPair keyPair) {
|
||||
FutureRemove futureRemove = peerDHT
|
||||
.remove(Number160.createHash(locationKey))
|
||||
.keyPair(keyPair)
|
||||
.contentKey(contentKey == null ? null : Number160.createHash(contentKey))
|
||||
.start()
|
||||
.awaitUninterruptibly();
|
||||
return futureRemove.isRemoved();
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: Signat
|
||||
|
||||
storageCheckIntervalMillis = 60 * 1000
|
||||
|
||||
dataMap = StoredSortedMap(dataMapDB, Serializer<Number640>(), DataSerializerEx(signatureFactory), true)
|
||||
dataMap = StoredSortedMap(dataMapDB, Serializer<Number640>(), DataSerializer(signatureFactory), true)
|
||||
timeoutMap = StoredSortedMap(timeoutMapDB, Serializer<Number640>(), Serializer<Long>(), true)
|
||||
timeoutMapRev = StoredSortedMap(timeoutMapRevDB, Serializer<Long>(), Serializer<Set<Number640>>(), true)
|
||||
protectedDomainMap = StoredSortedMap(protectedDomainMapDB, Serializer<Number320>(), Serializer<PublicKey>(), true)
|
||||
@ -89,8 +89,8 @@ class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: Signat
|
||||
return dataMap.subMap(from, true, to, true).size
|
||||
}
|
||||
|
||||
override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet<Number160> {
|
||||
return responsibilityMapRev[peerID] as MutableSet<Number160>
|
||||
override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet<Number160>? {
|
||||
return responsibilityMapRev[peerID] as MutableSet<Number160>?
|
||||
}
|
||||
|
||||
override fun findPeerIDsForResponsibleContent(locationKey: Number160?): Number160? {
|
||||
|
@ -12,4 +12,6 @@ public class UIActions {
|
||||
public static final int SUCCESSFUL_CREATE_CHAT = 0x8;
|
||||
public static final int MESSAGE_RECEIVED = 0x9;
|
||||
public static final int NODE_IS_OFFLINE = 0x10;
|
||||
public static final int USERNAME_ISNT_AVAILABLE = 0x11;
|
||||
public static final int USERNAME_AVAILABLE = 0x12;
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ package io.github.chronosx88.influence.logic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
|
||||
public class ChatListLogic implements IChatListLogicContract {
|
||||
public class ChatListLogic implements CoreContracts.IChatListLogicContract {
|
||||
|
||||
@Override
|
||||
public List<ChatEntity> loadAllChats() {
|
||||
|
@ -13,7 +13,7 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.IChatLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
@ -27,7 +27,7 @@ import io.github.chronosx88.influence.models.TextMessage;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatLogic implements IChatLogicContract {
|
||||
public class ChatLogic implements CoreContracts.IChatLogicContract {
|
||||
private static Gson gson = new Gson();
|
||||
private String chatID;
|
||||
private String newMessage = "";
|
||||
|
@ -22,6 +22,8 @@ import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.relay.tcp.TCPRelayClientConfig;
|
||||
import net.tomp2p.replication.AutoReplication;
|
||||
import net.tomp2p.replication.IndirectReplication;
|
||||
import net.tomp2p.replication.Replication;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -34,7 +36,7 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.IMainLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.JVMShutdownHook;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
@ -44,8 +46,8 @@ import io.github.chronosx88.influence.helpers.StorageBerkeleyDB;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.models.PublicUserProfile;
|
||||
|
||||
public class MainLogic implements IMainLogicContract {
|
||||
private static final String LOG_TAG = "MainLogic";
|
||||
public class MainLogic implements CoreContracts.IMainLogicContract {
|
||||
private static final String LOG_TAG = MainLogic.class.getName();
|
||||
|
||||
private SharedPreferences preferences;
|
||||
private Number160 peerID;
|
||||
@ -54,7 +56,7 @@ public class MainLogic implements IMainLogicContract {
|
||||
private InetAddress bootstrapAddress = null;
|
||||
private PeerAddress bootstrapPeerAddress = null;
|
||||
private Gson gson;
|
||||
private AutoReplication replication;
|
||||
private IndirectReplication replication;
|
||||
private KeyPairManager keyPairManager;
|
||||
private Thread checkNewChatsThread = null;
|
||||
private StorageBerkeleyDB storage;
|
||||
@ -142,11 +144,13 @@ public class MainLogic implements IMainLogicContract {
|
||||
jsonObject.addProperty("action", UIActions.BOOTSTRAP_SUCCESS);
|
||||
AppHelper.getObservable().notifyUIObservers(jsonObject);
|
||||
AppHelper.storePeerID(preferences.getString("peerID", null));
|
||||
AppHelper.updateUsername(preferences.getString("username", null));
|
||||
AppHelper.storePeerDHT(peerDHT);
|
||||
AppHelper.initNetworkHandler();
|
||||
setReceiveHandler();
|
||||
gson = new Gson();
|
||||
publicProfileToDHT();
|
||||
SettingsLogic.Companion.publishUsername(AppHelper.getUsername(), AppHelper.getUsername());
|
||||
NetworkHandler.handlePendingChatRequests();
|
||||
TimerTask timerTask = new TimerTask() {
|
||||
@Override
|
||||
@ -163,7 +167,7 @@ public class MainLogic implements IMainLogicContract {
|
||||
};
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(timerTask, 1, 5000);
|
||||
replication = new AutoReplication(peerDHT.peer()).start();
|
||||
replication = new IndirectReplication(peerDHT).start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -224,7 +228,7 @@ public class MainLogic implements IMainLogicContract {
|
||||
public void shutdownPeer() {
|
||||
new Thread(() -> {
|
||||
if(replication != null) {
|
||||
replication.shutdown().start();
|
||||
replication.shutdown();
|
||||
}
|
||||
peerDHT.peer().announceShutdown().start().awaitUninterruptibly();
|
||||
peerDHT.peer().shutdown().awaitUninterruptibly();
|
||||
@ -245,7 +249,7 @@ public class MainLogic implements IMainLogicContract {
|
||||
|
||||
private void publicProfileToDHT() {
|
||||
KeyPair mainKeyPair = keyPairManager.openMainKeyPair();
|
||||
PublicUserProfile userProfile = new PublicUserProfile(AppHelper.getPeerID(), peerDHT.peerAddress());
|
||||
PublicUserProfile userProfile = new PublicUserProfile(AppHelper.getUsername(), peerDHT.peerAddress());
|
||||
Data serializedUserProfile = null;
|
||||
try {
|
||||
serializedUserProfile = new Data(gson.toJson(userProfile))
|
||||
|
@ -0,0 +1,46 @@
|
||||
package io.github.chronosx88.influence.logic
|
||||
|
||||
import android.util.Log
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts
|
||||
import io.github.chronosx88.influence.helpers.AppHelper
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager
|
||||
import io.github.chronosx88.influence.helpers.P2PUtils
|
||||
import net.tomp2p.peers.Number640
|
||||
import net.tomp2p.storage.Data
|
||||
import java.io.IOException
|
||||
|
||||
class SettingsLogic : CoreContracts.ISettingsLogic {
|
||||
|
||||
override fun checkUsernameExists(username: String) : Boolean {
|
||||
val usernameMap: MutableMap<Number640, Data>? = P2PUtils.get(username)
|
||||
usernameMap ?: return false
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG: String = "SettingsLogic"
|
||||
private val keyPairManager = KeyPairManager()
|
||||
|
||||
fun publishUsername(oldUsername: String?, username: String?) {
|
||||
val mainKeyPair = keyPairManager.openMainKeyPair()
|
||||
if(oldUsername != null || !oldUsername.equals("")) {
|
||||
P2PUtils.remove(oldUsername, null, mainKeyPair)
|
||||
}
|
||||
|
||||
if (username.equals("") || username == null) {
|
||||
return
|
||||
}
|
||||
var data: Data? = null
|
||||
try {
|
||||
data = Data(AppHelper.getPeerID())
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
data!!.protectEntry(mainKeyPair)
|
||||
|
||||
val isSuccess = P2PUtils.put(username, null, data, mainKeyPair)
|
||||
Log.i(LOG_TAG, if (isSuccess) "Username $username is published!" else "Username $username isn't published!")
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.startchat.IStartChatLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
@ -24,7 +24,7 @@ import io.github.chronosx88.influence.models.ChatMetadata;
|
||||
import io.github.chronosx88.influence.models.NewChatRequestMessage;
|
||||
import io.github.chronosx88.influence.models.PublicUserProfile;
|
||||
|
||||
public class StartChatLogic implements IStartChatLogicContract {
|
||||
public class StartChatLogic implements CoreContracts.IStartChatLogicContract {
|
||||
private PeerDHT peerDHT;
|
||||
private Gson gson;
|
||||
private KeyPairManager keyPairManager;
|
||||
@ -37,20 +37,25 @@ public class StartChatLogic implements IStartChatLogicContract {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStartChatMessage(String peerID) {
|
||||
public void sendStartChatMessage(String username) {
|
||||
if(peerDHT == null) {
|
||||
ObservableUtils.notifyUI(UIActions.NODE_IS_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
String peerID = getPeerIDByUsername(username);
|
||||
if(peerID == null) {
|
||||
ObservableUtils.notifyUI(UIActions.PEER_NOT_EXIST);
|
||||
return;
|
||||
}
|
||||
PublicUserProfile recipientPublicProfile = getPublicProfile(peerID);
|
||||
if(recipientPublicProfile == null) {
|
||||
ObservableUtils.notifyUI(UIActions.PEER_NOT_EXIST);
|
||||
return;
|
||||
}
|
||||
|
||||
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(UUID.randomUUID().toString(), UUID.randomUUID().toString(), AppHelper.getPeerID(), AppHelper.getPeerID(), System.currentTimeMillis(), 0);
|
||||
NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(UUID.randomUUID().toString(), UUID.randomUUID().toString(), AppHelper.getPeerID(), AppHelper.getUsername(), System.currentTimeMillis(), 0);
|
||||
try {
|
||||
if(P2PUtils.put(peerID + "_pendingChats", newChatRequestMessage.getChatID(), new Data(gson.toJson(newChatRequestMessage)))) {
|
||||
Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + newChatRequestMessage.getChatID());
|
||||
@ -65,13 +70,13 @@ public class StartChatLogic implements IStartChatLogicContract {
|
||||
admins.add(AppHelper.getPeerID());
|
||||
Data data = null;
|
||||
try {
|
||||
data = new Data(gson.toJson(new ChatMetadata(peerID, admins, new ArrayList<>())));
|
||||
data = new Data(gson.toJson(new ChatMetadata(username, admins, new ArrayList<>())));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
data.protectEntry(keyPairManager.openMainKeyPair());
|
||||
P2PUtils.put(newChatRequestMessage.getChatID() + "_metadata", null, data);
|
||||
LocalDBWrapper.createChatEntry(newChatRequestMessage.getChatID(), peerID, newChatRequestMessage.getChatID() + "_metadata", newChatRequestMessage.getChatID() + "_members", 0);
|
||||
LocalDBWrapper.createChatEntry(newChatRequestMessage.getChatID(), username, newChatRequestMessage.getChatID() + "_metadata", newChatRequestMessage.getChatID() + "_members", 0);
|
||||
ObservableUtils.notifyUI(UIActions.NEW_CHAT);
|
||||
}).start();
|
||||
}
|
||||
@ -89,4 +94,17 @@ public class StartChatLogic implements IStartChatLogicContract {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getPeerIDByUsername(String username) {
|
||||
Map<Number640, Data> usernameMap = P2PUtils.get(username);
|
||||
if(usernameMap == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (String) usernameMap.values().iterator().next().object();
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,7 @@ package io.github.chronosx88.influence.presenters;
|
||||
import android.content.Intent;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListViewContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.ChatListAdapter;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
@ -13,12 +11,12 @@ 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 IChatListPresenterContract {
|
||||
private IChatListViewContract view;
|
||||
private IChatListLogicContract logic;
|
||||
public class ChatListPresenter implements CoreContracts.IChatListPresenterContract {
|
||||
private CoreContracts.IChatListViewContract view;
|
||||
private CoreContracts.IChatListLogicContract logic;
|
||||
private ChatListAdapter chatListAdapter;
|
||||
|
||||
public ChatListPresenter(IChatListViewContract view) {
|
||||
public ChatListPresenter(CoreContracts.IChatListViewContract view) {
|
||||
this.view = view;
|
||||
chatListAdapter = new ChatListAdapter((v, p)-> {
|
||||
openChat(chatListAdapter.getChatEntity(p).chatID);
|
||||
|
@ -10,9 +10,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.IChatLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.IChatPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.chatactivity.IChatViewContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||
@ -22,14 +20,14 @@ import io.github.chronosx88.influence.logic.ChatLogic;
|
||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity;
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity;
|
||||
|
||||
public class ChatPresenter implements IChatPresenterContract, IObserver {
|
||||
private IChatLogicContract logic;
|
||||
private IChatViewContract view;
|
||||
public class ChatPresenter implements CoreContracts.IChatPresenterContract, IObserver {
|
||||
private CoreContracts.IChatLogicContract logic;
|
||||
private CoreContracts.IChatViewContract view;
|
||||
private ChatEntity chatEntity;
|
||||
private String chatID;
|
||||
private Gson gson;
|
||||
|
||||
public ChatPresenter(IChatViewContract view, String chatID) {
|
||||
public ChatPresenter(CoreContracts.IChatViewContract view, String chatID) {
|
||||
this.logic = new ChatLogic(LocalDBWrapper.getChatByChatID(chatID));
|
||||
this.view = view;
|
||||
this.chatID = chatID;
|
||||
|
@ -1,15 +1,13 @@
|
||||
package io.github.chronosx88.influence.presenters;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.IMainLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.IMainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.IMainViewContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.logic.MainLogic;
|
||||
|
||||
public class MainPresenter implements IMainPresenterContract {
|
||||
private IMainLogicContract logic;
|
||||
private IMainViewContract view;
|
||||
public class MainPresenter implements CoreContracts.IMainPresenterContract {
|
||||
private CoreContracts.IMainLogicContract logic;
|
||||
private CoreContracts.IMainViewContract view;
|
||||
|
||||
public MainPresenter(IMainViewContract view) {
|
||||
public MainPresenter(CoreContracts.IMainViewContract view) {
|
||||
this.view = view;
|
||||
logic = new MainLogic();
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package io.github.chronosx88.influence.presenters
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
import com.google.gson.JsonObject
|
||||
import io.github.chronosx88.influence.R
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver
|
||||
import io.github.chronosx88.influence.helpers.AppHelper
|
||||
import io.github.chronosx88.influence.helpers.ObservableUtils
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions
|
||||
import io.github.chronosx88.influence.logic.SettingsLogic
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsPresenter(private val view: CoreContracts.ISettingsView) : CoreContracts.ISettingsPresenter, IObserver {
|
||||
private val mainThreadHandler: Handler = Handler(AppHelper.getContext().mainLooper)
|
||||
private val logic: SettingsLogic = SettingsLogic()
|
||||
|
||||
init {
|
||||
AppHelper.getObservable().register(this)
|
||||
}
|
||||
|
||||
override fun updateUsername(username: String) {
|
||||
view.loadingScreen(true)
|
||||
GlobalScope.launch {
|
||||
if(!logic.checkUsernameExists(username)) {
|
||||
// Save username in SharedPreferences
|
||||
val editor: SharedPreferences.Editor = AppHelper.getPreferences().edit()
|
||||
val oldUsername = AppHelper.getPreferences().getString("username", null)
|
||||
if(username.equals("")) {
|
||||
editor.remove("username")
|
||||
} else {
|
||||
editor.putString("username", username)
|
||||
}
|
||||
editor.apply()
|
||||
AppHelper.updateUsername(if (username.equals("")) null else username)
|
||||
|
||||
// Publish username on DHT network
|
||||
SettingsLogic.publishUsername(oldUsername, username)
|
||||
|
||||
ObservableUtils.notifyUI(UIActions.USERNAME_AVAILABLE)
|
||||
} else {
|
||||
ObservableUtils.notifyUI(UIActions.USERNAME_ISNT_AVAILABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleEvent(json: JsonObject) {
|
||||
val post = {
|
||||
when (json.get("action").asInt) {
|
||||
UIActions.USERNAME_AVAILABLE -> {
|
||||
view.loadingScreen(false)
|
||||
view.showMessage(AppHelper.getContext().getString(R.string.username_saved))
|
||||
}
|
||||
UIActions.USERNAME_ISNT_AVAILABLE -> {
|
||||
view.loadingScreen(false)
|
||||
view.showMessage(AppHelper.getContext().getString(R.string.username_isnt_saved))
|
||||
}
|
||||
}
|
||||
}
|
||||
mainThreadHandler.post(post)
|
||||
}
|
||||
}
|
@ -2,19 +2,17 @@ package io.github.chronosx88.influence.presenters;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver;
|
||||
import io.github.chronosx88.influence.contracts.startchat.IStartChatLogicContract;
|
||||
import io.github.chronosx88.influence.contracts.startchat.IStartChatPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.startchat.IStartChatViewContract;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
import io.github.chronosx88.influence.logic.StartChatLogic;
|
||||
|
||||
public class StartChatPresenter implements IStartChatPresenterContract, IObserver {
|
||||
private IStartChatViewContract view;
|
||||
private IStartChatLogicContract logic;
|
||||
public class StartChatPresenter implements CoreContracts.IStartChatPresenterContract, IObserver {
|
||||
private CoreContracts.IStartChatViewContract view;
|
||||
private CoreContracts.IStartChatLogicContract logic;
|
||||
|
||||
public StartChatPresenter(IStartChatViewContract view) {
|
||||
public StartChatPresenter(CoreContracts.IStartChatViewContract view) {
|
||||
this.view = view;
|
||||
this.logic = new StartChatLogic();
|
||||
AppHelper.getObservable().register(this);
|
||||
|
@ -1,95 +0,0 @@
|
||||
package io.github.chronosx88.influence.views;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
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.IChatViewContract;
|
||||
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 IChatViewContract {
|
||||
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);
|
||||
getSupportActionBar().setTitle("");
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
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) -> {
|
||||
if(messageTextEdit.getText().toString().equals("")) {
|
||||
return;
|
||||
}
|
||||
presenter.sendMessage(messageTextEdit.getText().toString());
|
||||
messageTextEdit.setText("");
|
||||
messageList.scrollToPosition(chatAdapter.getItemCount()-1);
|
||||
});
|
||||
contactUsernameTextView.setText(intent.getStringExtra("contactUsername"));
|
||||
messageList.scrollToPosition(chatAdapter.getItemCount()-1);
|
||||
}
|
||||
|
||||
@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();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
presenter.onDestroy();
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package io.github.chronosx88.influence.views
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
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.CoreContracts
|
||||
import io.github.chronosx88.influence.helpers.ChatAdapter
|
||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||
import io.github.chronosx88.influence.presenters.ChatPresenter
|
||||
|
||||
class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract {
|
||||
private var chatAdapter: ChatAdapter? = null
|
||||
private var messageList: RecyclerView? = null
|
||||
private var sendMessageButton: ImageButton? = null
|
||||
private var messageTextEdit: EditText? = null
|
||||
private var contactUsernameTextView: TextView? = null
|
||||
private var presenter: ChatPresenter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_chat)
|
||||
|
||||
val intent = intent
|
||||
|
||||
presenter = ChatPresenter(this, intent.getStringExtra("chatID"))
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar_chat_activity)
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar!!.setTitle("")
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar!!.setHomeButtonEnabled(true)
|
||||
messageList = findViewById(R.id.message_list)
|
||||
chatAdapter = ChatAdapter()
|
||||
presenter!!.updateAdapter()
|
||||
messageList!!.adapter = chatAdapter
|
||||
messageList!!.layoutManager = LinearLayoutManager(this)
|
||||
contactUsernameTextView = findViewById(R.id.appbar_username)
|
||||
messageTextEdit = findViewById(R.id.message_input)
|
||||
sendMessageButton = findViewById(R.id.send_button)
|
||||
sendMessageButton!!.setOnClickListener sendMessageButton@{
|
||||
if (messageTextEdit!!.text.toString() == "") {
|
||||
return@sendMessageButton
|
||||
}
|
||||
presenter!!.sendMessage(messageTextEdit!!.text.toString())
|
||||
messageTextEdit!!.setText("")
|
||||
messageList!!.scrollToPosition(chatAdapter!!.itemCount - 1)
|
||||
}
|
||||
contactUsernameTextView!!.text = intent.getStringExtra("contactUsername")
|
||||
messageList!!.scrollToPosition(chatAdapter!!.itemCount - 1)
|
||||
}
|
||||
|
||||
override fun updateMessageList(message: MessageEntity) {
|
||||
runOnUiThread {
|
||||
chatAdapter!!.addMessage(message)
|
||||
chatAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateMessageList(messages: List<MessageEntity>) {
|
||||
runOnUiThread {
|
||||
chatAdapter!!.addMessages(messages)
|
||||
chatAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter!!.onDestroy()
|
||||
}
|
||||
}
|
@ -13,8 +13,7 @@ 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.mainactivity.IMainPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.mainactivity.IMainViewContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.helpers.actions.UIActions;
|
||||
@ -23,9 +22,9 @@ import io.github.chronosx88.influence.views.fragments.ChatListFragment;
|
||||
import io.github.chronosx88.influence.views.fragments.SettingsFragment;
|
||||
import io.github.chronosx88.influence.views.fragments.StartChatFragment;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements IObserver, IMainViewContract {
|
||||
public class MainActivity extends AppCompatActivity implements IObserver, CoreContracts.IMainViewContract {
|
||||
|
||||
private IMainPresenterContract presenter;
|
||||
private CoreContracts.IMainPresenterContract presenter;
|
||||
private ProgressDialog progressDialog;
|
||||
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
|
||||
= new BottomNavigationView.OnNavigationItemSelectedListener() {
|
||||
|
@ -1,92 +0,0 @@
|
||||
package io.github.chronosx88.influence.views.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListPresenterContract;
|
||||
import io.github.chronosx88.influence.contracts.chatlist.IChatListViewContract;
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver;
|
||||
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.presenters.ChatListPresenter;
|
||||
|
||||
public class ChatListFragment extends Fragment implements IChatListViewContract, IObserver {
|
||||
private IChatListPresenterContract presenter;
|
||||
private RecyclerView chatList;
|
||||
private Handler mainThreadHandler;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
AppHelper.getObservable().register(this);
|
||||
this.mainThreadHandler = new Handler(getContext().getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.chatlist_fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
chatList = view.findViewById(R.id.chatlist_container);
|
||||
chatList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
presenter = new ChatListPresenter(this);
|
||||
presenter.updateChatList();
|
||||
registerForContextMenu(chatList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecycleAdapter(ChatListAdapter adapter) {
|
||||
chatList.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
presenter.updateChatList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(JsonObject object) {
|
||||
switch (object.get("action").getAsInt()) {
|
||||
case UIActions.SUCCESSFUL_CREATE_CHAT:
|
||||
case UIActions.NEW_CHAT: {
|
||||
presenter.updateChatList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateChatList(ChatListAdapter adapter, List<ChatEntity> chats) {
|
||||
mainThreadHandler.post(() -> {
|
||||
adapter.setChatList(chats);
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||
presenter.onContextItemSelected(item);
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package io.github.chronosx88.influence.views.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.chronosx88.influence.R
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts
|
||||
import io.github.chronosx88.influence.contracts.observer.IObserver
|
||||
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.presenters.ChatListPresenter
|
||||
|
||||
class ChatListFragment : Fragment(), CoreContracts.IChatListViewContract, IObserver {
|
||||
private var presenter: CoreContracts.IChatListPresenterContract? = null
|
||||
private var chatList: RecyclerView? = null
|
||||
private var mainThreadHandler: Handler? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
AppHelper.getObservable().register(this)
|
||||
this.mainThreadHandler = Handler(context!!.mainLooper)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.chatlist_fragment, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
chatList = view.findViewById(R.id.chatlist_container)
|
||||
chatList!!.layoutManager = LinearLayoutManager(context)
|
||||
presenter = ChatListPresenter(this)
|
||||
presenter!!.updateChatList()
|
||||
registerForContextMenu(chatList!!)
|
||||
}
|
||||
|
||||
override fun setRecycleAdapter(adapter: ChatListAdapter) {
|
||||
chatList!!.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
presenter!!.updateChatList()
|
||||
}
|
||||
|
||||
override fun handleEvent(`object`: JsonObject) {
|
||||
when (`object`.get("action").asInt) {
|
||||
UIActions.SUCCESSFUL_CREATE_CHAT, UIActions.NEW_CHAT -> {
|
||||
presenter!!.updateChatList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateChatList(adapter: ChatListAdapter, chats: List<ChatEntity>) {
|
||||
mainThreadHandler!!.post {
|
||||
adapter.setChatList(chats)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
presenter!!.onContextItemSelected(item)
|
||||
return super.onContextItemSelected(item)
|
||||
}
|
||||
}
|
@ -1,19 +1,33 @@
|
||||
package io.github.chronosx88.influence.views.fragments;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||
import io.github.chronosx88.influence.presenters.SettingsPresenter;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
public class SettingsFragment extends PreferenceFragmentCompat implements CoreContracts.ISettingsView {
|
||||
private ProgressDialog progressDialog;
|
||||
private SettingsPresenter presenter;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s) {
|
||||
progressDialog = new ProgressDialog(getContext(), R.style.AlertDialogTheme);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
||||
presenter = new SettingsPresenter(this);
|
||||
// Load the Preferences from the XML file
|
||||
addPreferencesFromResource(R.xml.main_settings);
|
||||
getPreferenceScreen().getPreference(0).setSummary(AppHelper.getPeerID());
|
||||
@ -24,5 +38,42 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
Toast.makeText(AppHelper.getContext(), "Скопировано в буфер обмена!", Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}));
|
||||
getPreferenceScreen().getPreference(1).setSummary(AppHelper.getUsername());
|
||||
getPreferenceScreen().getPreference(1).setOnPreferenceClickListener((v) -> {
|
||||
setupUsernameEditDialog().show();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadingScreen(boolean state) {
|
||||
if(state)
|
||||
progressDialog.show();
|
||||
else
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(@NotNull String message) {
|
||||
Toast.makeText(AppHelper.getContext(), message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private AlertDialog.Builder setupUsernameEditDialog() {
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
|
||||
alertDialog.setTitle(getContext().getString(R.string.username_settings));
|
||||
|
||||
final EditText input = new EditText(getContext());
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT);
|
||||
input.setSingleLine();
|
||||
input.setLayoutParams(lp);
|
||||
alertDialog.setView(input);
|
||||
|
||||
alertDialog.setPositiveButton(getContext().getString(R.string.ok), (dialog, which) -> presenter.updateUsername(input.getText().toString()));
|
||||
|
||||
alertDialog.setNegativeButton(getContext().getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
|
||||
return alertDialog;
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ import androidx.annotation.Nullable;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import io.github.chronosx88.influence.R;
|
||||
import io.github.chronosx88.influence.contracts.startchat.IStartChatViewContract;
|
||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||
import io.github.chronosx88.influence.presenters.StartChatPresenter;
|
||||
|
||||
public class StartChatFragment extends Fragment implements IStartChatViewContract {
|
||||
public class StartChatFragment extends Fragment implements CoreContracts.IStartChatViewContract {
|
||||
private TextInputLayout textInputPeerID;
|
||||
private ProgressDialog progressDialog;
|
||||
private Button createChatButton;
|
||||
|
@ -20,7 +20,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:hint="Peer ID"/>
|
||||
android:hint="@string/username_hint"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
|
10
app/src/main/res/values-ru-rRU/strings.xml
Normal file
10
app/src/main/res/values-ru-rRU/strings.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Influence</string>
|
||||
<string name="username_saved">Ваше имя пользователя успешно сохранено!</string>
|
||||
<string name="username_isnt_saved">К сожалению, это имя пользователя занято.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Отмена</string>
|
||||
<string name="username_settings">Ваше имя пользователя</string>
|
||||
<string name="username_hint">Имя пользователя</string>
|
||||
</resources>
|
@ -1,3 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name">Influence</string>
|
||||
<string name="username_saved">Your username saved successfully!</string>
|
||||
<string name="username_isnt_saved">Sorry, this username is busy.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="username_settings">Your username</string>
|
||||
<string name="username_hint">Username</string>
|
||||
</resources>
|
||||
|
@ -4,6 +4,10 @@
|
||||
android:key="peerID"
|
||||
android:title="Мой Peer ID"
|
||||
android:summary=""/>
|
||||
<Preference
|
||||
android:key="username"
|
||||
android:title="Мой username"
|
||||
android:summary=""/>
|
||||
<EditTextPreference
|
||||
android:key="bootstrapAddress"
|
||||
android:title="Адрес Bootstrap-ноды"
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.21'
|
||||
ext.kotlin_version = '1.3.30'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
Loading…
Reference in New Issue
Block a user