diff --git a/app/build.gradle b/app/build.gradle index 9e4d154..f309182 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,6 +42,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.1.0-alpha02' implementation "androidx.room:room-runtime:2.1.0-alpha04" + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04" implementation 'org.slf4j:slf4j-log4j12:1.7.26' implementation('net.tomp2p:tomp2p-all:5.0-Beta8') { @@ -58,7 +59,12 @@ dependencies { implementation 'com.esotericsoftware:kryo:5.0.0-RC1' implementation 'com.github.instacart.truetime-android:library:3.4' + implementation 'org.igniterealtime.smack:smack-core:4.3.3' + implementation 'org.igniterealtime.smack:smack-tcp:4.3.3' + implementation 'org.igniterealtime.smack:smack-android:4.3.3' + implementation 'org.igniterealtime.smack:smack-extensions:4.3.3' + } repositories { mavenCentral() -} \ No newline at end of file +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ed46495..9f8ee82 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,13 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> + + + + diff --git a/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java b/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java new file mode 100644 index 0000000..e96f075 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 ChronosX88 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.chronosx88.influence; + +public class LoginCredentials { + String username = ""; + String password = ""; + String jabberHost = ""; +} diff --git a/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java b/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java new file mode 100644 index 0000000..6bad8f7 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2019 ChronosX88 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.chronosx88.influence; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.util.Log; + +import androidx.preference.PreferenceManager; + +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.ConnectionListener; +import org.jivesoftware.smack.ReconnectionManager; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.chat2.Chat; +import org.jivesoftware.smack.chat2.ChatManager; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.tcp.XMPPTCPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; + +import java.io.IOException; + +import io.github.chronosx88.influence.helpers.AppHelper; +import io.github.chronosx88.influence.helpers.NetworkHandler; + +public class XMPPConnection implements ConnectionListener { + private final static String LOG_TAG = "XMPPConnection"; + private LoginCredentials credentials = new LoginCredentials(); + private XMPPTCPConnection connection = null; + private SharedPreferences prefs; + private NetworkHandler networkHandler; + private BroadcastReceiver sendMessageReceiver = null; + private Context context; + + public enum ConnectionState { + CONNECTED, + AUTHENTICATED, + CONNECTING, + DISCONNECTING, + DISCONNECTED + } + + public enum SessionState { + LOGGED_IN, + LOGGED_OUT + } + + public XMPPConnection(Context context) { + String jid = prefs.getString("jid", null); + String password = prefs.getString("pass", null); + if(jid != null && password != null) { + String username = jid.split("@")[0]; + String jabberHost = jid.split("@")[1]; + credentials.username = username; + credentials.jabberHost = jabberHost; + credentials.password = password; + } + networkHandler = new NetworkHandler(context); + prefs = PreferenceManager.getDefaultSharedPreferences(context); + this.context = context; + } + + public void connect() throws XMPPException, SmackException, IOException { + if(connection == null) { + XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder() + .setXmppDomain(credentials.jabberHost) + .setHost(credentials.jabberHost) + .setResource(AppHelper.APP_NAME) + .setKeystoreType(null) + .setSecurityMode(ConnectionConfiguration.SecurityMode.required) + .setCompressionEnabled(true) + .build(); + + setupSendMessageReceiver(); + + connection = new XMPPTCPConnection(conf); + connection.addConnectionListener(this); + try { + connection.connect(); + connection.login(credentials.username, credentials.password); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + ChatManager.getInstanceFor(connection).addIncomingListener(networkHandler); + ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); + ReconnectionManager.setEnabledPerDefault(true); + reconnectionManager.enableAutomaticReconnection(); + } + } + + public void disconnect() { + prefs.edit().putBoolean("logged_in", false).apply(); + if(connection != null) { + connection.disconnect(); + connection = null; + } + } + + @Override + public void connected(org.jivesoftware.smack.XMPPConnection connection) { + XMPPConnectionService.connectionState = ConnectionState.CONNECTED; + } + + @Override + public void authenticated(org.jivesoftware.smack.XMPPConnection connection, boolean resumed) { + XMPPConnectionService.sessionState = SessionState.LOGGED_IN; + prefs.edit().putBoolean("logged_in", true).apply(); + } + + @Override + public void connectionClosed() { + XMPPConnectionService.connectionState = ConnectionState.DISCONNECTED; + XMPPConnectionService.sessionState = SessionState.LOGGED_OUT; + prefs.edit().putBoolean("logged_in", false).apply(); + } + + @Override + public void connectionClosedOnError(Exception e) { + XMPPConnectionService.connectionState = ConnectionState.DISCONNECTED; + XMPPConnectionService.sessionState = SessionState.LOGGED_OUT; + prefs.edit().putBoolean("logged_in", false).apply(); + Log.e(LOG_TAG, "Connection closed, exception occurred"); + e.printStackTrace(); + } + + private void setupSendMessageReceiver() { + sendMessageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + switch (action) { + case XMPPConnectionService.INTENT_SEND_MESSAGE: { + String recipientJid = intent.getStringExtra(XMPPConnectionService.MESSAGE_RECIPIENT); + String messageText = intent.getStringExtra(XMPPConnectionService.MESSAGE_BODY); + sendMessage(recipientJid, messageText); + break; + } + } + } + }; + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(XMPPConnectionService.INTENT_SEND_MESSAGE); + context.registerReceiver(sendMessageReceiver, intentFilter); + } + + private void sendMessage(String recipientJid, String messageText) { + EntityBareJid jid = null; + try { + jid = JidCreate.entityBareFrom(recipientJid); + } catch (XmppStringprepException e) { + e.printStackTrace(); + } + Chat chat = ChatManager.getInstanceFor(connection).chatWith(jid); + try { + Message message = new Message(jid, Message.Type.chat); + message.setBody(messageText); + chat.send(message); + } catch (SmackException.NotConnectedException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public XMPPTCPConnection getConnection() { + return connection; + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java b/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java new file mode 100644 index 0000000..5e9b00b --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 ChronosX88 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.chronosx88.influence; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; + +import java.io.IOException; + +public class XMPPConnectionService extends Service { + public static final String INTENT_NEW_MESSAGE = "io.github.chronosx88.intents.new_message"; + public static final String INTENT_SEND_MESSAGE = "io.github.chronosx88.intents.send_message"; + public static final String INTENT_AUTHENTICATED = "io.github.chronosx88.intents.authenticated"; + public static final String INTENT_AUTHENTICATION_FAILED = "io.github.chronosx88.intents.authentication_failed"; + + public static final String MESSAGE_CHATID = "chat_jid"; + public static final String MESSAGE_ID = "message_id"; + public static final String MESSAGE_BODY = "message_body"; + public static final String MESSAGE_RECIPIENT = "message_recipient"; + + public static XMPPConnection.ConnectionState connectionState = XMPPConnection.ConnectionState.DISCONNECTED; + public static XMPPConnection.SessionState sessionState = XMPPConnection.SessionState.LOGGED_OUT; + + private Thread thread; + private Handler threadHandler; + private boolean isThreadAlive = false; + private XMPPConnection connection; + private Context context; + + public XMPPConnectionService(Context context) { + this.context = context; + } + + @Override + public IBinder onBind(Intent intent) { return null; } + + public void onServiceStart() { + if(!isThreadAlive) + { + isThreadAlive = true; + if(thread == null || !thread.isAlive()) { + thread = new Thread(() -> { + Looper.prepare(); + threadHandler = new Handler(); + createConnection(); + Looper.loop(); + }); + thread.start(); + } + } + } + + private void onServiceStop() { + isThreadAlive = false; + threadHandler.post(() -> { + if(connection != null) { + connection.disconnect(); + } + }); + } + + private void createConnection() { + if(connection == null) { + connection = new XMPPConnection(this); + } + try { + connection.connect(); + } catch (IOException | SmackException | XMPPException e) { + Intent intent = new Intent(INTENT_AUTHENTICATION_FAILED); + + e.printStackTrace(); + //Stop the service all together. + stopSelf(); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + onServiceStart(); + return Service.START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + onServiceStop(); + } +} 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 index 99d3a76..cfef030 100644 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt +++ b/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt @@ -9,6 +9,10 @@ import io.github.chronosx88.influence.models.roomEntities.MessageEntity interface CoreContracts { + interface ViewWithLoadingScreen { + fun loadingScreen(state: Boolean); + } + // -----ChatList----- interface IChatListLogicContract { @@ -27,21 +31,6 @@ interface CoreContracts { 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 { @@ -94,4 +83,7 @@ interface CoreContracts { fun showMessage(message: String) fun refreshScreen() } + + // -----LoginActivity----- + interface ILoginViewContract : ViewWithLoadingScreen } 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 ab6797f..ceb7ea0 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 @@ -3,6 +3,7 @@ package io.github.chronosx88.influence.helpers; import android.app.Application; import android.content.Context; import android.content.SharedPreferences; +import android.preference.PreferenceManager; import net.tomp2p.dht.PeerDHT; @@ -22,11 +23,10 @@ import io.github.chronosx88.influence.observable.MainObservable; public class AppHelper extends MultiDexApplication { private static Application instance; private static MainObservable observable; - private static String peerID; - private static PeerDHT peerDHT; + public final static String APP_NAME = "Influence"; + + private static String jid; private static RoomHelper chatDB; - private static NetworkHandler networkHandler; - private static String username = ""; private static SharedPreferences preferences; @Override @@ -37,7 +37,7 @@ public class AppHelper extends MultiDexApplication { chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB") .allowMainThreadQueries() .build(); - preferences = getApplicationContext().getSharedPreferences("io.github.chronosx88.influence_preferences", MODE_PRIVATE); + preferences = PreferenceManager.getDefaultSharedPreferences(instance); new Thread(() -> { try { TrueTime.build().initialize(); @@ -47,28 +47,18 @@ public class AppHelper extends MultiDexApplication { }).start(); } - 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() { return instance.getApplicationContext(); } public static MainObservable getObservable() { return observable; } - public static String getPeerID() { return peerID; } + public static String getJid() { return jid; } - public static String getUsername() { return username; } - - public static PeerDHT getPeerDHT() { return peerDHT; } + public static void setJid(String jid1) { jid = jid1; } public static RoomHelper getChatDB() { return chatDB; } - public static void initNetworkHandler() { networkHandler = new NetworkHandler(); } - public static SharedPreferences getPreferences() { return preferences; } diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt b/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt deleted file mode 100644 index ef5b4b4..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/DataSerializer.kt +++ /dev/null @@ -1,98 +0,0 @@ -package io.github.chronosx88.influence.helpers - -import android.util.Log -import com.sleepycat.bind.EntryBinding -import com.sleepycat.je.DatabaseEntry -import io.netty.buffer.Unpooled -import net.tomp2p.connection.SignatureFactory -import net.tomp2p.storage.Data -import java.io.* -import java.nio.ByteBuffer -import java.security.InvalidKeyException -import java.security.SignatureException - - -class DataSerializer(private val signatureFactory: SignatureFactory) : EntryBinding, Serializable { - private val LOG_TAG = "DataSerializer" - - override fun entryToObject(databaseEntry: DatabaseEntry): Data? { - if (databaseEntry.data == null) { - return null - } - val dataInput = ByteArrayInputStream(databaseEntry.data) - var buf = Unpooled.buffer() - var data: Data? = null - while (data == null) { - buf.writeByte(dataInput.read()) - data = Data.decodeHeader(buf, signatureFactory) - } - val len = data.length() - var me = ByteArray(len) - try { - dataInput.read(me) - } catch (e: IOException) { - e.printStackTrace() - } - - buf = Unpooled.wrappedBuffer(me) - var retVal = data.decodeBuffer(buf) - if (!retVal) { - Log.e(LOG_TAG, "# ERROR: Data could not be deserialized!") - } - if (data.isSigned) { - me = ByteArray(signatureFactory.signatureSize()) - dataInput.read(me) - buf = Unpooled.wrappedBuffer(me) - } - retVal = data.decodeDone(buf, signatureFactory); - if(!retVal) { - throw IOException("Signature could not be read!") - } - return data - } - - override fun objectToEntry(data: Data, databaseEntry: DatabaseEntry) { - val out = ByteArrayOutputStream() - val acb = Unpooled.buffer() - // store data to disk - // header first - data.encodeHeader(acb, signatureFactory) - writeData(out, acb.nioBuffers()) - acb.skipBytes(acb.writerIndex()) - // next data - no need to copy to another buffer, just take the data - // from memory - writeData(out, data.toByteBuffers()) - // rest - try { - data.encodeDone(acb, signatureFactory) - writeData(out, acb.nioBuffers()) - } catch (e: InvalidKeyException) { - throw IOException(e) - } catch (e: SignatureException) { - throw IOException(e) - } - out.flush() - databaseEntry.data = out.toByteArray() - out.close() - } - - @Throws(IOException::class) - private fun writeData(out: OutputStream, nioBuffers: Array) { - val length = nioBuffers.size - for (i in 0 until length) { - val remaining = nioBuffers[i].remaining() - if (nioBuffers[i].hasArray()) { - out.write(nioBuffers[i].array(), nioBuffers[i].arrayOffset(), remaining) - } else { - val me = ByteArray(remaining) - nioBuffers[i].get(me) - out.write(me) - } - } - } - - companion object { - private const val serialVersionUID = 1428836065493792295L - } -} - diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/JVMShutdownHook.java b/app/src/main/java/io/github/chronosx88/influence/helpers/JVMShutdownHook.java deleted file mode 100644 index 4e5c7bd..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/JVMShutdownHook.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.chronosx88.influence.helpers; - -import android.util.Log; - -import net.tomp2p.dht.Storage; - -public class JVMShutdownHook extends Thread { - - Storage storage; - - public JVMShutdownHook(Storage storage) { - this.storage = storage; - } - - @Override - public void run() { - super.run(); - Log.d("JVMShutdownHook", "# Closing storage..."); - storage.close(); - Log.d("JVMShutdownHook", "# Storage is closed"); - } - -} diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/LocalDBWrapper.java b/app/src/main/java/io/github/chronosx88/influence/helpers/LocalDBWrapper.java index 8b5a035..1347b63 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/LocalDBWrapper.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/LocalDBWrapper.java @@ -12,39 +12,22 @@ public class LocalDBWrapper { private static final String LOG_TAG = "LocalDBWrapper"; private static RoomHelper dbInstance = AppHelper.getChatDB(); - /** - * Create a chat entry in the local database. - * @param chatID Chat ID - * @param name Chat name - * @param metadataRef Reference to general chat metadata (key in DHT) - * @param membersRef Reference to member list - */ - public static void createChatEntry(String chatID, String name, String metadataRef, String membersRef, int chunkID) { - dbInstance.chatDao().addChat(new ChatEntity(chatID, name, metadataRef, membersRef, new ArrayList<>(), chunkID)); + public static void createChatEntry(String jid, String chatName) { + dbInstance.chatDao().addChat(new ChatEntity(jid, chatName)); } - /** - * Creating a message entry in the local database - * @param type Message type - * @param chatID ID of the chat in which need to create a message - * @param username Sender username - * @param senderID Sender peer ID - * @param timestamp Message timestamp - * @param text Message text - * @return New message - */ - public static MessageEntity createMessageEntry(int type, String messageID, String chatID, String username, String senderID, long timestamp, String text, boolean isSent, boolean isRead) { - List chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(chatID); + public static long createMessageEntry(String jid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) { + List chatEntities = AppHelper.getChatDB().chatDao().getChatByChatID(jid); if(chatEntities.size() < 1) { - Log.e(LOG_TAG, "Failed to create message entry because chat " + chatID + " doesn't exists!"); - return null; + Log.e(LOG_TAG, "Failed to create message entry because chat " + jid + " doesn't exists!"); + return -1; } - MessageEntity message = new MessageEntity(type, messageID, chatID, senderID, username, timestamp, text, isSent, isRead); - dbInstance.messageDao().insertMessage(message); - return message; + MessageEntity message = new MessageEntity(jid, senderJid, timestamp, text, isSent, isRead); + long index = dbInstance.messageDao().insertMessage(message); + return index; } - public static MessageEntity getMessageByID(String messageID) { + public static MessageEntity getMessageByID(long messageID) { List messages = dbInstance.messageDao().getMessageByID(messageID); if(messages.isEmpty()) { return null; 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 b6d5224..9512a6f 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 @@ -1,78 +1,35 @@ package io.github.chronosx88.influence.helpers; -import com.google.gson.Gson; +import android.content.Context; +import android.content.Intent; -import net.tomp2p.dht.PeerDHT; -import net.tomp2p.peers.Number640; -import net.tomp2p.storage.Data; +import com.instacart.library.truetime.TrueTime; -import java.io.IOException; -import java.util.Map; -import java.util.UUID; +import org.jivesoftware.smack.chat2.Chat; +import org.jivesoftware.smack.chat2.IncomingChatMessageListener; +import org.jivesoftware.smack.packet.Message; +import org.jxmpp.jid.EntityBareJid; -import io.github.chronosx88.influence.contracts.observer.INetworkObserver; -import io.github.chronosx88.influence.helpers.actions.UIActions; -import io.github.chronosx88.influence.models.ChatMember; -import io.github.chronosx88.influence.models.JoinChatMessage; -import io.github.chronosx88.influence.models.NewChatRequestMessage; +import io.github.chronosx88.influence.XMPPConnectionService; -public class NetworkHandler implements INetworkObserver { +public class NetworkHandler implements IncomingChatMessageListener { private final static String LOG_TAG = "NetworkHandler"; - private static Gson gson = new Gson(); - private static PeerDHT peerDHT = AppHelper.getPeerDHT(); - private static KeyPairManager keyPairManager = new KeyPairManager(); + private Context context; - public NetworkHandler() { - AppHelper.getObservable().register(this); + public NetworkHandler(Context context) { + this.context = context; } @Override - public void handleEvent(Object object) { - // Empty - } - - - - public static void handlePendingChatRequests() { - Map pendingChats = P2PUtils.get(AppHelper.getPeerID() + "_pendingChats"); - if (pendingChats != null) { - for (Map.Entry entry : pendingChats.entrySet()) { - NewChatRequestMessage newChatRequestMessage = null; - try { - newChatRequestMessage = gson.fromJson((String) entry.getValue().object(), NewChatRequestMessage.class); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - ChatMember chatMember = new ChatMember(AppHelper.getPeerID(), AppHelper.getPeerID()); - Data putData = null; - try { - putData = new Data(gson.toJson(chatMember)).protectEntry(keyPairManager.openMainKeyPair()); - } catch (IOException e) { - e.printStackTrace(); - } - - P2PUtils.put(newChatRequestMessage.getChatID() + "_members", AppHelper.getPeerID(), putData); - - LocalDBWrapper.createChatEntry( - newChatRequestMessage.getChatID(), - newChatRequestMessage.getUsername(), - newChatRequestMessage.getChatID() + "_metadata", - newChatRequestMessage.getChatID() + "_members", - newChatRequestMessage.getChunkID() - ); - - 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.getUsername() == null ? AppHelper.getPeerID() : AppHelper.getUsername(), newChatRequestMessage.getChatID(), System.currentTimeMillis()))).protectEntry(keyPairManager.openMainKeyPair())); - } catch (IOException e) { - e.printStackTrace(); - } - ObservableUtils.notifyUI(UIActions.SUCCESSFUL_CREATE_CHAT); - } + public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { + if(LocalDBWrapper.getChatByChatID(from.asEntityBareJidString()) == null) { + LocalDBWrapper.createChatEntry(chat.getXmppAddressOfChatPartner().asUnescapedString(), chat.getXmppAddressOfChatPartner().asBareJid().asUnescapedString()); } + long messageID = LocalDBWrapper.createMessageEntry(chat.getXmppAddressOfChatPartner().asUnescapedString(), from.asUnescapedString(), TrueTime.now().getTime(), message.getBody(), true, false); + Intent intent = new Intent(XMPPConnectionService.INTENT_NEW_MESSAGE); + intent.setPackage(context.getPackageName()); + intent.putExtra(XMPPConnectionService.MESSAGE_CHATID, chat.getXmppAddressOfChatPartner().toString()); + intent.putExtra(XMPPConnectionService.MESSAGE_ID, messageID); + context.sendBroadcast(intent); } } \ 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 deleted file mode 100644 index 0d914e1..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/P2PUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.chronosx88.influence.helpers; - -import com.google.gson.Gson; - -import net.tomp2p.dht.FutureGet; -import net.tomp2p.dht.FuturePut; -import net.tomp2p.dht.FutureRemove; -import net.tomp2p.dht.PeerDHT; -import net.tomp2p.futures.FutureDirect; -import net.tomp2p.futures.FuturePing; -import net.tomp2p.peers.Number160; -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 put(String locationKey, String contentKey, Data data) { - FuturePut futurePut = peerDHT - .put(Number160.createHash(locationKey)) - .data(contentKey == null ? Number160.ZERO : Number160.createHash(contentKey), data) - .start() - .awaitUninterruptibly(); - 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)) - .all() - .start() - .awaitUninterruptibly(); - if(futureGet != null) { - if(!futureGet.isEmpty()) { - return futureGet.dataMap(); - } - } - return null; - } - - public static boolean remove(String locationKey, String contentKey) { - FutureRemove futureRemove = peerDHT - .remove(Number160.createHash(locationKey)) - .contentKey(contentKey == null ? null : Number160.createHash(contentKey)) - .start() - .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/Serializer.java b/app/src/main/java/io/github/chronosx88/influence/helpers/Serializer.java deleted file mode 100644 index f5a5aa2..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/Serializer.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.chronosx88.influence.helpers; - -import com.sleepycat.bind.EntryBinding; -import com.sleepycat.je.DatabaseEntry; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -public class Serializer implements EntryBinding { - public byte[] serialize(T object) { - ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); - try { - ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArray); - objectOutputStream.writeObject(object); - objectOutputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return byteArray.toByteArray(); - } - - public T deserialize(byte[] serializedObject) { - if(serializedObject == null) - return null; - ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedObject); - Object object = null; - try { - ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); - object = objectInputStream.readObject(); - } catch (ClassNotFoundException | IOException e) { - e.printStackTrace(); - } - return (T) object; - } - - @Override - public T entryToObject(DatabaseEntry databaseEntry) { - return deserialize(databaseEntry.getData()); - } - - @Override - public void objectToEntry(T object, DatabaseEntry databaseEntry) { - databaseEntry.setData(serialize(object)); - } -} 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 deleted file mode 100644 index fba5ea8..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/StorageBerkeleyDB.kt +++ /dev/null @@ -1,290 +0,0 @@ -package io.github.chronosx88.influence.helpers - -import com.sleepycat.collections.StoredSortedMap -import com.sleepycat.je.Database -import com.sleepycat.je.DatabaseConfig -import com.sleepycat.je.Environment -import com.sleepycat.je.EnvironmentConfig -import io.github.chronosx88.influence.helpers.comparators.CompareLong -import io.github.chronosx88.influence.helpers.comparators.CompareNumber640 -import net.tomp2p.connection.SignatureFactory -import net.tomp2p.dht.Storage -import net.tomp2p.peers.Number160 -import net.tomp2p.peers.Number320 -import net.tomp2p.peers.Number480 -import net.tomp2p.peers.Number640 -import net.tomp2p.storage.Data -import java.io.File -import java.security.PublicKey -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import kotlin.collections.ArrayList -import kotlin.collections.HashMap - -class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: SignatureFactory) : Storage { - // Core - private val dataMap: StoredSortedMap - // Maintenance - private val timeoutMap: StoredSortedMap - private val timeoutMapRev: StoredSortedMap> - // Protection - private val protectedDomainMap: StoredSortedMap - private val protectedEntryMap: StoredSortedMap - // Responsibility - private val responsibilityMap: StoredSortedMap - private val responsibilityMapRev: StoredSortedMap> - - private val dataMapDB: Database - private val timeoutMapDB: Database - private val timeoutMapRevDB: Database - private val protectedDomainMapDB: Database - private val protectedEntryMapDB: Database - private val responsibilityMapDB: Database - private val responsibilityMapRevDB: Database - - - private val storageCheckIntervalMillis: Int - private val dbEnvironment: Environment - - init { - val envConfig = EnvironmentConfig() - envConfig.allowCreate = true - dbEnvironment = Environment(path, envConfig) - - val configMap : HashMap = HashMap() - - val compareNumber640 = CompareNumber640() - val compareLong = CompareLong() - configMap["dataMapConfig"] = DatabaseConfig().setBtreeComparator(compareNumber640) - configMap["dataMapConfig"]!!.allowCreate = true - configMap["timeoutMapRevConfig"] = DatabaseConfig().setBtreeComparator(compareLong) - configMap["timeoutMapRevConfig"]!!.allowCreate = true - configMap["other"] = DatabaseConfig() - configMap["other"]!!.allowCreate = true - - dataMapDB = dbEnvironment.openDatabase(null, "dataMap_$peerId", configMap["dataMapConfig"]) - timeoutMapDB = dbEnvironment.openDatabase(null, "timeoutMap_$peerId", configMap["other"]) - timeoutMapRevDB = dbEnvironment.openDatabase(null, "timeoutMapRev_$peerId", configMap["timeoutMapRevConfig"]) - protectedDomainMapDB = dbEnvironment.openDatabase(null, "protectedDomainMap_$peerId", configMap["other"]) - protectedEntryMapDB = dbEnvironment.openDatabase(null, "protectedEntryMap_$peerId", configMap["other"]) - responsibilityMapDB = dbEnvironment.openDatabase(null, "responsibilityMap_$peerId", configMap["other"]) - responsibilityMapRevDB = dbEnvironment.openDatabase(null, "responsibilityMapRev_$peerId", configMap["other"]) - - storageCheckIntervalMillis = 60 * 1000 - - 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) - protectedEntryMap = StoredSortedMap(protectedEntryMapDB, Serializer(), Serializer(), true) - responsibilityMap = StoredSortedMap(responsibilityMapDB, Serializer(), Serializer(), true) - responsibilityMapRev = StoredSortedMap(responsibilityMapRevDB, Serializer(), Serializer>(), true) - } - - override fun contains(key: Number640?): Boolean { - return dataMap.containsKey(key) - } - - override fun contains(from: Number640?, to: Number640?): Int { - return dataMap.subMap(from, true, to, true).size - } - - override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet? { - return responsibilityMapRev[peerID] as MutableSet? - } - - override fun findPeerIDsForResponsibleContent(locationKey: Number160?): Number160? { - return responsibilityMap[locationKey] - } - - override fun put(key: Number640?, value: Data?): Data? { - val oldData = dataMap.put(key, value) - dbEnvironment.sync() - return oldData - } - - override fun get(key: Number640?): Data? { - return dataMap[key] - } - - override fun remove(key: Number640?, returnData: Boolean): Data? { - val oldData = dataMap.remove(key) - dbEnvironment.sync() - return oldData - } - - override fun remove(from: Number640?, to: Number640?): NavigableMap { - val tmp = dataMap.subMap(from, true, to, true) - val retVal = TreeMap() - for(entry : Map.Entry in tmp.entries) { - retVal[entry.key] = entry.value - } - tmp.clear() - dbEnvironment.sync() - return retVal - } - - override fun addTimeout(key: Number640, expiration: Long) { - val oldExpiration = timeoutMap.put(key, expiration) - putIfAbsent2(expiration, key) - if (oldExpiration == null) { - return - } - removeRevTimeout(key, oldExpiration) - dbEnvironment.sync() - } - - private fun putIfAbsent2(expiration: Long, key: Number640) { - var timeouts = timeoutMapRev[expiration] - //var timeouts : MutableSet = timeoutMapRev[expiration] as MutableSet - if (timeouts == null) { - timeouts = Collections.newSetFromMap(ConcurrentHashMap()) - } - (timeouts as MutableSet).add(key) - timeoutMapRev[expiration] = timeouts - dbEnvironment.sync() - } - - private fun removeRevTimeout(key: Number640, expiration: Long?) { - val tmp = timeoutMapRev[expiration] as MutableSet? - if (tmp != null) { - tmp.remove(key) - if (tmp.isEmpty()) { - timeoutMapRev.remove(expiration) - } else { - timeoutMapRev[expiration!!] = tmp - } - } - dbEnvironment.sync() - } - - override fun updateResponsibilities(locationKey: Number160, peerId: Number160?): Boolean { - val oldPeerID = responsibilityMap.put(locationKey, peerId) - val hasChanged: Boolean - if (oldPeerID != null) { - if (oldPeerID == peerId) { - hasChanged = false - } else { - removeRevResponsibility(oldPeerID, locationKey) - hasChanged = true - } - } else { - hasChanged = true - } - var contentIDs: MutableSet? = responsibilityMapRev[peerId] as MutableSet? - if (contentIDs == null) { - contentIDs = HashSet() - } - contentIDs.add(locationKey) - responsibilityMapRev[peerId] = contentIDs - dbEnvironment.sync() - return hasChanged - } - - private fun removeRevResponsibility(peerId: Number160, locationKey: Number160) { - val contentIDs = responsibilityMapRev[peerId] as MutableSet - if (contentIDs != null) { - contentIDs.remove(locationKey) - if (contentIDs.isEmpty()) { - responsibilityMapRev.remove(peerId) - } else { - responsibilityMapRev[peerId] = contentIDs - } - } - dbEnvironment.sync() - } - - override fun protectDomain(key: Number320?, publicKey: PublicKey?): Boolean { - protectedDomainMap[key] = publicKey - dbEnvironment.sync() - return true - } - - override fun storageCheckIntervalMillis(): Int { - return storageCheckIntervalMillis - } - - override fun isDomainProtectedByOthers(key: Number320?, publicKey: PublicKey?): Boolean { - val other = protectedDomainMap[key] ?: return false - return other != publicKey - } - - override fun removeTimeout(key: Number640) { - val expiration = timeoutMap.remove(key) ?: return - removeRevTimeout(key, expiration) - timeoutMapDB.sync() - dbEnvironment.sync() - } - - override fun removeResponsibility(locationKey: Number160) { - val peerId = responsibilityMap.remove(locationKey) - if (peerId != null) { - removeRevResponsibility(peerId, locationKey) - } - dbEnvironment.sync() - } - - override fun protectEntry(key: Number480?, publicKey: PublicKey?): Boolean { - protectedEntryMap[key] = publicKey - dbEnvironment.sync() - return true - } - - override fun map(): NavigableMap { - val retVal = TreeMap() - for ((key, value) in dataMap) { - retVal[key] = value - } - - return retVal - } - - override fun isEntryProtectedByOthers(key: Number480?, publicKey: PublicKey?): Boolean { - val other = protectedEntryMap[key] ?: return false - return other != publicKey - } - - override fun subMap(from: Number640?, to: Number640?, limit: Int, ascending: Boolean): NavigableMap { - val tmp = dataMap.subMap(from, true, to, true) - val descendingMap = TreeMap(tmp).descendingMap() - val retVal = TreeMap() - if (limit < 0) { - for ((key, value) in if (ascending) tmp else descendingMap) { - retVal[key] = value - } - } else { - val limit1 = Math.min(limit, tmp.size) - val iterator = if (ascending) - tmp.entries.iterator() - else - descendingMap.entries.iterator() - var i = 0 - while (iterator.hasNext() && i < limit1) { - val entry = iterator.next() - retVal[entry.key] = entry.value - i++ - } - } - return retVal - } - - override fun subMapTimeout(to: Long): MutableCollection { - val tmp = timeoutMapRev.subMap(0L, to) - val toRemove = ArrayList() - for (set in tmp.values) { - toRemove.addAll(set) - } - return toRemove - } - - override fun close() { - dataMapDB.close() - timeoutMapDB.close() - timeoutMapRevDB.close() - protectedDomainMapDB.close() - protectedEntryMapDB.close() - responsibilityMapDB.close() - responsibilityMapRevDB.close() - dbEnvironment.close() - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/actions/NetworkActions.java b/app/src/main/java/io/github/chronosx88/influence/helpers/actions/NetworkActions.java deleted file mode 100644 index d691620..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/actions/NetworkActions.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.chronosx88.influence.helpers.actions; - -public class NetworkActions { - public static final int CREATE_CHAT = 0x0; - public static final int TEXT_MESSAGE = 0x1; - public static final int JOIN_CHAT = 0x2; - public static final int NEXT_CHUNK_REF = 0x3; -} 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 deleted file mode 100644 index 152af68..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/actions/UIActions.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.chronosx88.influence.helpers.actions; - -public class UIActions { - public static final int BOOTSTRAP_NOT_SPECIFIED = 0x0; - public static final int NETWORK_ERROR = 0x1; - public static final int BOOTSTRAP_SUCCESS = 0x2; - public static final int PORT_FORWARDING_ERROR = 0x3; - public static final int RELAY_CONNECTION_ERROR = 0x4; - public static final int BOOTSTRAP_ERROR = 0x5; - public static final int NEW_CHAT = 0x6; - public static final int PEER_NOT_EXIST = 0x7; - 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/helpers/comparators/CompareLong.java b/app/src/main/java/io/github/chronosx88/influence/helpers/comparators/CompareLong.java deleted file mode 100644 index f8e74f0..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/comparators/CompareLong.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.chronosx88.influence.helpers.comparators; - -import java.io.Serializable; -import java.util.Comparator; - -import io.github.chronosx88.influence.helpers.Serializer; - -public class CompareLong implements Comparator, Serializable { - @Override - public int compare(byte[] o1, byte[] o2) { - Serializer serializer = new Serializer<>(); - Long num1 = serializer.deserialize(o1); - Long num2 = serializer.deserialize(o2); - return num1.compareTo(num2); - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/comparators/CompareNumber640.java b/app/src/main/java/io/github/chronosx88/influence/helpers/comparators/CompareNumber640.java deleted file mode 100644 index 35a0b5f..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/comparators/CompareNumber640.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.chronosx88.influence.helpers.comparators; - -import net.tomp2p.peers.Number640; - -import java.io.Serializable; -import java.util.Comparator; - -import io.github.chronosx88.influence.helpers.Serializer; - -public class CompareNumber640 implements Comparator, Serializable { - @Override - public int compare(byte[] o1, byte[] o2) { - Serializer serializer = new Serializer<>(); - Number640 num1 = serializer.deserialize(o1); - Number640 num2 = serializer.deserialize(o2); - return num1.compareTo(num2); - } -} - - 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 57e8d0f..2d5897e 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 @@ -18,7 +18,6 @@ import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.KeyPairManager; import io.github.chronosx88.influence.helpers.LocalDBWrapper; import io.github.chronosx88.influence.helpers.ObservableUtils; -import io.github.chronosx88.influence.helpers.P2PUtils; import io.github.chronosx88.influence.helpers.actions.NetworkActions; import io.github.chronosx88.influence.helpers.actions.UIActions; import io.github.chronosx88.influence.models.JoinChatMessage; @@ -159,9 +158,9 @@ public class ChatLogic implements CoreContracts.IChatLogicContract { if(messages.size() > 10) { String messageID = UUID.randomUUID().toString(); try { - P2PUtils.put(chatEntity.chatID + "_messages" + chunkID, messageID, new Data(gson.toJson(new NextChunkReference(messageID, AppHelper.getPeerID(), AppHelper.getPeerID(), System.currentTimeMillis(), chatEntity.chunkCursor+1)))); + int nextChunkCursor = chatEntity.chunkCursor + 1; + P2PUtils.put(chatEntity.chatID + "_messages" + chunkID, messageID, new Data(gson.toJson(new NextChunkReference(messageID, AppHelper.getPeerID(), AppHelper.getPeerID(), System.currentTimeMillis(), nextChunkCursor)))); P2PUtils.put(chatEntity.chatID + "_newMessage", null, new Data(messageID)); - LocalDBWrapper.updateChatEntity(chatEntity); } catch (IOException e) { e.printStackTrace(); } 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 3ddaaeb..219d7b3 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 @@ -45,12 +45,10 @@ import java.util.UUID; 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; import io.github.chronosx88.influence.helpers.LocalDBWrapper; import io.github.chronosx88.influence.helpers.NetworkHandler; import io.github.chronosx88.influence.helpers.ObservableUtils; -import io.github.chronosx88.influence.helpers.P2PUtils; import io.github.chronosx88.influence.helpers.StorageBerkeleyDB; import io.github.chronosx88.influence.helpers.actions.UIActions; import io.github.chronosx88.influence.models.ChatMetadata; 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 index e0a7899..e616a87 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt +++ b/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt @@ -5,7 +5,6 @@ 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.ObservableUtils -import io.github.chronosx88.influence.helpers.P2PUtils import io.github.chronosx88.influence.helpers.actions.UIActions import net.tomp2p.peers.Number640 import net.tomp2p.storage.Data diff --git a/app/src/main/java/io/github/chronosx88/influence/models/BasicNetworkMessage.java b/app/src/main/java/io/github/chronosx88/influence/models/BasicNetworkMessage.java deleted file mode 100644 index 1bb934f..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/BasicNetworkMessage.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; - -/** - * Абстрактный класс-модель для любых сообщений, которые передаются по DHT-сети - */ -public class BasicNetworkMessage implements Serializable { - private int action; - private String messageID; - private String senderID; - private String username; - private long timestamp; - - public BasicNetworkMessage() { - // - } - - public BasicNetworkMessage(int action, String messageID, String senderID, String username, long timestamp) { - this.action = action; - this.senderID = senderID; - this.username = username; - this.messageID = messageID; - this.timestamp = timestamp; - } - - public int getAction() { - return action; - } - - public String getSenderID() { - return senderID; - } - - public String getUsername() { - return username; - } - - public String getMessageID() { - return messageID; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/ChatMember.java b/app/src/main/java/io/github/chronosx88/influence/models/ChatMember.java deleted file mode 100644 index 133d584..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/ChatMember.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; - -public class ChatMember implements Serializable { - private String username; - private String peerID; - - public ChatMember(String username, String peerID) { - this.username = username; - this.peerID = peerID; - } - - public String getUsername() { - return username; - } - - public String getPeerID() { - return peerID; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/ChatMetadata.java b/app/src/main/java/io/github/chronosx88/influence/models/ChatMetadata.java deleted file mode 100644 index 54e2141..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/ChatMetadata.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; -import java.util.ArrayList; - -public class ChatMetadata implements Serializable { - private String name; - private ArrayList admins; - private ArrayList banned; - - public ChatMetadata(String name, ArrayList admins, ArrayList banned) { - this.name = name; - this.admins = admins; - this.banned = banned; - } - - public String getName() { - return name; - } - - public ArrayList getAdmins() { - return admins; - } - - public ArrayList getBanned() { - return banned; - } - - public void setName(String name) { - this.name = name; - } - - public void setAdmins(ArrayList admins) { - this.admins = admins; - } - - public void setBanned(ArrayList banned) { - this.banned = banned; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/JoinChatMessage.java b/app/src/main/java/io/github/chronosx88/influence/models/JoinChatMessage.java deleted file mode 100644 index e12162a..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/JoinChatMessage.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; -import java.util.UUID; - -import io.github.chronosx88.influence.helpers.actions.NetworkActions; - -public class JoinChatMessage extends BasicNetworkMessage implements Serializable { - private String chatID; - - public JoinChatMessage(String senderID, String username, String chatID, long timestamp) { - super(NetworkActions.JOIN_CHAT, UUID.randomUUID().toString(), senderID, username, timestamp); - this.chatID = chatID; - } - - public String getChatID() { - return chatID; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/NewChatRequestMessage.java b/app/src/main/java/io/github/chronosx88/influence/models/NewChatRequestMessage.java deleted file mode 100644 index f1e17e4..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/NewChatRequestMessage.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; - -import io.github.chronosx88.influence.helpers.actions.NetworkActions; - -public class NewChatRequestMessage extends BasicNetworkMessage implements Serializable { - private String chatID; - private int chunkID; - - public NewChatRequestMessage(String messageID, String chatID, String senderID, String username, long timestamp, int chunkID) { - super(NetworkActions.CREATE_CHAT, messageID, senderID, username, timestamp); - this.chatID = chatID; - this.chunkID = chunkID; - } - - public String getChatID() { - return chatID; - } - - public int getChunkID() { - return chunkID; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/NextChunkReference.java b/app/src/main/java/io/github/chronosx88/influence/models/NextChunkReference.java deleted file mode 100644 index 7251de8..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/NextChunkReference.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; - -import io.github.chronosx88.influence.helpers.actions.NetworkActions; - -public class NextChunkReference extends BasicNetworkMessage implements Serializable { - private int nextChunkID; - - public NextChunkReference(String messageID, String senderID, String username, long timestamp, int nextChunkID) { - super(NetworkActions.NEXT_CHUNK_REF, messageID, senderID, username, timestamp); - this.nextChunkID = nextChunkID; - } - - public int getNextChunkID() { - return nextChunkID; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/PublicUserProfile.kt b/app/src/main/java/io/github/chronosx88/influence/models/PublicUserProfile.kt deleted file mode 100644 index 699a946..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/PublicUserProfile.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.chronosx88.influence.models - -import net.tomp2p.peers.PeerAddress - -import java.io.Serializable - -/** - * Класс-модель публичного профиля для размещения в DHT-сети - */ -data class PublicUserProfile(var userName: String?, var peerAddress: PeerAddress?) : Serializable diff --git a/app/src/main/java/io/github/chronosx88/influence/models/TextMessage.java b/app/src/main/java/io/github/chronosx88/influence/models/TextMessage.java deleted file mode 100644 index 91c7276..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/models/TextMessage.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.chronosx88.influence.models; - -import java.io.Serializable; - -import io.github.chronosx88.influence.helpers.actions.NetworkActions; - -public class TextMessage extends BasicNetworkMessage implements Serializable { - private String chatID; // Chat ID - private String text; // Message text - private boolean isRead; // Message Read Indicator - - public TextMessage(String senderID, String messageID, String chatID, String username, long timestamp, String text, boolean isRead) { - super(NetworkActions.TEXT_MESSAGE, messageID, senderID, username, timestamp); - this.chatID = chatID; - this.text = text; - this.isRead = isRead; - } - - public String getChatID() { - return chatID; - } - - public String getText() { - return text; - } - - public boolean isRead() { - return isRead; - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/daos/ChatDao.java b/app/src/main/java/io/github/chronosx88/influence/models/daos/ChatDao.java index c9a4f8c..04b3adb 100644 --- a/app/src/main/java/io/github/chronosx88/influence/models/daos/ChatDao.java +++ b/app/src/main/java/io/github/chronosx88/influence/models/daos/ChatDao.java @@ -14,14 +14,14 @@ public interface ChatDao { @Insert(onConflict = OnConflictStrategy.IGNORE) void addChat(ChatEntity chatEntity); - @Query("DELETE FROM chats WHERE chatID = :chatID") - void deleteChat(String chatID); + @Query("DELETE FROM chats WHERE jid = :jid") + void deleteChat(String jid); @Query("SELECT * FROM chats") List getAllChats(); - @Query("SELECT * FROM chats WHERE chatID = :chatID") - List getChatByChatID(String chatID); + @Query("SELECT * FROM chats WHERE jid = :jid") + List getChatByChatID(String jid); @Update void updateChat(ChatEntity chat); diff --git a/app/src/main/java/io/github/chronosx88/influence/models/daos/MessageDao.java b/app/src/main/java/io/github/chronosx88/influence/models/daos/MessageDao.java index 85afa75..bba41ae 100644 --- a/app/src/main/java/io/github/chronosx88/influence/models/daos/MessageDao.java +++ b/app/src/main/java/io/github/chronosx88/influence/models/daos/MessageDao.java @@ -12,19 +12,19 @@ import io.github.chronosx88.influence.models.roomEntities.MessageEntity; @Dao public interface MessageDao { @Insert(onConflict = OnConflictStrategy.IGNORE) - void insertMessage(MessageEntity chatModel); + long insertMessage(MessageEntity chatModel); @Query("DELETE FROM messages WHERE messageID = :messageID") void deleteMessage(String messageID); - @Query("DELETE FROM messages WHERE chatID = :chatID") - void deleteMessagesByChatID(String chatID); + @Query("DELETE FROM messages WHERE jid = :jid") + void deleteMessagesByChatID(String jid); - @Query("SELECT * FROM messages WHERE chatID = :chatID") - List getMessagesByChatID(String chatID); + @Query("SELECT * FROM messages WHERE jid = :jid") + List getMessagesByChatID(String jid); @Query("SELECT * FROM messages WHERE messageID = :messageID") - List getMessageByID(String messageID); + List getMessageByID(long messageID); @Update void updateMessage(MessageEntity message); diff --git a/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/ChatEntity.java b/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/ChatEntity.java index 0315c08..8bf0476 100644 --- a/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/ChatEntity.java +++ b/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/ChatEntity.java @@ -1,7 +1,5 @@ package io.github.chronosx88.influence.models.roomEntities; -import java.util.ArrayList; - import androidx.annotation.NonNull; import androidx.room.ColumnInfo; import androidx.room.Entity; @@ -9,19 +7,11 @@ import androidx.room.PrimaryKey; @Entity(tableName = "chats") public class ChatEntity { - @PrimaryKey @NonNull public String chatID; - @ColumnInfo public String name; - @ColumnInfo public String metadataRef; - @ColumnInfo public String membersRef; - @ColumnInfo public ArrayList bannedUsers; - @ColumnInfo public int chunkCursor; + @PrimaryKey @NonNull public String jid; + @ColumnInfo public String chatName; - public ChatEntity(@NonNull String chatID, String name, String metadataRef, String membersRef, ArrayList bannedUsers, int chunkCursor) { - this.chatID = chatID; - this.name = name; - this.metadataRef = metadataRef; - this.membersRef = membersRef; - this.bannedUsers = bannedUsers; - this.chunkCursor = chunkCursor; + public ChatEntity(@NonNull String jid, String chatName) { + this.jid = jid; + this.chatName = chatName; } } diff --git a/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/MessageEntity.java b/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/MessageEntity.java index bdd8eaa..a152dde 100644 --- a/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/MessageEntity.java +++ b/app/src/main/java/io/github/chronosx88/influence/models/roomEntities/MessageEntity.java @@ -7,22 +7,17 @@ import androidx.room.PrimaryKey; @Entity(tableName = "messages") public class MessageEntity { - @PrimaryKey @NonNull public String messageID; // Global message ID - @ColumnInfo public int type; // Message type - @ColumnInfo public String chatID; // Chat ID - @ColumnInfo public String senderID; // PeerID - @ColumnInfo public String username; // Username + @PrimaryKey(autoGenerate = true) public long messageID; // Global message ID + @ColumnInfo public String jid; // Chat ID + @ColumnInfo public String senderJid; @ColumnInfo public long timestamp; // Timestamp @ColumnInfo public String text; // Message text @ColumnInfo public boolean isSent; // Send status indicator @ColumnInfo public boolean isRead; // Message Read Indicator - public MessageEntity(int type, String messageID, String chatID, String senderID, String username, long timestamp, String text, boolean isSent, boolean isRead) { - this.type = type; - this.messageID = messageID; - this.chatID = chatID; - this.senderID = senderID; - this.username = username; + public MessageEntity(String jid, String senderJid, long timestamp, String text, boolean isSent, boolean isRead) { + this.jid = jid; + this.senderJid = senderJid; this.timestamp = timestamp; this.text = text; this.isSent = isSent; diff --git a/app/src/main/java/io/github/chronosx88/influence/views/LoginActivity.java b/app/src/main/java/io/github/chronosx88/influence/views/LoginActivity.java new file mode 100644 index 0000000..f07a5eb --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/views/LoginActivity.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 ChronosX88 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.chronosx88.influence.views; + +import android.app.ProgressDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import io.github.chronosx88.influence.R; +import io.github.chronosx88.influence.XMPPConnectionService; +import io.github.chronosx88.influence.contracts.CoreContracts; +import io.github.chronosx88.influence.helpers.AppHelper; + +public class LoginActivity extends AppCompatActivity implements CoreContracts.ILoginViewContract { + private EditText jidEditText; + private EditText passwordEditText; + private Button signInButton; + private BroadcastReceiver broadcastReceiver; + private ProgressDialog progressDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + jidEditText = findViewById(R.id.login_jid); + passwordEditText = findViewById(R.id.login_password); + signInButton = findViewById(R.id.sign_in_button); + progressDialog = new ProgressDialog(LoginActivity.this); + signInButton.setOnClickListener((v) -> { + if(checkLoginCredentials()) { + saveLoginCredentials(); + doLogin(); + } + }); + } + + @Override + public void loadingScreen(boolean state) { + if(state) + progressDialog.show(); + else + progressDialog.dismiss(); + } + + @Override + protected void onResume() { + super.onResume(); + broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + switch (action) { + case XMPPConnectionService.INTENT_AUTHENTICATED: { + loadingScreen(false); + finish(); + break; + } + case XMPPConnectionService.INTENT_AUTHENTICATION_FAILED: { + loadingScreen(false); + passwordEditText.setError("Invalid JID/Password"); + break; + } + } + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(XMPPConnectionService.INTENT_AUTHENTICATED); + filter.addAction(XMPPConnectionService.INTENT_AUTHENTICATION_FAILED); + this.registerReceiver(broadcastReceiver, filter); + } + + @Override + protected void onPause() { + super.onPause(); + this.unregisterReceiver(broadcastReceiver); + } + + private boolean checkLoginCredentials() { + jidEditText.setError(null); + passwordEditText.setError(null); + + String jid = jidEditText.getText().toString(); + String password = passwordEditText.getText().toString(); + + boolean cancel = false; + View focusView = null; + + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + passwordEditText.setError("Invalid password"); + focusView = passwordEditText; + cancel = true; + } + + if (TextUtils.isEmpty(jid)) { + jidEditText.setError("Field is required!"); + focusView = jidEditText; + cancel = true; + } else if (!isEmailValid(jid)) { + jidEditText.setError("Invalid JID"); + focusView = jidEditText; + cancel = true; + } + + if (cancel) { + focusView.requestFocus(); + return false; + } else { + return true; + } + } + + private boolean isEmailValid(String email) { + return email.contains("@"); + } + + private boolean isPasswordValid(String password) { + return password.length() > 4; + } + + private void saveLoginCredentials() { + AppHelper.getPreferences().edit() + .putString("jid", jidEditText.getText().toString()) + .putString("pass", passwordEditText.getText().toString()) + .putBoolean("logged_in", true) + .apply(); + } + + private void doLogin() { + loadingScreen(true); + startService(new Intent(this, XMPPConnectionService.class)); + } +} 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 4a54544..1d3f72f 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 @@ -32,7 +32,7 @@ import io.github.chronosx88.influence.views.fragments.ChatListFragment; import io.github.chronosx88.influence.views.fragments.SettingsFragment; import kotlin.Pair; -public class MainActivity extends AppCompatActivity implements IObserver, CoreContracts.IMainViewContract { +public class MainActivity extends AppCompatActivity implements CoreContracts.IMainViewContract { private CoreContracts.IMainPresenterContract presenter; private ProgressDialog progressDialog; @@ -88,7 +88,7 @@ public class MainActivity extends AppCompatActivity implements IObserver, CoreCo .commit(); presenter = new MainPresenter(this); - AppHelper.getObservable().register(this); + progressDialog = new ProgressDialog(MainActivity.this, R.style.AlertDialogTheme); progressDialog.setCancelable(false); @@ -101,54 +101,6 @@ public class MainActivity extends AppCompatActivity implements IObserver, CoreCo protected void onDestroy() { super.onDestroy(); presenter.onDestroy(); - AppHelper.getObservable().unregister(this); - } - - @Override - public void handleEvent(JsonObject object) { - switch (object.get("action").getAsInt()) { - case UIActions.BOOTSTRAP_NOT_SPECIFIED: { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Bootstrap-нода не указана. Прерываю подключение к сети...", Toast.LENGTH_LONG).show(); - }); - break; - } - case UIActions.NETWORK_ERROR: { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Ошибка сети. Возможно, нода недоступна, или у вас отсутствует Интернет.", Toast.LENGTH_LONG).show(); - }); - break; - } - case UIActions.BOOTSTRAP_SUCCESS: { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Нода успешно запущена!", Toast.LENGTH_LONG).show(); - }); - break; - } - case UIActions.PORT_FORWARDING_ERROR: { - runOnUiThread(() -> { - Toast.makeText(this, "Проблемы с пробросом портов. Возможно, у вас не настроен uPnP.", Toast.LENGTH_LONG).show(); - }); - break; - } - case UIActions.BOOTSTRAP_ERROR: { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Не удалось подключиться к бутстрап-ноде.", Toast.LENGTH_LONG).show(); - }); - break; - } - case UIActions.RELAY_CONNECTION_ERROR: { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Не удалось подключиться к relay-ноде.", Toast.LENGTH_LONG).show(); - }); - break; - } - } } @Override diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..f3ea72f --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + +