mirror of
https://github.com/ChronosX88/Influence-P2P.git
synced 2024-11-22 15:22:18 +00:00
Moved interclass communication to EventBus library from greenrobot, implemented loading roster entries and user avatars. Refactored service system
This commit is contained in:
parent
6241ad8cb9
commit
0264e87296
8
LICENSE
8
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.
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 = "";
|
||||
}
|
||||
|
@ -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<RosterEntry> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<ChatEntity>
|
||||
fun loadLocalChats(): List<ChatEntity>
|
||||
fun getRemoteContacts(): Set<RosterEntry>?
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
package io.github.chronosx88.influence.contracts.observer;
|
||||
|
||||
public interface INetworkObserver {
|
||||
void handleEvent(Object object);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<ChatEntity> loadAllChats() {
|
||||
return AppHelper.getChatDB().chatDao().getAllChats();
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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<ChatEntity> loadLocalChats() {
|
||||
return AppHelper.getChatDB().chatDao().getAllChats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RosterEntry> getRemoteContacts() {
|
||||
if(AppHelper.getXmppConnection() != null) {
|
||||
return AppHelper.getXmppConnection().getContactList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -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<String> 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<Number640, Data> 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<Number640, Data> usernameMap = P2PUtils.get(username);
|
||||
if(usernameMap == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (String) usernameMap.values().iterator().next().object();
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
}
|
||||
|
@ -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<Number640, Data>? = 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
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -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<GenericUser> 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;
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.chronosx88.influence.models.appEvents;
|
||||
|
||||
public class NewChatEvent {
|
||||
public final String chatID;
|
||||
|
||||
public NewChatEvent(String chatID) {
|
||||
this.chatID = chatID;
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<IObserver> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<GenericMessage>
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<GenericDialog> 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<GenericDialog> 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())));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<GenericDialog>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -25,6 +24,7 @@
|
||||
tools:context=".views.LoginActivity">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/jid_input_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/password_input_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
@ -1,22 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<Preference
|
||||
android:key="peerID"
|
||||
android:title="Мой Peer ID"
|
||||
android:summary=""/>
|
||||
<Preference
|
||||
android:key="username"
|
||||
android:title="Мой username"
|
||||
android:summary=""/>
|
||||
<EditTextPreference
|
||||
android:key="bootstrapAddress"
|
||||
android:title="Адрес Bootstrap-ноды"
|
||||
android:summary="Bootstrap-нода необходима для подключения к сети" />
|
||||
<CheckBoxPreference
|
||||
android:key="allowWorkBackground"
|
||||
android:title="Работать в фоне (not implemented)"
|
||||
android:summary="Позволять узлу работать в фоне (может повысить потребление трафика и батареи)"/>
|
||||
<Preference
|
||||
android:key="exportEncryptionKeys"
|
||||
android:title="Экспортировать ключи шифрования (not implemented)" />
|
||||
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user