From 2a78d7bd8126889cb9fde7110d0dc43efa6877e6 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 22 May 2019 23:42:01 +0400 Subject: [PATCH] Moved interclass communication to EventBus library from greenrobot, implemented loading roster entries and user avatars. Refactored service system --- LICENSE | 8 +- app/build.gradle | 3 +- .../influence/LoginCredentials.java | 6 +- .../chronosx88/influence/XMPPConnection.java | 91 +++--- .../influence/XMPPConnectionService.java | 34 +- .../influence/contracts/CoreContracts.kt | 28 +- .../contracts/observer/INetworkObserver.java | 5 - .../contracts/observer/IObservable.java | 9 - .../contracts/observer/IObserver.java | 8 - .../influence/helpers/AppHelper.java | 74 ++++- .../influence/helpers/HashUtils.java | 56 ++++ .../influence/helpers/NetworkHandler.java | 18 +- .../influence/helpers/ObservableUtils.java | 50 --- .../influence/logic/ChatListLogic.java | 15 - .../chronosx88/influence/logic/ChatLogic.java | 21 +- .../influence/logic/DialogListLogic.java | 26 ++ .../chronosx88/influence/logic/MainLogic.java | 307 +----------------- .../influence/logic/SettingsLogic.kt | 41 --- .../influence/models/GenericDialog.java | 3 +- .../appEvents/AuthenticationStatusEvent.java | 30 ++ .../models/appEvents/NewChatEvent.java | 26 ++ .../models/appEvents/NewMessageEvent.java | 28 ++ .../influence/observable/MainObservable.java | 35 -- .../influence/presenters/ChatPresenter.kt | 54 +-- .../presenters/DialogListPresenter.java | 110 +++++-- .../influence/presenters/MainPresenter.kt | 63 +--- .../influence/presenters/SettingsPresenter.kt | 35 -- .../influence/views/LoginActivity.java | 95 +++--- .../influence/views/MainActivity.java | 29 +- .../views/fragments/DialogListFragment.kt | 16 +- .../views/fragments/SettingsFragment.java | 55 +--- app/src/main/res/layout/activity_login.xml | 3 +- app/src/main/res/xml/main_settings.xml | 19 -- 33 files changed, 552 insertions(+), 849 deletions(-) delete mode 100644 app/src/main/java/io/github/chronosx88/influence/contracts/observer/INetworkObserver.java delete mode 100644 app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObservable.java delete mode 100644 app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObserver.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java delete mode 100644 app/src/main/java/io/github/chronosx88/influence/helpers/ObservableUtils.java delete mode 100644 app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/logic/DialogListLogic.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/models/appEvents/AuthenticationStatusEvent.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewChatEvent.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewMessageEvent.java delete mode 100644 app/src/main/java/io/github/chronosx88/influence/observable/MainObservable.java diff --git a/LICENSE b/LICENSE index f288702..cccd6cb 100644 --- a/LICENSE +++ b/LICENSE @@ -510,7 +510,7 @@ covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. - If, pursuant to or in connection with a single transaction or + If, pursuant to or in xmppConnection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify @@ -527,9 +527,9 @@ in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work +patent license (a) in xmppConnection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that +for and in xmppConnection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. @@ -614,7 +614,7 @@ SUCH DAMAGES. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the +an absolute waiver of all civil liability in xmppConnection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. diff --git a/app/build.gradle b/app/build.gradle index f4c63f6..e371fe7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,8 @@ dependencies { implementation 'com.github.stfalcon:chatkit:0.3.3' implementation 'net.sourceforge.streamsupport:streamsupport:1.7.0' - + implementation 'org.greenrobot:eventbus:3.1.1' + implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.0' } repositories { mavenCentral() diff --git a/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java b/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java index e96f075..4f86166 100644 --- a/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java +++ b/app/src/main/java/io/github/chronosx88/influence/LoginCredentials.java @@ -18,7 +18,7 @@ package io.github.chronosx88.influence; public class LoginCredentials { - String username = ""; - String password = ""; - String jabberHost = ""; + public String username = ""; + public String password = ""; + public 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 index 3688686..c5eb369 100644 --- a/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java +++ b/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java @@ -17,15 +17,13 @@ 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.greenrobot.eventbus.EventBus; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.ReconnectionManager; @@ -34,16 +32,19 @@ 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.roster.Roster; +import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smack.tcp.XMPPTCPConnection; import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; +import org.jivesoftware.smackx.vcardtemp.VCardManager; import org.jxmpp.jid.EntityBareJid; -import org.jxmpp.jid.impl.JidCreate; -import org.jxmpp.stringprep.XmppStringprepException; import java.io.IOException; +import java.util.Set; import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.NetworkHandler; +import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent; public class XMPPConnection implements ConnectionListener { private final static String LOG_TAG = "XMPPConnection"; @@ -51,8 +52,8 @@ public class XMPPConnection implements ConnectionListener { private XMPPTCPConnection connection = null; private SharedPreferences prefs; private NetworkHandler networkHandler; - private BroadcastReceiver sendMessageReceiver = null; private Context context; + private Roster roster; public enum ConnectionState { CONNECTED, @@ -76,10 +77,10 @@ public class XMPPConnection implements ConnectionListener { credentials.jabberHost = jabberHost; credentials.password = password; } - networkHandler = new NetworkHandler(context); + networkHandler = new NetworkHandler(); } - public void connect() throws XMPPException, SmackException, IOException { + public void connect() throws XMPPException, IOException, SmackException { if(connection == null) { XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder() .setXmppDomain(credentials.jabberHost) @@ -90,8 +91,6 @@ public class XMPPConnection implements ConnectionListener { .setCompressionEnabled(true) .build(); - setupSendMessageReceiver(); - connection = new XMPPTCPConnection(conf); connection.addConnectionListener(this); if(credentials.jabberHost.equals("") && credentials.password.equals("") && credentials.username.equals("")){ @@ -108,6 +107,7 @@ public class XMPPConnection implements ConnectionListener { ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); ReconnectionManager.setEnabledPerDefault(true); reconnectionManager.enableAutomaticReconnection(); + roster = roster.getInstanceFor(connection); } } @@ -128,8 +128,7 @@ public class XMPPConnection implements ConnectionListener { public void authenticated(org.jivesoftware.smack.XMPPConnection connection, boolean resumed) { XMPPConnectionService.SESSION_STATE = SessionState.LOGGED_IN; prefs.edit().putBoolean("logged_in", true).apply(); - context.sendBroadcast(new Intent(XMPPConnectionService.INTENT_AUTHENTICATED)); - AppHelper.setJid(credentials.username + "@" + credentials.jabberHost); + EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.CONNECT_AND_LOGIN_SUCCESSFUL)); } @Override @@ -148,36 +147,10 @@ public class XMPPConnection implements ConnectionListener { 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; + public void sendMessage(EntityBareJid recipientJid, String messageText) { + Chat chat = ChatManager.getInstanceFor(connection).chatWith(recipientJid); 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 message = new Message(recipientJid, Message.Type.chat); message.setBody(messageText); chat.send(message); } catch (SmackException.NotConnectedException e) { @@ -190,4 +163,40 @@ public class XMPPConnection implements ConnectionListener { public XMPPTCPConnection getConnection() { return connection; } + + public byte[] getAvatar(EntityBareJid jid) { + if(isConnectionAlive()) { + VCardManager manager = VCardManager.getInstanceFor(connection); + byte[] avatar = null; + try { + avatar = manager.loadVCard(jid).getAvatar(); + } catch (SmackException.NoResponseException e) { + e.printStackTrace(); + } catch (XMPPException.XMPPErrorException e) { + e.printStackTrace(); + } catch (SmackException.NotConnectedException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return avatar; + } + return null; + } + + public Set getContactList() { + if(isConnectionAlive()) { + while (roster == null); + return roster.getEntries(); + } + return null; + } + + public boolean isConnectionAlive() { + if(XMPPConnectionService.CONNECTION_STATE.equals(ConnectionState.CONNECTED) && XMPPConnectionService.SESSION_STATE.equals(SessionState.LOGGED_IN)) { + return true; + } else { + return false; + } + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java b/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java index c9ea545..03e20b8 100644 --- a/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java +++ b/app/src/main/java/io/github/chronosx88/influence/XMPPConnectionService.java @@ -20,28 +20,21 @@ package io.github.chronosx88.influence; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import org.greenrobot.eventbus.EventBus; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import java.io.IOException; import io.github.chronosx88.influence.helpers.AppHelper; +import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent; 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 CONNECTION_STATE = XMPPConnection.ConnectionState.DISCONNECTED; public static XMPPConnection.SessionState SESSION_STATE = XMPPConnection.SessionState.LOGGED_OUT; @@ -50,11 +43,14 @@ public class XMPPConnectionService extends Service { private boolean isThreadAlive = false; private XMPPConnection connection; private Context context = AppHelper.getContext(); + private XMPPServiceBinder binder = new XMPPServiceBinder(); public XMPPConnectionService() { } @Override - public IBinder onBind(Intent intent) { return null; } + public IBinder onBind(Intent intent) { + return binder; + } public void onServiceStart() { if(!isThreadAlive) @@ -77,6 +73,7 @@ public class XMPPConnectionService extends Service { threadHandler.post(() -> { if(connection != null) { connection.disconnect(); + connection = null; } }); } @@ -87,9 +84,12 @@ public class XMPPConnectionService extends Service { } try { connection.connect(); - } catch (IOException | SmackException | XMPPException e) { - Intent intent = new Intent(INTENT_AUTHENTICATION_FAILED); - context.sendBroadcast(intent); + } catch (IOException | SmackException e) { + EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.NETWORK_ERROR)); + e.printStackTrace(); + stopSelf(); + } catch (XMPPException e) { + EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.INCORRECT_LOGIN_OR_PASSWORD)); e.printStackTrace(); stopSelf(); } @@ -106,4 +106,10 @@ public class XMPPConnectionService extends Service { super.onDestroy(); onServiceStop(); } + + public class XMPPServiceBinder extends Binder { + public XMPPConnection getConnection() { + return connection; + } + } } 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 35d4bb0..d7e427d 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,21 +9,26 @@ import io.github.chronosx88.influence.models.GenericDialog import io.github.chronosx88.influence.models.GenericMessage import io.github.chronosx88.influence.models.roomEntities.ChatEntity import io.github.chronosx88.influence.models.roomEntities.MessageEntity +import org.jivesoftware.smack.roster.RosterEntry interface CoreContracts { - interface ViewWithLoadingScreen { - fun loadingScreen(state: Boolean); + interface IGenericView { + fun loadingScreen(state: Boolean) } // -----ChatList----- interface IDialogListLogicContract { - fun loadAllChats(): List + fun loadLocalChats(): List + fun getRemoteContacts(): Set? } interface IDialogListPresenterContract { fun openChat(chatID: String) + fun onStart() + fun onStop() + fun loadRemoteContactList() } interface IChatListViewContract { @@ -35,12 +40,12 @@ interface CoreContracts { // -----MainActivity----- interface IMainLogicContract { + fun startService() } interface IMainPresenterContract { - fun initPeer() + fun initConnection() fun startChatWithPeer(username: String) - fun onDestroy() } interface IMainViewContract { @@ -51,7 +56,7 @@ interface CoreContracts { // -----ChatActivity----- interface IChatLogicContract { - fun sendMessage(text: String): MessageEntity + fun sendMessage(text: String): MessageEntity? } interface IChatPresenterContract { @@ -66,20 +71,15 @@ interface CoreContracts { // -----SettingsFragment----- - interface ISettingsLogic { - fun checkUsernameExists(username: String) : Boolean - } + interface ISettingsLogic // TODO - interface ISettingsPresenter { - fun updateUsername(username: String) - } + interface ISettingsPresenter // TODO interface ISettingsView { fun loadingScreen(state: Boolean) fun showMessage(message: String) - fun refreshScreen() } // -----LoginActivity----- - interface ILoginViewContract : ViewWithLoadingScreen + interface ILoginViewContract : IGenericView } diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/INetworkObserver.java b/app/src/main/java/io/github/chronosx88/influence/contracts/observer/INetworkObserver.java deleted file mode 100644 index 3a2d0fe..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/INetworkObserver.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.chronosx88.influence.contracts.observer; - -public interface INetworkObserver { - void handleEvent(Object object); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObservable.java b/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObservable.java deleted file mode 100644 index d94166f..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObservable.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.chronosx88.influence.contracts.observer; - -import org.json.JSONObject; - -public interface IObservable { - void register(IObserver observer); - void unregister(IObserver observer); - void notifyUIObservers(JSONObject jsonObject); -} diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObserver.java b/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObserver.java deleted file mode 100644 index 3eb83c5..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/observer/IObserver.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.chronosx88.influence.contracts.observer; - -import org.json.JSONException; -import org.json.JSONObject; - -public interface IObserver { - void handleEvent(JSONObject object) throws JSONException; -} \ No newline at end of file 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 ada74ae..bfb6e73 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,8 @@ package io.github.chronosx88.influence.helpers; import android.app.Application; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; import android.preference.PreferenceManager; import androidx.multidex.MultiDexApplication; @@ -12,45 +14,39 @@ import com.instacart.library.truetime.TrueTime; import java.io.IOException; -import io.github.chronosx88.influence.observable.MainObservable; +import io.github.chronosx88.influence.LoginCredentials; +import io.github.chronosx88.influence.XMPPConnection; /** * Extended Application class which designed for centralized getting various objects from anywhere in the application. */ - public class AppHelper extends MultiDexApplication { private static Application instance; - private static MainObservable observable; public final static String APP_NAME = "Influence"; private static String jid; private static RoomHelper chatDB; private static SharedPreferences preferences; + private static XMPPConnection xmppConnection; + private static LoginCredentials currentLoginCredentials; + private static Handler mainUIThreadHandler; @Override public void onCreate() { super.onCreate(); instance = this; - observable = new MainObservable(); - chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB") - .allowMainThreadQueries() - .build(); + + mainUIThreadHandler = new Handler(Looper.getMainLooper()); + initChatDB(); preferences = PreferenceManager.getDefaultSharedPreferences(instance); - new Thread(() -> { - try { - TrueTime.build().initialize(); - } catch (IOException e) { - e.printStackTrace(); - } - }).start(); + initTrueTime(); + loadLoginCredentials(); } public static Context getContext() { return instance.getApplicationContext(); } - public static MainObservable getObservable() { return observable; } - public static String getJid() { return jid; } public static void setJid(String jid1) { jid = jid1; } @@ -60,4 +56,50 @@ public class AppHelper extends MultiDexApplication { public static SharedPreferences getPreferences() { return preferences; } + + public static XMPPConnection getXmppConnection() { + return xmppConnection; + } + + public static void setXmppConnection(XMPPConnection xmppConnection) { + AppHelper.xmppConnection = xmppConnection; + } + + private static void loadLoginCredentials() { + currentLoginCredentials = new LoginCredentials(); + String jid = preferences.getString("jid", null); + String password = preferences.getString("pass", null); + if(jid != null && password != null) { + String username = jid.split("@")[0]; + String jabberHost = jid.split("@")[1]; + currentLoginCredentials.username = username; + currentLoginCredentials.jabberHost = jabberHost; + currentLoginCredentials.password = password; + } + AppHelper.setJid(currentLoginCredentials.username + "@" + currentLoginCredentials.jabberHost); + } + + private static void initTrueTime() { + new Thread(() -> { + boolean isTrueTimeIsOn = false; + while(!isTrueTimeIsOn) { + try { + TrueTime.build().initialize(); + isTrueTimeIsOn = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + } + + private void initChatDB() { + chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB") + .allowMainThreadQueries() + .build(); + } + + public static Handler getMainUIThread() { + return mainUIThreadHandler; + } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java b/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java new file mode 100644 index 0000000..93f5434 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java @@ -0,0 +1,56 @@ +/* + * 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.helpers; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class HashUtils { + public static String sha1(final String text) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(text.getBytes("UTF-8"), 0, text.length()); + byte[] sha1hash = md.digest(); + return hashToString(sha1hash); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } + + private static String hashToString(final byte[] buf) { + if (buf == null) return ""; + int l = buf.length; + StringBuffer result = new StringBuffer(2 * l); + for (int i = 0; i < buf.length; i++) { + appendByte(result, buf[i]); + } + return result.toString(); + } + + private final static String HEX_PACK = "0123456789ABCDEF"; + + private static void appendByte(final StringBuffer sb, final byte b) { + sb + .append(HEX_PACK.charAt((b >> 4) & 0x0f)) + .append(HEX_PACK.charAt(b & 0x0f)); + } +} 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 9512a6f..02182af 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,24 +1,17 @@ package io.github.chronosx88.influence.helpers; -import android.content.Context; -import android.content.Intent; - import com.instacart.library.truetime.TrueTime; +import org.greenrobot.eventbus.EventBus; 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.XMPPConnectionService; +import io.github.chronosx88.influence.models.appEvents.NewMessageEvent; public class NetworkHandler implements IncomingChatMessageListener { private final static String LOG_TAG = "NetworkHandler"; - private Context context; - - public NetworkHandler(Context context) { - this.context = context; - } @Override public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { @@ -26,10 +19,7 @@ public class NetworkHandler implements IncomingChatMessageListener { 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); + + EventBus.getDefault().post(new NewMessageEvent(chat.getXmppAddressOfChatPartner().toString(), messageID)); } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/ObservableUtils.java b/app/src/main/java/io/github/chronosx88/influence/helpers/ObservableUtils.java deleted file mode 100644 index 60f698d..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/ObservableUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.chronosx88.influence.helpers; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public class ObservableUtils { - public static void notifyUI(int action) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("action", action); - } catch (JSONException e) { - e.printStackTrace(); - } - AppHelper.getObservable().notifyUIObservers(jsonObject); - } - - public static void notifyUI(int action, String... additional) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("action", action); - } catch (JSONException e) { - e.printStackTrace(); - } - JSONArray jsonArray = new JSONArray(); - for(String info : additional) { - jsonArray.put(info); - } - try { - jsonObject.put("additional", jsonArray); - } catch (JSONException e) { - e.printStackTrace(); - } - AppHelper.getObservable().notifyUIObservers(jsonObject); - } - - public static void notifyUI(int action, int additional) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("action", action); - jsonObject.put("additional", additional); - } catch (JSONException e) { - e.printStackTrace(); - } - AppHelper.getObservable().notifyUIObservers(jsonObject); - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java deleted file mode 100644 index 6e34198..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/logic/ChatListLogic.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.chronosx88.influence.logic; - -import java.util.List; - -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 CoreContracts.IDialogListLogicContract { - - @Override - public List loadAllChats() { - return AppHelper.getChatDB().chatDao().getAllChats(); - } -} 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 3639821..7941989 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 @@ -1,11 +1,11 @@ package io.github.chronosx88.influence.logic; -import android.content.Intent; - import com.instacart.library.truetime.TrueTime; -import io.github.chronosx88.influence.XMPPConnection; -import io.github.chronosx88.influence.XMPPConnectionService; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; + import io.github.chronosx88.influence.contracts.CoreContracts; import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.LocalDBWrapper; @@ -25,11 +25,14 @@ public class ChatLogic implements CoreContracts.IChatLogicContract { @Override public MessageEntity sendMessage(String text) { - if (XMPPConnectionService.CONNECTION_STATE.equals(XMPPConnection.ConnectionState.CONNECTED)) { - Intent intent = new Intent(XMPPConnectionService.INTENT_SEND_MESSAGE); - intent.putExtra(XMPPConnectionService.MESSAGE_BODY, text); - intent.putExtra(XMPPConnectionService.MESSAGE_RECIPIENT, chatEntity.jid); - AppHelper.getContext().sendBroadcast(intent); + if (AppHelper.getXmppConnection().isConnectionAlive()) { + EntityBareJid jid; + try { + jid = JidCreate.entityBareFrom(chatEntity.jid); + } catch (XmppStringprepException e) { + return null; + } + AppHelper.getXmppConnection().sendMessage(jid, text); long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, false, false); return LocalDBWrapper.getMessageByID(messageID); } else { diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/DialogListLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/DialogListLogic.java new file mode 100644 index 0000000..a73ab1c --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/logic/DialogListLogic.java @@ -0,0 +1,26 @@ +package io.github.chronosx88.influence.logic; + +import org.jivesoftware.smack.roster.RosterEntry; + +import java.util.List; +import java.util.Set; + +import io.github.chronosx88.influence.contracts.CoreContracts; +import io.github.chronosx88.influence.helpers.AppHelper; +import io.github.chronosx88.influence.models.roomEntities.ChatEntity; + +public class DialogListLogic implements CoreContracts.IDialogListLogicContract { + + @Override + public List loadLocalChats() { + return AppHelper.getChatDB().chatDao().getAllChats(); + } + + @Override + public Set getRemoteContacts() { + if(AppHelper.getXmppConnection() != null) { + return AppHelper.getXmppConnection().getContactList(); + } + return null; + } +} 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 f037fb8..7fe00fe 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 @@ -1,12 +1,14 @@ package io.github.chronosx88.influence.logic; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; -import com.google.gson.Gson; - +import io.github.chronosx88.influence.XMPPConnectionService; import io.github.chronosx88.influence.contracts.CoreContracts; import io.github.chronosx88.influence.helpers.AppHelper; -import io.github.chronosx88.influence.helpers.KeyPairManager; public class MainLogic implements CoreContracts.IMainLogicContract { private static final String LOG_TAG = MainLogic.class.getName(); @@ -17,294 +19,21 @@ public class MainLogic implements CoreContracts.IMainLogicContract { this.context = AppHelper.getContext(); } - /*@Override - public void initPeer() { - org.apache.log4j.BasicConfigurator.configure(); - - if(checkFirstRun()) { - SharedPreferences.Editor editor = preferences.edit(); - String uuid = UUID.randomUUID().toString(); - editor.putString("peerID", uuid); - editor.apply(); - } - - peerID = Number160.createHash(preferences.getString("peerID", null)); - - new Thread(() -> { - try { - File dhtDBEnv = new File(context.getFilesDir(), "dhtDBEnv"); - if(!dhtDBEnv.exists()) - dhtDBEnv.mkdirs(); - Storage storage = new StorageBerkeleyDB(peerID, dhtDBEnv, new RSASignatureFactory()); - this.storage = storage; - peerDHT = new PeerBuilderDHT( - new PeerBuilder(peerID) - .ports(7243) - .channelClientConfiguration(createChannelClientConfig()) - .channelServerConfiguration(createChannelServerConfig()) - .start() - ) - .storage(storage) - .start(); - Runtime.getRuntime().addShutdownHook(new JVMShutdownHook(storage)); - try { - String bootstrapIP = this.preferences.getString("bootstrapAddress", null); - if(bootstrapIP == null) { - throw new NullPointerException(); - } - bootstrapAddress = Inet4Address.getByName(bootstrapIP); - } catch (NullPointerException e) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("action", UIActions.BOOTSTRAP_NOT_SPECIFIED); - AppHelper.getObservable().notifyUIObservers(jsonObject); - peerDHT.shutdown(); - return; - } catch (UnknownHostException e) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("action", UIActions.NETWORK_ERROR); - AppHelper.getObservable().notifyUIObservers(jsonObject); - peerDHT.shutdown(); - return; - } - - boolean discoveredExternalAddress = false; - - if(discoverExternalAddress()) { - discoveredExternalAddress = true; - } - - if(!discoveredExternalAddress) { - if(!setupConnectionToRelay()) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("action", UIActions.RELAY_CONNECTION_ERROR); - AppHelper.getObservable().notifyUIObservers(jsonObject); - return; - } - } - - if(!bootstrapPeer()) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("action", UIActions.BOOTSTRAP_ERROR); - AppHelper.getObservable().notifyUIObservers(jsonObject); - return; - } - - JsonObject jsonObject = new JsonObject(); - 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 - public void run() { - if(checkNewChatsThread == null) { - checkNewChatsThread = new Thread(NetworkHandler::handlePendingChatRequests); - checkNewChatsThread.start(); - } - if(!checkNewChatsThread.isAlive()) { - checkNewChatsThread = new Thread(NetworkHandler::handlePendingChatRequests); - checkNewChatsThread.start(); - } - } - }; - Timer timer = new Timer(); - timer.schedule(timerTask, 1, 5000); - replication = new IndirectReplication(peerDHT).start(); - } catch (IOException e) { - e.printStackTrace(); - } - }).start(); - } - - private boolean bootstrapPeer() { - FutureBootstrap futureBootstrap = peerDHT.peer().bootstrap().inetAddress(bootstrapAddress).ports(7243).start(); - futureBootstrap.awaitUninterruptibly(); - if(futureBootstrap.isSuccess()) { - Log.i("MainLogic", "# Successfully bootstrapped to " + bootstrapAddress.toString()); - return true; - } else { - Log.e("MainLogic", "# Cannot bootstrap to " + bootstrapAddress.toString() + ". Reason: " + futureBootstrap.failedReason()); - return false; - } - } - - private boolean discoverExternalAddress() { - FutureDiscover futureDiscover = peerDHT - .peer() - .discover() - .inetAddress(bootstrapAddress) - .ports(7243) - .start(); - futureDiscover.awaitUninterruptibly(); - bootstrapPeerAddress = futureDiscover.reporter(); - if(futureDiscover.isSuccess()) { - Log.i(LOG_TAG, "# Success discover! Your external IP: " + futureDiscover.peerAddress().toString()); - return true; - } else { - Log.e(LOG_TAG, "# Failed to discover my external IP. Reason: " + futureDiscover.failedReason()); - return false; - } - } - - private boolean setupConnectionToRelay() { - PeerNAT peerNat = new PeerBuilderNAT(peerDHT.peer()).start(); - FutureRelayNAT futureRelayNAT = peerNat.startRelay(new TCPRelayClientConfig(), bootstrapPeerAddress).awaitUninterruptibly(); - if (futureRelayNAT.isSuccess()) { - Log.i(LOG_TAG, "# Successfully connected to relay node."); - return true; - } else { - Log.e(LOG_TAG, "# Cannot connect to relay node. Reason: " + futureRelayNAT.failedReason()); - return false; - } - } - - private void setReceiveHandler() { - AppHelper.getPeerDHT().peer().objectDataReply((s, r) -> { - Log.i(LOG_TAG, "# Incoming message: " + r); - AppHelper.getObservable().notifyNetworkObservers(r); - return null; - }); - } - @Override - public void shutdownPeer() { - new Thread(() -> { - if(replication != null) { - replication.shutdown(); + public void startService() { + context.startService(new Intent(context, XMPPConnectionService.class)); + ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + XMPPConnectionService.XMPPServiceBinder binder = (XMPPConnectionService.XMPPServiceBinder) service; + AppHelper.setXmppConnection(binder.getConnection()); } - if(peerDHT != null) { - peerDHT.peer().announceShutdown().start().awaitUninterruptibly(); - peerDHT.peer().shutdown().awaitUninterruptibly(); + + @Override + public void onServiceDisconnected(ComponentName name) { + AppHelper.setXmppConnection(null); } - storage.close(); - System.exit(0); - }).start(); + }; + context.bindService(new Intent(context, XMPPConnectionService.class), connection,Context.BIND_AUTO_CREATE); } - - private boolean checkFirstRun() { - if (preferences.getBoolean("firstRun", true)) { - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean("firstRun", false); - editor.apply(); - return true; - } - return false; - } - - private void publicProfileToDHT() { - KeyPair mainKeyPair = keyPairManager.openMainKeyPair(); - PublicUserProfile userProfile = new PublicUserProfile(AppHelper.getUsername(), peerDHT.peerAddress()); - Data serializedUserProfile = null; - try { - serializedUserProfile = new Data(gson.toJson(userProfile)) - .protectEntry(mainKeyPair.getPrivate()); - } catch (IOException e) { - e.printStackTrace(); - } - Log.i(LOG_TAG, P2PUtils.put(AppHelper.getPeerID() + "_profile", null, serializedUserProfile, mainKeyPair) ? "# Profile successfully published!" : "# Profile publishing failed!"); - } - - private ChannelClientConfiguration createChannelClientConfig() { - ChannelClientConfiguration channelClientConfiguration = new ChannelClientConfiguration(); - channelClientConfiguration.bindings(new Bindings()); - channelClientConfiguration.maxPermitsPermanentTCP(250); - channelClientConfiguration.maxPermitsTCP(250); - channelClientConfiguration.maxPermitsUDP(250); - channelClientConfiguration.pipelineFilter(new PeerBuilder.DefaultPipelineFilter()); - channelClientConfiguration.signatureFactory(new RSASignatureFactory()); - channelClientConfiguration.senderTCP((new InetSocketAddress(0)).getAddress()); - channelClientConfiguration.senderUDP((new InetSocketAddress(0)).getAddress()); - channelClientConfiguration.byteBufPool(false); - return channelClientConfiguration; - } - - private ChannelServerConfiguration createChannelServerConfig() { - ChannelServerConfiguration channelServerConfiguration = new ChannelServerConfiguration(); - channelServerConfiguration.bindings(new Bindings()); - //these two values may be overwritten in the peer builder - channelServerConfiguration.ports(new Ports(Ports.DEFAULT_PORT, Ports.DEFAULT_PORT)); - channelServerConfiguration.portsForwarding(new Ports(Ports.DEFAULT_PORT, Ports.DEFAULT_PORT)); - channelServerConfiguration.behindFirewall(false); - channelServerConfiguration.pipelineFilter(new PeerBuilder.DefaultPipelineFilter()); - channelServerConfiguration.signatureFactory(new RSASignatureFactory()); - channelServerConfiguration.byteBufPool(false); - return channelServerConfiguration; - } - - @Override - public void sendStartChatMessage(@NotNull String username) { - if(AppHelper.getPeerDHT() == null) { - ObservableUtils.notifyUI(UIActions.NODE_IS_OFFLINE); - return; - } - - String companionPeerID = getPeerIDByUsername(username); - if(companionPeerID == null) { - ObservableUtils.notifyUI(UIActions.PEER_NOT_EXIST); - return; - } - PublicUserProfile recipientPublicProfile = getPublicProfile(companionPeerID); - if(recipientPublicProfile == null) { - ObservableUtils.notifyUI(UIActions.PEER_NOT_EXIST); - return; - } - - NewChatRequestMessage newChatRequestMessage = new NewChatRequestMessage(UUID.randomUUID().toString(), UUID.randomUUID().toString(), AppHelper.getPeerID(), AppHelper.getUsername(), System.currentTimeMillis(), 0); - try { - if(P2PUtils.put(companionPeerID + "_pendingChats", newChatRequestMessage.getChatID(), new Data(gson.toJson(newChatRequestMessage)))) { - Log.i(LOG_TAG, "# Create new offline chat request is successful! ChatID: " + newChatRequestMessage.getChatID()); - } else { - Log.e(LOG_TAG, "# Failed to create offline chat request. ChatID: " + newChatRequestMessage.getChatID()); - } - } catch (IOException e) { - e.printStackTrace(); - } - - ArrayList admins = new ArrayList<>(); - admins.add(AppHelper.getPeerID()); - Data data = null; - try { - 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(), username, newChatRequestMessage.getChatID() + "_metadata", newChatRequestMessage.getChatID() + "_members", 0); - ObservableUtils.notifyUI(UIActions.NEW_CHAT); - } - - private PublicUserProfile getPublicProfile(String peerID) { - PublicUserProfile publicProfile = null; - Map data = P2PUtils.get(peerID + "_profile"); - if (data != null && data.size() == 1) { - try { - publicProfile = gson.fromJson((String) data.values().iterator().next().object(), PublicUserProfile.class); - } catch (ClassNotFoundException | IOException e) { - e.printStackTrace(); - } - return publicProfile; - } - return null; - } - - private String getPeerIDByUsername(String username) { - Map usernameMap = P2PUtils.get(username); - if(usernameMap == null) { - return null; - } - try { - return (String) usernameMap.values().iterator().next().object(); - } catch (ClassNotFoundException | IOException e) { - e.printStackTrace(); - } - return null; - }*/ } diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt b/app/src/main/java/io/github/chronosx88/influence/logic/SettingsLogic.kt index f24f618..ebb2dca 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 @@ -4,49 +4,8 @@ import io.github.chronosx88.influence.contracts.CoreContracts import io.github.chronosx88.influence.helpers.KeyPairManager class SettingsLogic : CoreContracts.ISettingsLogic { - - override fun checkUsernameExists(username: String) : Boolean { - /*if (AppHelper.getPeerDHT() == null) { - ObservableUtils.notifyUI(UIActions.NODE_IS_OFFLINE) - return false - } - val usernameMap: MutableMap? = P2PUtils.get(username) - usernameMap ?: return false - return true*/ - return true - } - companion object { private val LOG_TAG: String = "SettingsLogic" private val keyPairManager = KeyPairManager() - - /*fun publishUsername(oldUsername: String?, username: String?) { - if (AppHelper.getPeerDHT() == null) { - ObservableUtils.notifyUI(UIActions.NODE_IS_OFFLINE) - return - } - val mainKeyPair = keyPairManager.openMainKeyPair() - oldUsername?.let { - if(!oldUsername.equals("")) { - P2PUtils.remove(oldUsername, null, mainKeyPair) - } - } - - username?.let { - 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!") - } ?: run { - return - } - }*/ } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/models/GenericDialog.java b/app/src/main/java/io/github/chronosx88/influence/models/GenericDialog.java index fd228b1..ac6cba8 100644 --- a/app/src/main/java/io/github/chronosx88/influence/models/GenericDialog.java +++ b/app/src/main/java/io/github/chronosx88/influence/models/GenericDialog.java @@ -28,7 +28,7 @@ import io.github.chronosx88.influence.models.roomEntities.ChatEntity; public class GenericDialog implements IDialog { private String dialogID; - private String dialogPhoto = ""; + private String dialogPhoto; private String dialogName; private List users; private IMessage lastMessage; @@ -36,6 +36,7 @@ public class GenericDialog implements IDialog { public GenericDialog(ChatEntity chatEntity) { dialogID = chatEntity.jid; + dialogPhoto = chatEntity.jid; dialogName = chatEntity.chatName; users = new ArrayList<>(); unreadMessagesCount = chatEntity.unreadMessagesCount; diff --git a/app/src/main/java/io/github/chronosx88/influence/models/appEvents/AuthenticationStatusEvent.java b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/AuthenticationStatusEvent.java new file mode 100644 index 0000000..19bb10e --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/AuthenticationStatusEvent.java @@ -0,0 +1,30 @@ +/* + * 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.models.appEvents; + +public class AuthenticationStatusEvent { + public static final int NETWORK_ERROR = 0x0; + public static final int INCORRECT_LOGIN_OR_PASSWORD = 0x1; + public static final int CONNECT_AND_LOGIN_SUCCESSFUL = 0x2; + + public final int authenticationStatus; + + public AuthenticationStatusEvent(int authenticationStatus) { + this.authenticationStatus = authenticationStatus; + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewChatEvent.java b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewChatEvent.java new file mode 100644 index 0000000..1a1b775 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewChatEvent.java @@ -0,0 +1,26 @@ +/* + * 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.models.appEvents; + +public class NewChatEvent { + public final String chatID; + + public NewChatEvent(String chatID) { + this.chatID = chatID; + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewMessageEvent.java b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewMessageEvent.java new file mode 100644 index 0000000..f5e689f --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/NewMessageEvent.java @@ -0,0 +1,28 @@ +/* + * 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.models.appEvents; + +public class NewMessageEvent { + public final String chatID; + public final long messageID; + + public NewMessageEvent(String chatID, long messageID) { + this.chatID = chatID; + this.messageID = messageID; + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/observable/MainObservable.java b/app/src/main/java/io/github/chronosx88/influence/observable/MainObservable.java deleted file mode 100644 index e582c77..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/observable/MainObservable.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.chronosx88.influence.observable; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; - -import io.github.chronosx88.influence.contracts.observer.IObservable; -import io.github.chronosx88.influence.contracts.observer.IObserver; - -public class MainObservable implements IObservable { - private ArrayList uiObservers = new ArrayList<>(); - - @Override - public void register(IObserver observer) { - uiObservers.add(observer); - } - - - @Override - public void unregister(IObserver observer) { - uiObservers.remove(observer); - } - - @Override - public void notifyUIObservers(JSONObject jsonObject) { - for (IObserver observer : uiObservers) { - try { - observer.handleEvent(jsonObject); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt index 224cac5..6b57551 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt @@ -1,37 +1,56 @@ package io.github.chronosx88.influence.presenters -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter +import android.graphics.BitmapFactory import com.google.gson.Gson import com.stfalcon.chatkit.commons.ImageLoader import com.stfalcon.chatkit.messages.MessagesListAdapter 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 import io.github.chronosx88.influence.helpers.LocalDBWrapper import io.github.chronosx88.influence.logic.ChatLogic import io.github.chronosx88.influence.models.GenericMessage +import io.github.chronosx88.influence.models.appEvents.NewMessageEvent import io.github.chronosx88.influence.models.roomEntities.ChatEntity import io.github.chronosx88.influence.models.roomEntities.MessageEntity +import java9.util.concurrent.CompletableFuture +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.jxmpp.jid.EntityBareJid +import org.jxmpp.jid.impl.JidCreate +import org.jxmpp.stringprep.XmppStringprepException class ChatPresenter(private val view: CoreContracts.IChatViewContract, private val chatID: String) : CoreContracts.IChatPresenterContract { private val logic: CoreContracts.IChatLogicContract private val chatEntity: ChatEntity? private val gson: Gson private val chatAdapter: MessagesListAdapter - private lateinit var newMessageReceiver: BroadcastReceiver init { this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!) this.chatEntity = LocalDBWrapper.getChatByChatID(chatID) gson = Gson() - chatAdapter = MessagesListAdapter(AppHelper.getJid(), ImageLoader { imageView, _, _ -> imageView.setImageResource(R.mipmap.ic_launcher) }) - view.setAdapter(chatAdapter) + chatAdapter = MessagesListAdapter(AppHelper.getJid(), ImageLoader { imageView, url, _ -> + imageView.setImageResource(R.mipmap.ic_launcher) + CompletableFuture.supplyAsync { while (AppHelper.getXmppConnection() == null) ; + while (!AppHelper.getXmppConnection().isConnectionAlive) ; + var jid: EntityBareJid? = null + try { + jid = JidCreate.entityBareFrom(url) + } catch (e: XmppStringprepException) { + e.printStackTrace() + } - setupIncomingMessagesReceiver() + AppHelper.getXmppConnection().getAvatar(jid) }.thenAccept { avatarBytes -> AppHelper.getMainUIThread().post { + if (avatarBytes != null) { + val avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.size) + imageView.setImageBitmap(avatar) + } + } + } + }) + view.setAdapter(chatAdapter) + EventBus.getDefault().register(this) } override fun sendMessage(text: String): Boolean { @@ -58,18 +77,11 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v // } - private fun setupIncomingMessagesReceiver() { - newMessageReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if(intent.getStringExtra(XMPPConnectionService.MESSAGE_CHATID).equals(chatEntity!!.jid)) { - val messageID = intent.getLongExtra(XMPPConnectionService.MESSAGE_ID, -1) - chatAdapter.addToStart(GenericMessage(LocalDBWrapper.getMessageByID(messageID)), true) - } - } + @Subscribe + public fun onNewMessage(event: NewMessageEvent) { + if(event.chatID.equals(chatEntity!!.jid)) { + val messageID = event.messageID + chatAdapter.addToStart(GenericMessage(LocalDBWrapper.getMessageByID(messageID)), true) } - val filter = IntentFilter() - filter.addAction(XMPPConnectionService.INTENT_NEW_MESSAGE) - AppHelper.getContext().registerReceiver(newMessageReceiver, filter) } - } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java index e11883c..f22d428 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java @@ -1,38 +1,60 @@ package io.github.chronosx88.influence.presenters; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import androidx.appcompat.app.AlertDialog; import com.stfalcon.chatkit.dialogs.DialogsListAdapter; -import org.json.JSONException; -import org.json.JSONObject; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.jivesoftware.smack.roster.RosterEntry; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; import java.util.ArrayList; +import java.util.Set; import io.github.chronosx88.influence.R; -import io.github.chronosx88.influence.XMPPConnectionService; 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; -import io.github.chronosx88.influence.helpers.ObservableActions; -import io.github.chronosx88.influence.logic.ChatListLogic; +import io.github.chronosx88.influence.logic.DialogListLogic; import io.github.chronosx88.influence.models.GenericDialog; +import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent; +import io.github.chronosx88.influence.models.appEvents.NewChatEvent; +import io.github.chronosx88.influence.models.appEvents.NewMessageEvent; import io.github.chronosx88.influence.views.ChatActivity; import java8.util.stream.StreamSupport; +import java9.util.concurrent.CompletableFuture; -public class DialogListPresenter implements CoreContracts.IDialogListPresenterContract, IObserver { +public class DialogListPresenter implements CoreContracts.IDialogListPresenterContract { private CoreContracts.IChatListViewContract view; private CoreContracts.IDialogListLogicContract logic; private DialogsListAdapter dialogListAdapter = new DialogsListAdapter<>((imageView, url, payload) -> { - imageView.setImageResource(R.mipmap.ic_launcher); // FIXME + imageView.setImageResource(R.mipmap.ic_launcher); + CompletableFuture.supplyAsync(() -> { + while (AppHelper.getXmppConnection() == null); + while (AppHelper.getXmppConnection().isConnectionAlive() != true); + EntityBareJid jid = null; + try { + jid = JidCreate.entityBareFrom(url); + } catch (XmppStringprepException e) { + e.printStackTrace(); + } + return AppHelper.getXmppConnection().getAvatar(jid); + }).thenAccept((avatarBytes) -> { + AppHelper.getMainUIThread().post(() -> { + if(avatarBytes != null) { + Bitmap avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length); + imageView.setImageBitmap(avatar); + } + }); + }); }); - private BroadcastReceiver incomingMessagesReceiver; public DialogListPresenter(CoreContracts.IChatListViewContract view) { this.view = view; @@ -50,14 +72,23 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo builder.setMessage("Remove chat?"); builder.create().show(); }); - this.logic = new ChatListLogic(); + this.logic = new DialogListLogic(); this.view.setDialogAdapter(dialogListAdapter); ArrayList dialogs = new ArrayList<>(); - StreamSupport.stream(logic.loadAllChats()) + StreamSupport.stream(logic.loadLocalChats()) .forEach(chatEntity -> dialogs.add(new GenericDialog(chatEntity))); dialogListAdapter.setItems(dialogs); - setupIncomingMessagesReceiver(); - AppHelper.getObservable().register(this); + loadRemoteContactList(); + } + + @Override + public void onStart() { + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + EventBus.getDefault().unregister(this); } @Override @@ -68,29 +99,36 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo view.startActivity(intent); } - private void setupIncomingMessagesReceiver() { - incomingMessagesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String chatID = intent.getStringExtra(XMPPConnectionService.MESSAGE_CHATID); - GenericDialog dialog = dialogListAdapter.getItemById(chatID); - if(dialog == null) { - dialogListAdapter.addItem(new GenericDialog(LocalDBWrapper.getChatByChatID(chatID))); - } - } - }; - IntentFilter filter = new IntentFilter(); - filter.addAction(XMPPConnectionService.INTENT_NEW_MESSAGE); - AppHelper.getContext().registerReceiver(incomingMessagesReceiver, filter); + @Subscribe + public void onNewChatCreated(NewChatEvent event) { + dialogListAdapter.upsertItem(new GenericDialog(LocalDBWrapper.getChatByChatID(event.chatID))); + } + + @Subscribe + public void onNewMessage(NewMessageEvent event) { + String chatID = event.chatID; + GenericDialog dialog = dialogListAdapter.getItemById(chatID); + if(dialog == null) { + dialogListAdapter.addItem(new GenericDialog(LocalDBWrapper.getChatByChatID(chatID))); + } + } + + @Subscribe + public void onAuthenticate(AuthenticationStatusEvent event) { + if(event.authenticationStatus == AuthenticationStatusEvent.CONNECT_AND_LOGIN_SUCCESSFUL) { + loadRemoteContactList(); + } } @Override - public void handleEvent(JSONObject object) throws JSONException { - switch (object.getInt("action")) { - case ObservableActions.NEW_CHAT_CREATED: { - dialogListAdapter.addItem(new GenericDialog(LocalDBWrapper.getChatByChatID(object.getJSONArray("additional").optString(0)))); - break; + public void loadRemoteContactList() { + CompletableFuture.supplyAsync(() -> logic.getRemoteContacts()).thenAccept((contacts) -> { + if(contacts != null) { + StreamSupport.stream(contacts).forEach(contact -> { + LocalDBWrapper.createChatEntry(contact.getJid().asUnescapedString(), contact.getName() == null ? contact.getJid().asUnescapedString() : contact.getName()); + dialogListAdapter.upsertItem(new GenericDialog(LocalDBWrapper.getChatByChatID(contact.getJid().asUnescapedString()))); + }); } - } + }); } } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.kt b/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.kt index e42c694..4d90b84 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.kt +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/MainPresenter.kt @@ -1,68 +1,41 @@ package io.github.chronosx88.influence.presenters -import android.app.ActivityManager -import android.content.BroadcastReceiver -import android.content.Context import android.content.Intent -import android.content.IntentFilter -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 import io.github.chronosx88.influence.helpers.LocalDBWrapper -import io.github.chronosx88.influence.helpers.ObservableActions -import io.github.chronosx88.influence.helpers.ObservableUtils import io.github.chronosx88.influence.logic.MainLogic +import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent +import io.github.chronosx88.influence.models.appEvents.NewChatEvent import io.github.chronosx88.influence.views.LoginActivity -import org.jetbrains.anko.doAsync +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode class MainPresenter(private val view: CoreContracts.IMainViewContract) : CoreContracts.IMainPresenterContract { private val logic: CoreContracts.IMainLogicContract = MainLogic() - private var broadcastReceiver: BroadcastReceiver? = null + init { + EventBus.getDefault().register(this) + } - override fun initPeer() { - broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - when (action) { - XMPPConnectionService.INTENT_AUTHENTICATED -> { - view.showProgressBar(false) - } - XMPPConnectionService.INTENT_AUTHENTICATION_FAILED -> { - view.showProgressBar(false) - val intent = Intent(AppHelper.getContext(), LoginActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - AppHelper.getContext().startActivity(intent) - } - } - } - } - val filter = IntentFilter() - filter.addAction(XMPPConnectionService.INTENT_AUTHENTICATED) - filter.addAction(XMPPConnectionService.INTENT_AUTHENTICATION_FAILED) - AppHelper.getContext().registerReceiver(broadcastReceiver, filter) - - AppHelper.getContext().startService(Intent(AppHelper.getContext(), XMPPConnectionService::class.java)) + override fun initConnection() { + logic.startService() } override fun startChatWithPeer(username: String) { LocalDBWrapper.createChatEntry(username, username) - ObservableUtils.notifyUI(ObservableActions.NEW_CHAT_CREATED, username) + EventBus.getDefault().post(NewChatEvent(username)) } - override fun onDestroy() { - // - } - - // TODO - private fun isServiceRunning(serviceClass: Class<*>): Boolean { - val manager = AppHelper.getContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - for (service in manager.getRunningServices(Integer.MAX_VALUE)) { - if (serviceClass.name == service.service.className) { - return true + @Subscribe(threadMode = ThreadMode.MAIN) + fun onAuthenticate(event: AuthenticationStatusEvent) { + when(event.authenticationStatus) { + AuthenticationStatusEvent.INCORRECT_LOGIN_OR_PASSWORD -> { + val intent = Intent(AppHelper.getContext(), LoginActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + AppHelper.getContext().startActivity(intent) } } - return false } } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt b/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt index fd9507f..e46acd1 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/SettingsPresenter.kt @@ -8,39 +8,4 @@ import io.github.chronosx88.influence.logic.SettingsLogic class SettingsPresenter(private val view: CoreContracts.ISettingsView) : CoreContracts.ISettingsPresenter { private val mainThreadHandler: Handler = Handler(AppHelper.getContext().mainLooper) private val logic: SettingsLogic = SettingsLogic() - - - override fun updateUsername(username: String) { - /*view.loadingScreen(true) - val editor: SharedPreferences.Editor = AppHelper.getPreferences().edit() - - GlobalScope.launch { - val oldUsername = AppHelper.getPreferences().getString("username", null) - if(username.equals("")) { - SettingsLogic.publishUsername(oldUsername, null) - editor.remove("username") - editor.apply() - AppHelper.updateUsername(null) - ObservableUtils.notifyUI(UIActions.USERNAME_AVAILABLE) - return@launch - } - if(!logic.checkUsernameExists(username)) { - // Save username in SharedPreferences - 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) - } - }*/ - } } \ No newline at end of file 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 index f8e28e5..9c64f37 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/LoginActivity.java +++ b/app/src/main/java/io/github/chronosx88/influence/views/LoginActivity.java @@ -18,10 +18,7 @@ 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; @@ -30,24 +27,40 @@ import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; +import com.google.android.material.textfield.TextInputLayout; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + 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; +import io.github.chronosx88.influence.helpers.HashUtils; +import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent; public class LoginActivity extends AppCompatActivity implements CoreContracts.ILoginViewContract { private EditText jidEditText; private EditText passwordEditText; + private TextInputLayout jidInputLayout; + private TextInputLayout passwordInputLayout; 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); + + jidInputLayout = findViewById(R.id.jid_input_layout); + passwordInputLayout = findViewById(R.id.password_input_layout); + jidInputLayout.setErrorEnabled(true); + passwordInputLayout.setErrorEnabled(true); + signInButton = findViewById(R.id.sign_in_button); progressDialog = new ProgressDialog(LoginActivity.this); progressDialog.setCancelable(false); @@ -68,39 +81,6 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL 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); - jidEditText.setError("Invalid JID/Password/Server"); - 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); @@ -121,7 +101,7 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL jidEditText.setError("Field is required!"); focusView = jidEditText; cancel = true; - } else if (!isEmailValid(jid)) { + } else if (!isJidValid(jid)) { jidEditText.setError("Invalid JID"); focusView = jidEditText; cancel = true; @@ -135,8 +115,8 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL } } - private boolean isEmailValid(String email) { - return email.contains("@"); + private boolean isJidValid(String jid) { + return jid.contains("@"); } private boolean isPasswordValid(String password) { @@ -146,7 +126,7 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL private void saveLoginCredentials() { AppHelper.getPreferences().edit() .putString("jid", jidEditText.getText().toString()) - .putString("pass", passwordEditText.getText().toString()) + .putString("pass", HashUtils.sha1(passwordEditText.getText().toString())) .putBoolean("logged_in", true) .apply(); } @@ -155,4 +135,37 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL loadingScreen(true); startService(new Intent(this, XMPPConnectionService.class)); } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onAuthenticate(AuthenticationStatusEvent event) { + switch (event.authenticationStatus) { + case AuthenticationStatusEvent.CONNECT_AND_LOGIN_SUCCESSFUL: { + loadingScreen(false); + finish(); + break; + } + case AuthenticationStatusEvent.INCORRECT_LOGIN_OR_PASSWORD: { + loadingScreen(false); + passwordInputLayout.setError("Invalid JID/Password"); + break; + } + case AuthenticationStatusEvent.NETWORK_ERROR: { + loadingScreen(false); + jidInputLayout.setError("Network error"); + break; + } + } + } + + @Override + protected void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + protected void onStop() { + EventBus.getDefault().unregister(this); + super.onStop(); + } } 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 60708b0..5b6d1cc 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 @@ -18,20 +18,15 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import org.jetbrains.annotations.NotNull; -import org.json.JSONException; -import org.json.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.ObservableActions; import io.github.chronosx88.influence.presenters.MainPresenter; import io.github.chronosx88.influence.views.fragments.DialogListFragment; import io.github.chronosx88.influence.views.fragments.SettingsFragment; import kotlin.Pair; -public class MainActivity extends AppCompatActivity implements CoreContracts.IMainViewContract, IObserver { +public class MainActivity extends AppCompatActivity implements CoreContracts.IMainViewContract { private CoreContracts.IMainPresenterContract presenter; private ProgressDialog progressDialog; @@ -84,15 +79,7 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa progressDialog = new ProgressDialog(MainActivity.this, R.style.AlertDialogTheme); progressDialog.setCancelable(false); progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small); - progressDialog.show(); - presenter.initPeer(); - AppHelper.getObservable().register(this); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - presenter.onDestroy(); + presenter.initConnection(); } @Override @@ -105,7 +92,7 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa public boolean onOptionsItemSelected(MenuItem item) { if(item.getItemId() == R.id.action_reconnect_network) { progressDialog.show(); - presenter.initPeer(); + presenter.initConnection(); } return true; } @@ -138,14 +125,4 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa return rootView; } - - @Override - public void handleEvent(JSONObject object) throws JSONException { - switch (object.getInt("action")) { - case ObservableActions.NEW_CHAT_CREATED: { - showProgressBar(false); - break; - } - } - } } diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/DialogListFragment.kt b/app/src/main/java/io/github/chronosx88/influence/views/fragments/DialogListFragment.kt index cc04adf..f962f12 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/fragments/DialogListFragment.kt +++ b/app/src/main/java/io/github/chronosx88/influence/views/fragments/DialogListFragment.kt @@ -15,8 +15,8 @@ import io.github.chronosx88.influence.presenters.DialogListPresenter class DialogListFragment : Fragment(), CoreContracts.IChatListViewContract { - private var presenter: CoreContracts.IDialogListPresenterContract? = null - private var dialogList: DialogsList? = null + private lateinit var presenter: CoreContracts.IDialogListPresenterContract + private lateinit var dialogList: DialogsList override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -30,10 +30,20 @@ class DialogListFragment : Fragment(), CoreContracts.IChatListViewContract { } override fun setDialogAdapter(adapter: DialogsListAdapter) { - dialogList!!.setAdapter(adapter) + dialogList.setAdapter(adapter) } override fun getActivityContext(): Context? { return context } + + override fun onStart() { + super.onStart() + presenter.onStart() + } + + override fun onStop() { + presenter.onStop() + super.onStop() + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java b/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java index ee32987..4df3af2 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java +++ b/app/src/main/java/io/github/chronosx88/influence/views/fragments/SettingsFragment.java @@ -1,20 +1,12 @@ 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 androidx.preference.PreferenceScreen; -import androidx.recyclerview.widget.RecyclerView; + +import org.jetbrains.annotations.NotNull; import io.github.chronosx88.influence.R; import io.github.chronosx88.influence.contracts.CoreContracts; @@ -31,25 +23,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements CoreCo 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()); - getPreferenceScreen().getPreference(0).setOnPreferenceClickListener((preference -> { - ClipboardManager clipboard = (ClipboardManager) AppHelper.getActivityContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("", AppHelper.getPeerID()); - clipboard.setPrimaryClip(clip); - Toast.makeText(AppHelper.getActivityContext(), "Скопировано в буфер обмена!", Toast.LENGTH_SHORT).show(); - return false; - })); - getPreferenceScreen().getPreference(1).setSummary(AppHelper.getUsername()); - getPreferenceScreen().getPreference(1).setOnPreferenceClickListener((v) -> { - setupUsernameEditDialog().show(); - return true; - }); - getPreferenceScreen().getPreference(1).setOnPreferenceChangeListener((p, nV) -> { - getPreferenceScreen().getPreference(1).setSummary((String) nV); - return true; - });*/ } @Override @@ -64,29 +38,4 @@ public class SettingsFragment extends PreferenceFragmentCompat implements CoreCo 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); - input.setText(AppHelper.getPreferences().getString("username", null)); - - 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; - } - - @Override - public void refreshScreen() { - getPreferenceScreen().getPreference(1).callChangeListener(AppHelper.getPreferences().getString("username", null)); - } - } diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index f3ea72f..9749ab7 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -17,7 +17,6 @@ --> @@ -40,6 +40,7 @@ diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml index d01b341..dfc2102 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/main/res/xml/main_settings.xml @@ -1,22 +1,3 @@ - - - - - \ No newline at end of file