diff --git a/app/build.gradle b/app/build.gradle index 5a1bc9f..c63c3e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 53e778d..ed46495 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,14 +3,16 @@ xmlns:tools="http://schemas.android.com/tools" package="io.github.chronosx88.influence"> + + + android:theme="@style/AppTheme"> @@ -20,7 +22,7 @@ + android:theme="@style/NoWindowActionBar" /> - + \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt b/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt new file mode 100644 index 0000000..998ce6f --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt @@ -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 + } + + 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) + } + + // -----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) + } + + // -----SettingsFragment----- + + interface ISettingsLogic { + fun checkUsernameExists(username: String) : Boolean + } + + interface ISettingsPresenter { + fun updateUsername(username: String) + } + + interface ISettingsView { + fun loadingScreen(state: Boolean) + fun showMessage(message: String) + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatLogicContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatLogicContract.java deleted file mode 100644 index e5878fb..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatLogicContract.java +++ /dev/null @@ -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(); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatPresenterContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatPresenterContract.java deleted file mode 100644 index 3e77721..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatPresenterContract.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.chronosx88.influence.contracts.chatactivity; - -public interface IChatPresenterContract { - void sendMessage(String text); - void updateAdapter(); - void onDestroy(); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatViewContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatViewContract.java deleted file mode 100644 index 871de0a..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatactivity/IChatViewContract.java +++ /dev/null @@ -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 messages); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListLogicContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListLogicContract.java deleted file mode 100644 index eff7c8b..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListLogicContract.java +++ /dev/null @@ -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 loadAllChats(); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListPresenterContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListPresenterContract.java deleted file mode 100644 index 125a767..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListPresenterContract.java +++ /dev/null @@ -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); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListViewContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListViewContract.java deleted file mode 100644 index 4a7a544..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/chatlist/IChatListViewContract.java +++ /dev/null @@ -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 chats); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainLogicContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainLogicContract.java deleted file mode 100644 index 65ad0c9..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainLogicContract.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.chronosx88.influence.contracts.mainactivity; - -public interface IMainLogicContract { - void initPeer(); - void shutdownPeer(); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainPresenterContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainPresenterContract.java deleted file mode 100644 index 69bd549..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainPresenterContract.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.chronosx88.influence.contracts.mainactivity; - -public interface IMainPresenterContract { - void initPeer(); - void onDestroy(); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainViewContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainViewContract.java deleted file mode 100644 index ee49885..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/mainactivity/IMainViewContract.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.chronosx88.influence.contracts.mainactivity; - -public interface IMainViewContract { - // -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatLogicContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatLogicContract.java deleted file mode 100644 index 59763b3..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatLogicContract.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.chronosx88.influence.contracts.startchat; - -public interface IStartChatLogicContract { - void sendStartChatMessage(String peerID); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatPresenterContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatPresenterContract.java deleted file mode 100644 index de736b8..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatPresenterContract.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.chronosx88.influence.contracts.startchat; - -public interface IStartChatPresenterContract { - void startChatWithPeer(String peerID); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatViewContract.java b/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatViewContract.java deleted file mode 100644 index cdf5bf3..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/startchat/IStartChatViewContract.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.chronosx88.influence.contracts.startchat; - -public interface IStartChatViewContract { - void showMessage(String message); - void showProgressDialog(boolean enabled); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/AppHelper.java b/app/src/main/java/io/github/chronosx88/influence/helpers/AppHelper.java index 311d511..99def7c 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/AppHelper.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/AppHelper.java @@ -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; + } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializerEx.kt b/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt similarity index 87% rename from app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializerEx.kt rename to app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt index f85eb11..be74217 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializerEx.kt +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt @@ -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 } diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/KeyPairManager.java b/app/src/main/java/io/github/chronosx88/influence/helpers/KeyPairManager.java index 258d4f9..2b7dc80 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/KeyPairManager.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/KeyPairManager.java @@ -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(); diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java b/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java index 4cf0f54..b6d5224 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java @@ -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 { } } } - - } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/P2PUtils.java b/app/src/main/java/io/github/chronosx88/influence/helpers/P2PUtils.java index 9d4ef52..6ae57fe 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/P2PUtils.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/P2PUtils.java @@ -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 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(); + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/StorageBerkeleyDB.kt b/app/src/main/java/io/github/chronosx88/influence/helpers/StorageBerkeleyDB.kt index 4e1b893..525dc62 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/StorageBerkeleyDB.kt +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/StorageBerkeleyDB.kt @@ -72,7 +72,7 @@ class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: Signat storageCheckIntervalMillis = 60 * 1000 - dataMap = StoredSortedMap(dataMapDB, Serializer(), DataSerializerEx(signatureFactory), true) + dataMap = StoredSortedMap(dataMapDB, Serializer(), DataSerializer(signatureFactory), true) timeoutMap = StoredSortedMap(timeoutMapDB, Serializer(), Serializer(), true) timeoutMapRev = StoredSortedMap(timeoutMapRevDB, Serializer(), Serializer>(), true) protectedDomainMap = StoredSortedMap(protectedDomainMapDB, Serializer(), Serializer(), 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 { - return responsibilityMapRev[peerID] as MutableSet + override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet? { + return responsibilityMapRev[peerID] as MutableSet? } override fun findPeerIDsForResponsibleContent(locationKey: Number160?): Number160? { diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/actions/UIActions.java b/app/src/main/java/io/github/chronosx88/influence/helpers/actions/UIActions.java index 208f7c7..152af68 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/actions/UIActions.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/actions/UIActions.java @@ -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; } diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java index 0f74550..12eda21 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java +++ b/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java @@ -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 loadAllChats() { diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java index adc2303..09aa5e9 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java +++ b/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java @@ -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 = ""; diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/MainLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/MainLogic.java index eea2263..d7f8b95 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/MainLogic.java +++ b/app/src/main/java/io/github/chronosx88/influence/logic/MainLogic.java @@ -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)) diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt b/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt new file mode 100644 index 0000000..a64a28d --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt @@ -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? = 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!") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/StartChatLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/StartChatLogic.java index 0474355..8511a07 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/StartChatLogic.java +++ b/app/src/main/java/io/github/chronosx88/influence/logic/StartChatLogic.java @@ -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 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; + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatListPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatListPresenter.java index 5f58903..438cd47 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatListPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatListPresenter.java @@ -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); diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.java index 4c049f8..3031dac 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.java @@ -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; diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.java index 9262726..82797d4 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.java @@ -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(); } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt b/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt new file mode 100644 index 0000000..c3eb653 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/StartChatPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/StartChatPresenter.java index 78c1b78..555ad7b 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/StartChatPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/StartChatPresenter.java @@ -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); diff --git a/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.java b/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.java deleted file mode 100644 index cb0cb7b..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.java +++ /dev/null @@ -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 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(); - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt b/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt new file mode 100644 index 0000000..1d0a067 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt @@ -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(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) { + 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() + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/views/MainActivity.java b/app/src/main/java/io/github/chronosx88/influence/views/MainActivity.java index 95ef21c..006dfa9 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/MainActivity.java +++ b/app/src/main/java/io/github/chronosx88/influence/views/MainActivity.java @@ -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() { diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.java b/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.java deleted file mode 100644 index 7407b8f..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.java +++ /dev/null @@ -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 chats) { - mainThreadHandler.post(() -> { - adapter.setChatList(chats); - adapter.notifyDataSetChanged(); - }); - } - - @Override - public boolean onContextItemSelected(@NonNull MenuItem item) { - presenter.onContextItemSelected(item); - return super.onContextItemSelected(item); - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.kt b/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.kt new file mode 100644 index 0000000..433db06 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/views/fragments/ChatListFragment.kt @@ -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) { + mainThreadHandler!!.post { + adapter.setChatList(chats) + adapter.notifyDataSetChanged() + } + } + + override fun onContextItemSelected(item: MenuItem): Boolean { + presenter!!.onContextItemSelected(item) + return super.onContextItemSelected(item) + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java b/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java index bda3782..3041fbc 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java +++ b/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java @@ -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; } } diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/StartChatFragment.java b/app/src/main/java/io/github/chronosx88/influence/views/fragments/StartChatFragment.java index b307167..a75463a 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/fragments/StartChatFragment.java +++ b/app/src/main/java/io/github/chronosx88/influence/views/fragments/StartChatFragment.java @@ -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; diff --git a/app/src/main/res/layout/start_chat_fragment.xml b/app/src/main/res/layout/start_chat_fragment.xml index 66e2815..326d09a 100644 --- a/app/src/main/res/layout/start_chat_fragment.xml +++ b/app/src/main/res/layout/start_chat_fragment.xml @@ -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"/>