[BIG COMMIT] Made a username system and slightly fixed the signature system. Refactor.

This commit is contained in:
ChronosX88 2019-04-16 14:52:41 +04:00
parent 1eb1f0c0bc
commit 99409e1a13
44 changed files with 565 additions and 381 deletions

View File

@ -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()

View File

@ -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>

View File

@ -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)
}
}

View File

@ -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();
}

View File

@ -1,7 +0,0 @@
package io.github.chronosx88.influence.contracts.chatactivity;
public interface IChatPresenterContract {
void sendMessage(String text);
void updateAdapter();
void onDestroy();
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -1,6 +0,0 @@
package io.github.chronosx88.influence.contracts.mainactivity;
public interface IMainLogicContract {
void initPeer();
void shutdownPeer();
}

View File

@ -1,6 +0,0 @@
package io.github.chronosx88.influence.contracts.mainactivity;
public interface IMainPresenterContract {
void initPeer();
void onDestroy();
}

View File

@ -1,5 +0,0 @@
package io.github.chronosx88.influence.contracts.mainactivity;
public interface IMainViewContract {
//
}

View File

@ -1,5 +0,0 @@
package io.github.chronosx88.influence.contracts.startchat;
public interface IStartChatLogicContract {
void sendStartChatMessage(String peerID);
}

View File

@ -1,5 +0,0 @@
package io.github.chronosx88.influence.contracts.startchat;
public interface IStartChatPresenterContract {
void startChatWithPeer(String peerID);
}

View File

@ -1,6 +0,0 @@
package io.github.chronosx88.influence.contracts.startchat;
public interface IStartChatViewContract {
void showMessage(String message);
void showProgressDialog(boolean enabled);
}

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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();

View File

@ -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 {
}
}
}
}

View File

@ -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();
}
}

View File

@ -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? {

View File

@ -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;
}

View File

@ -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() {

View File

@ -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 = "";

View File

@ -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))

View File

@ -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!")
}
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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()
}
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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

View 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>

View File

@ -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>

View File

@ -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-ноды"

View File

@ -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()