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
|
in a country, would infringe one or more identifiable patents in that
|
||||||
country that you have reason to believe are valid.
|
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
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
covered work, and grant a patent license to some of the parties
|
covered work, and grant a patent license to some of the parties
|
||||||
receiving the covered work authorizing them to use, propagate, modify
|
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
|
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
|
the work, and under which the third party grants, to any of the
|
||||||
parties who would receive the covered work from you, a discriminatory
|
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
|
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,
|
contain the covered work, unless you entered into that arrangement,
|
||||||
or that patent license was granted, prior to 28 March 2007.
|
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
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
above cannot be given local legal effect according to their terms,
|
above cannot be given local legal effect according to their terms,
|
||||||
reviewing courts shall apply local law that most closely approximates
|
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
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
copy of the Program in return for a fee.
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
@ -63,7 +63,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.github.stfalcon:chatkit:0.3.3'
|
implementation 'com.github.stfalcon:chatkit:0.3.3'
|
||||||
implementation 'net.sourceforge.streamsupport:streamsupport:1.7.0'
|
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 {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
package io.github.chronosx88.influence;
|
package io.github.chronosx88.influence;
|
||||||
|
|
||||||
public class LoginCredentials {
|
public class LoginCredentials {
|
||||||
String username = "";
|
public String username = "";
|
||||||
String password = "";
|
public String password = "";
|
||||||
String jabberHost = "";
|
public String jabberHost = "";
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
|
|
||||||
package io.github.chronosx88.influence;
|
package io.github.chronosx88.influence;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||||
import org.jivesoftware.smack.ConnectionListener;
|
import org.jivesoftware.smack.ConnectionListener;
|
||||||
import org.jivesoftware.smack.ReconnectionManager;
|
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.Chat;
|
||||||
import org.jivesoftware.smack.chat2.ChatManager;
|
import org.jivesoftware.smack.chat2.ChatManager;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
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.XMPPTCPConnection;
|
||||||
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
||||||
|
import org.jivesoftware.smackx.vcardtemp.VCardManager;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||||
import io.github.chronosx88.influence.helpers.NetworkHandler;
|
import io.github.chronosx88.influence.helpers.NetworkHandler;
|
||||||
|
import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent;
|
||||||
|
|
||||||
public class XMPPConnection implements ConnectionListener {
|
public class XMPPConnection implements ConnectionListener {
|
||||||
private final static String LOG_TAG = "XMPPConnection";
|
private final static String LOG_TAG = "XMPPConnection";
|
||||||
@ -51,8 +52,8 @@ public class XMPPConnection implements ConnectionListener {
|
|||||||
private XMPPTCPConnection connection = null;
|
private XMPPTCPConnection connection = null;
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
private NetworkHandler networkHandler;
|
private NetworkHandler networkHandler;
|
||||||
private BroadcastReceiver sendMessageReceiver = null;
|
|
||||||
private Context context;
|
private Context context;
|
||||||
|
private Roster roster;
|
||||||
|
|
||||||
public enum ConnectionState {
|
public enum ConnectionState {
|
||||||
CONNECTED,
|
CONNECTED,
|
||||||
@ -76,10 +77,10 @@ public class XMPPConnection implements ConnectionListener {
|
|||||||
credentials.jabberHost = jabberHost;
|
credentials.jabberHost = jabberHost;
|
||||||
credentials.password = password;
|
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) {
|
if(connection == null) {
|
||||||
XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
|
XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
|
||||||
.setXmppDomain(credentials.jabberHost)
|
.setXmppDomain(credentials.jabberHost)
|
||||||
@ -90,8 +91,6 @@ public class XMPPConnection implements ConnectionListener {
|
|||||||
.setCompressionEnabled(true)
|
.setCompressionEnabled(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
setupSendMessageReceiver();
|
|
||||||
|
|
||||||
connection = new XMPPTCPConnection(conf);
|
connection = new XMPPTCPConnection(conf);
|
||||||
connection.addConnectionListener(this);
|
connection.addConnectionListener(this);
|
||||||
if(credentials.jabberHost.equals("") && credentials.password.equals("") && credentials.username.equals("")){
|
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 reconnectionManager = ReconnectionManager.getInstanceFor(connection);
|
||||||
ReconnectionManager.setEnabledPerDefault(true);
|
ReconnectionManager.setEnabledPerDefault(true);
|
||||||
reconnectionManager.enableAutomaticReconnection();
|
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) {
|
public void authenticated(org.jivesoftware.smack.XMPPConnection connection, boolean resumed) {
|
||||||
XMPPConnectionService.SESSION_STATE = SessionState.LOGGED_IN;
|
XMPPConnectionService.SESSION_STATE = SessionState.LOGGED_IN;
|
||||||
prefs.edit().putBoolean("logged_in", true).apply();
|
prefs.edit().putBoolean("logged_in", true).apply();
|
||||||
context.sendBroadcast(new Intent(XMPPConnectionService.INTENT_AUTHENTICATED));
|
EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.CONNECT_AND_LOGIN_SUCCESSFUL));
|
||||||
AppHelper.setJid(credentials.username + "@" + credentials.jabberHost);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -148,36 +147,10 @@ public class XMPPConnection implements ConnectionListener {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupSendMessageReceiver() {
|
public void sendMessage(EntityBareJid recipientJid, String messageText) {
|
||||||
sendMessageReceiver = new BroadcastReceiver() {
|
Chat chat = ChatManager.getInstanceFor(connection).chatWith(recipientJid);
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
switch (action) {
|
|
||||||
case XMPPConnectionService.INTENT_SEND_MESSAGE: {
|
|
||||||
String recipientJid = intent.getStringExtra(XMPPConnectionService.MESSAGE_RECIPIENT);
|
|
||||||
String messageText = intent.getStringExtra(XMPPConnectionService.MESSAGE_BODY);
|
|
||||||
sendMessage(recipientJid, messageText);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(XMPPConnectionService.INTENT_SEND_MESSAGE);
|
|
||||||
context.registerReceiver(sendMessageReceiver, intentFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMessage(String recipientJid, String messageText) {
|
|
||||||
EntityBareJid jid = null;
|
|
||||||
try {
|
try {
|
||||||
jid = JidCreate.entityBareFrom(recipientJid);
|
Message message = new Message(recipientJid, Message.Type.chat);
|
||||||
} catch (XmppStringprepException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Chat chat = ChatManager.getInstanceFor(connection).chatWith(jid);
|
|
||||||
try {
|
|
||||||
Message message = new Message(jid, Message.Type.chat);
|
|
||||||
message.setBody(messageText);
|
message.setBody(messageText);
|
||||||
chat.send(message);
|
chat.send(message);
|
||||||
} catch (SmackException.NotConnectedException e) {
|
} catch (SmackException.NotConnectedException e) {
|
||||||
@ -190,4 +163,40 @@ public class XMPPConnection implements ConnectionListener {
|
|||||||
public XMPPTCPConnection getConnection() {
|
public XMPPTCPConnection getConnection() {
|
||||||
return connection;
|
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.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||||
|
import io.github.chronosx88.influence.models.appEvents.AuthenticationStatusEvent;
|
||||||
|
|
||||||
public class XMPPConnectionService extends Service {
|
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.ConnectionState CONNECTION_STATE = XMPPConnection.ConnectionState.DISCONNECTED;
|
||||||
public static XMPPConnection.SessionState SESSION_STATE = XMPPConnection.SessionState.LOGGED_OUT;
|
public static XMPPConnection.SessionState SESSION_STATE = XMPPConnection.SessionState.LOGGED_OUT;
|
||||||
|
|
||||||
@ -50,11 +43,14 @@ public class XMPPConnectionService extends Service {
|
|||||||
private boolean isThreadAlive = false;
|
private boolean isThreadAlive = false;
|
||||||
private XMPPConnection connection;
|
private XMPPConnection connection;
|
||||||
private Context context = AppHelper.getContext();
|
private Context context = AppHelper.getContext();
|
||||||
|
private XMPPServiceBinder binder = new XMPPServiceBinder();
|
||||||
|
|
||||||
public XMPPConnectionService() { }
|
public XMPPConnectionService() { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) { return null; }
|
public IBinder onBind(Intent intent) {
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
public void onServiceStart() {
|
public void onServiceStart() {
|
||||||
if(!isThreadAlive)
|
if(!isThreadAlive)
|
||||||
@ -77,6 +73,7 @@ public class XMPPConnectionService extends Service {
|
|||||||
threadHandler.post(() -> {
|
threadHandler.post(() -> {
|
||||||
if(connection != null) {
|
if(connection != null) {
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
|
connection = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -87,9 +84,12 @@ public class XMPPConnectionService extends Service {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
connection.connect();
|
connection.connect();
|
||||||
} catch (IOException | SmackException | XMPPException e) {
|
} catch (IOException | SmackException e) {
|
||||||
Intent intent = new Intent(INTENT_AUTHENTICATION_FAILED);
|
EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.NETWORK_ERROR));
|
||||||
context.sendBroadcast(intent);
|
e.printStackTrace();
|
||||||
|
stopSelf();
|
||||||
|
} catch (XMPPException e) {
|
||||||
|
EventBus.getDefault().post(new AuthenticationStatusEvent(AuthenticationStatusEvent.INCORRECT_LOGIN_OR_PASSWORD));
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
@ -106,4 +106,10 @@ public class XMPPConnectionService extends Service {
|
|||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
onServiceStop();
|
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.GenericMessage
|
||||||
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
import io.github.chronosx88.influence.models.roomEntities.ChatEntity
|
||||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
||||||
|
import org.jivesoftware.smack.roster.RosterEntry
|
||||||
|
|
||||||
interface CoreContracts {
|
interface CoreContracts {
|
||||||
|
|
||||||
interface ViewWithLoadingScreen {
|
interface IGenericView {
|
||||||
fun loadingScreen(state: Boolean);
|
fun loadingScreen(state: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----ChatList-----
|
// -----ChatList-----
|
||||||
|
|
||||||
interface IDialogListLogicContract {
|
interface IDialogListLogicContract {
|
||||||
fun loadAllChats(): List<ChatEntity>
|
fun loadLocalChats(): List<ChatEntity>
|
||||||
|
fun getRemoteContacts(): Set<RosterEntry>?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDialogListPresenterContract {
|
interface IDialogListPresenterContract {
|
||||||
fun openChat(chatID: String)
|
fun openChat(chatID: String)
|
||||||
|
fun onStart()
|
||||||
|
fun onStop()
|
||||||
|
fun loadRemoteContactList()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IChatListViewContract {
|
interface IChatListViewContract {
|
||||||
@ -35,12 +40,12 @@ interface CoreContracts {
|
|||||||
// -----MainActivity-----
|
// -----MainActivity-----
|
||||||
|
|
||||||
interface IMainLogicContract {
|
interface IMainLogicContract {
|
||||||
|
fun startService()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMainPresenterContract {
|
interface IMainPresenterContract {
|
||||||
fun initPeer()
|
fun initConnection()
|
||||||
fun startChatWithPeer(username: String)
|
fun startChatWithPeer(username: String)
|
||||||
fun onDestroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMainViewContract {
|
interface IMainViewContract {
|
||||||
@ -51,7 +56,7 @@ interface CoreContracts {
|
|||||||
// -----ChatActivity-----
|
// -----ChatActivity-----
|
||||||
|
|
||||||
interface IChatLogicContract {
|
interface IChatLogicContract {
|
||||||
fun sendMessage(text: String): MessageEntity
|
fun sendMessage(text: String): MessageEntity?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IChatPresenterContract {
|
interface IChatPresenterContract {
|
||||||
@ -66,20 +71,15 @@ interface CoreContracts {
|
|||||||
|
|
||||||
// -----SettingsFragment-----
|
// -----SettingsFragment-----
|
||||||
|
|
||||||
interface ISettingsLogic {
|
interface ISettingsLogic // TODO
|
||||||
fun checkUsernameExists(username: String) : Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISettingsPresenter {
|
interface ISettingsPresenter // TODO
|
||||||
fun updateUsername(username: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISettingsView {
|
interface ISettingsView {
|
||||||
fun loadingScreen(state: Boolean)
|
fun loadingScreen(state: Boolean)
|
||||||
fun showMessage(message: String)
|
fun showMessage(message: String)
|
||||||
fun refreshScreen()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----LoginActivity-----
|
// -----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.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import androidx.multidex.MultiDexApplication;
|
import androidx.multidex.MultiDexApplication;
|
||||||
@ -12,45 +14,39 @@ import com.instacart.library.truetime.TrueTime;
|
|||||||
|
|
||||||
import java.io.IOException;
|
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.
|
* Extended Application class which designed for centralized getting various objects from anywhere in the application.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class AppHelper extends MultiDexApplication {
|
public class AppHelper extends MultiDexApplication {
|
||||||
private static Application instance;
|
private static Application instance;
|
||||||
private static MainObservable observable;
|
|
||||||
public final static String APP_NAME = "Influence";
|
public final static String APP_NAME = "Influence";
|
||||||
|
|
||||||
private static String jid;
|
private static String jid;
|
||||||
private static RoomHelper chatDB;
|
private static RoomHelper chatDB;
|
||||||
private static SharedPreferences preferences;
|
private static SharedPreferences preferences;
|
||||||
|
private static XMPPConnection xmppConnection;
|
||||||
|
private static LoginCredentials currentLoginCredentials;
|
||||||
|
private static Handler mainUIThreadHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
instance = this;
|
instance = this;
|
||||||
observable = new MainObservable();
|
|
||||||
chatDB = Room.databaseBuilder(getApplicationContext(), RoomHelper.class, "chatDB")
|
mainUIThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
.allowMainThreadQueries()
|
initChatDB();
|
||||||
.build();
|
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(instance);
|
preferences = PreferenceManager.getDefaultSharedPreferences(instance);
|
||||||
new Thread(() -> {
|
initTrueTime();
|
||||||
try {
|
loadLoginCredentials();
|
||||||
TrueTime.build().initialize();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Context getContext() {
|
public static Context getContext() {
|
||||||
return instance.getApplicationContext();
|
return instance.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MainObservable getObservable() { return observable; }
|
|
||||||
|
|
||||||
public static String getJid() { return jid; }
|
public static String getJid() { return jid; }
|
||||||
|
|
||||||
public static void setJid(String jid1) { jid = jid1; }
|
public static void setJid(String jid1) { jid = jid1; }
|
||||||
@ -60,4 +56,50 @@ public class AppHelper extends MultiDexApplication {
|
|||||||
public static SharedPreferences getPreferences() {
|
public static SharedPreferences getPreferences() {
|
||||||
return preferences;
|
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;
|
package io.github.chronosx88.influence.helpers;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.instacart.library.truetime.TrueTime;
|
import com.instacart.library.truetime.TrueTime;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.jivesoftware.smack.chat2.Chat;
|
import org.jivesoftware.smack.chat2.Chat;
|
||||||
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.XMPPConnectionService;
|
import io.github.chronosx88.influence.models.appEvents.NewMessageEvent;
|
||||||
|
|
||||||
public class NetworkHandler implements IncomingChatMessageListener {
|
public class NetworkHandler implements IncomingChatMessageListener {
|
||||||
private final static String LOG_TAG = "NetworkHandler";
|
private final static String LOG_TAG = "NetworkHandler";
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public NetworkHandler(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
|
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());
|
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);
|
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());
|
EventBus.getDefault().post(new NewMessageEvent(chat.getXmppAddressOfChatPartner().toString(), messageID));
|
||||||
intent.putExtra(XMPPConnectionService.MESSAGE_CHATID, chat.getXmppAddressOfChatPartner().toString());
|
|
||||||
intent.putExtra(XMPPConnectionService.MESSAGE_ID, messageID);
|
|
||||||
context.sendBroadcast(intent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
package io.github.chronosx88.influence.logic;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.instacart.library.truetime.TrueTime;
|
import com.instacart.library.truetime.TrueTime;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.XMPPConnection;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import io.github.chronosx88.influence.XMPPConnectionService;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||||
@ -25,11 +25,14 @@ public class ChatLogic implements CoreContracts.IChatLogicContract {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageEntity sendMessage(String text) {
|
public MessageEntity sendMessage(String text) {
|
||||||
if (XMPPConnectionService.CONNECTION_STATE.equals(XMPPConnection.ConnectionState.CONNECTED)) {
|
if (AppHelper.getXmppConnection().isConnectionAlive()) {
|
||||||
Intent intent = new Intent(XMPPConnectionService.INTENT_SEND_MESSAGE);
|
EntityBareJid jid;
|
||||||
intent.putExtra(XMPPConnectionService.MESSAGE_BODY, text);
|
try {
|
||||||
intent.putExtra(XMPPConnectionService.MESSAGE_RECIPIENT, chatEntity.jid);
|
jid = JidCreate.entityBareFrom(chatEntity.jid);
|
||||||
AppHelper.getContext().sendBroadcast(intent);
|
} catch (XmppStringprepException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AppHelper.getXmppConnection().sendMessage(jid, text);
|
||||||
long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, false, false);
|
long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, false, false);
|
||||||
return LocalDBWrapper.getMessageByID(messageID);
|
return LocalDBWrapper.getMessageByID(messageID);
|
||||||
} else {
|
} 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;
|
package io.github.chronosx88.influence.logic;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
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.contracts.CoreContracts;
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||||
import io.github.chronosx88.influence.helpers.KeyPairManager;
|
|
||||||
|
|
||||||
public class MainLogic implements CoreContracts.IMainLogicContract {
|
public class MainLogic implements CoreContracts.IMainLogicContract {
|
||||||
private static final String LOG_TAG = MainLogic.class.getName();
|
private static final String LOG_TAG = MainLogic.class.getName();
|
||||||
@ -17,294 +19,21 @@ public class MainLogic implements CoreContracts.IMainLogicContract {
|
|||||||
this.context = AppHelper.getContext();
|
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
|
@Override
|
||||||
public void shutdownPeer() {
|
public void startService() {
|
||||||
new Thread(() -> {
|
context.startService(new Intent(context, XMPPConnectionService.class));
|
||||||
if(replication != null) {
|
ServiceConnection connection = new ServiceConnection() {
|
||||||
replication.shutdown();
|
@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();
|
@Override
|
||||||
peerDHT.peer().shutdown().awaitUninterruptibly();
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
AppHelper.setXmppConnection(null);
|
||||||
}
|
}
|
||||||
storage.close();
|
};
|
||||||
System.exit(0);
|
context.bindService(new Intent(context, XMPPConnectionService.class), connection,Context.BIND_AUTO_CREATE);
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
import io.github.chronosx88.influence.helpers.KeyPairManager
|
||||||
|
|
||||||
class SettingsLogic : CoreContracts.ISettingsLogic {
|
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 {
|
companion object {
|
||||||
private val LOG_TAG: String = "SettingsLogic"
|
private val LOG_TAG: String = "SettingsLogic"
|
||||||
private val keyPairManager = KeyPairManager()
|
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 {
|
public class GenericDialog implements IDialog {
|
||||||
private String dialogID;
|
private String dialogID;
|
||||||
private String dialogPhoto = "";
|
private String dialogPhoto;
|
||||||
private String dialogName;
|
private String dialogName;
|
||||||
private List<GenericUser> users;
|
private List<GenericUser> users;
|
||||||
private IMessage lastMessage;
|
private IMessage lastMessage;
|
||||||
@ -36,6 +36,7 @@ public class GenericDialog implements IDialog {
|
|||||||
|
|
||||||
public GenericDialog(ChatEntity chatEntity) {
|
public GenericDialog(ChatEntity chatEntity) {
|
||||||
dialogID = chatEntity.jid;
|
dialogID = chatEntity.jid;
|
||||||
|
dialogPhoto = chatEntity.jid;
|
||||||
dialogName = chatEntity.chatName;
|
dialogName = chatEntity.chatName;
|
||||||
users = new ArrayList<>();
|
users = new ArrayList<>();
|
||||||
unreadMessagesCount = chatEntity.unreadMessagesCount;
|
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
|
package io.github.chronosx88.influence.presenters
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.graphics.BitmapFactory
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.stfalcon.chatkit.commons.ImageLoader
|
import com.stfalcon.chatkit.commons.ImageLoader
|
||||||
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
||||||
import io.github.chronosx88.influence.R
|
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.CoreContracts
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper
|
import io.github.chronosx88.influence.helpers.AppHelper
|
||||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper
|
import io.github.chronosx88.influence.helpers.LocalDBWrapper
|
||||||
import io.github.chronosx88.influence.logic.ChatLogic
|
import io.github.chronosx88.influence.logic.ChatLogic
|
||||||
import io.github.chronosx88.influence.models.GenericMessage
|
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.ChatEntity
|
||||||
import io.github.chronosx88.influence.models.roomEntities.MessageEntity
|
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 {
|
class ChatPresenter(private val view: CoreContracts.IChatViewContract, private val chatID: String) : CoreContracts.IChatPresenterContract {
|
||||||
private val logic: CoreContracts.IChatLogicContract
|
private val logic: CoreContracts.IChatLogicContract
|
||||||
private val chatEntity: ChatEntity?
|
private val chatEntity: ChatEntity?
|
||||||
private val gson: Gson
|
private val gson: Gson
|
||||||
private val chatAdapter: MessagesListAdapter<GenericMessage>
|
private val chatAdapter: MessagesListAdapter<GenericMessage>
|
||||||
private lateinit var newMessageReceiver: BroadcastReceiver
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!)
|
this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!)
|
||||||
this.chatEntity = LocalDBWrapper.getChatByChatID(chatID)
|
this.chatEntity = LocalDBWrapper.getChatByChatID(chatID)
|
||||||
gson = Gson()
|
gson = Gson()
|
||||||
chatAdapter = MessagesListAdapter(AppHelper.getJid(), ImageLoader { imageView, _, _ -> imageView.setImageResource(R.mipmap.ic_launcher) })
|
chatAdapter = MessagesListAdapter(AppHelper.getJid(), ImageLoader { imageView, url, _ ->
|
||||||
view.setAdapter(chatAdapter)
|
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 {
|
override fun sendMessage(text: String): Boolean {
|
||||||
@ -58,18 +77,11 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupIncomingMessagesReceiver() {
|
@Subscribe
|
||||||
newMessageReceiver = object : BroadcastReceiver() {
|
public fun onNewMessage(event: NewMessageEvent) {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
if(event.chatID.equals(chatEntity!!.jid)) {
|
||||||
if(intent.getStringExtra(XMPPConnectionService.MESSAGE_CHATID).equals(chatEntity!!.jid)) {
|
val messageID = event.messageID
|
||||||
val messageID = intent.getLongExtra(XMPPConnectionService.MESSAGE_ID, -1)
|
chatAdapter.addToStart(GenericMessage(LocalDBWrapper.getMessageByID(messageID)), true)
|
||||||
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;
|
package io.github.chronosx88.influence.presenters;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import com.stfalcon.chatkit.dialogs.DialogsListAdapter;
|
import com.stfalcon.chatkit.dialogs.DialogsListAdapter;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.json.JSONObject;
|
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.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import io.github.chronosx88.influence.R;
|
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.CoreContracts;
|
||||||
import io.github.chronosx88.influence.contracts.observer.IObserver;
|
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
import io.github.chronosx88.influence.helpers.AppHelper;
|
||||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
import io.github.chronosx88.influence.helpers.LocalDBWrapper;
|
||||||
import io.github.chronosx88.influence.helpers.ObservableActions;
|
import io.github.chronosx88.influence.logic.DialogListLogic;
|
||||||
import io.github.chronosx88.influence.logic.ChatListLogic;
|
|
||||||
import io.github.chronosx88.influence.models.GenericDialog;
|
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 io.github.chronosx88.influence.views.ChatActivity;
|
||||||
import java8.util.stream.StreamSupport;
|
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.IChatListViewContract view;
|
||||||
private CoreContracts.IDialogListLogicContract logic;
|
private CoreContracts.IDialogListLogicContract logic;
|
||||||
private DialogsListAdapter<GenericDialog> dialogListAdapter = new DialogsListAdapter<>((imageView, url, payload) -> {
|
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) {
|
public DialogListPresenter(CoreContracts.IChatListViewContract view) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
@ -50,14 +72,23 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo
|
|||||||
builder.setMessage("Remove chat?");
|
builder.setMessage("Remove chat?");
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
});
|
});
|
||||||
this.logic = new ChatListLogic();
|
this.logic = new DialogListLogic();
|
||||||
this.view.setDialogAdapter(dialogListAdapter);
|
this.view.setDialogAdapter(dialogListAdapter);
|
||||||
ArrayList<GenericDialog> dialogs = new ArrayList<>();
|
ArrayList<GenericDialog> dialogs = new ArrayList<>();
|
||||||
StreamSupport.stream(logic.loadAllChats())
|
StreamSupport.stream(logic.loadLocalChats())
|
||||||
.forEach(chatEntity -> dialogs.add(new GenericDialog(chatEntity)));
|
.forEach(chatEntity -> dialogs.add(new GenericDialog(chatEntity)));
|
||||||
dialogListAdapter.setItems(dialogs);
|
dialogListAdapter.setItems(dialogs);
|
||||||
setupIncomingMessagesReceiver();
|
loadRemoteContactList();
|
||||||
AppHelper.getObservable().register(this);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
EventBus.getDefault().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -68,29 +99,36 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo
|
|||||||
view.startActivity(intent);
|
view.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupIncomingMessagesReceiver() {
|
@Subscribe
|
||||||
incomingMessagesReceiver = new BroadcastReceiver() {
|
public void onNewChatCreated(NewChatEvent event) {
|
||||||
@Override
|
dialogListAdapter.upsertItem(new GenericDialog(LocalDBWrapper.getChatByChatID(event.chatID)));
|
||||||
public void onReceive(Context context, Intent intent) {
|
}
|
||||||
String chatID = intent.getStringExtra(XMPPConnectionService.MESSAGE_CHATID);
|
|
||||||
GenericDialog dialog = dialogListAdapter.getItemById(chatID);
|
@Subscribe
|
||||||
if(dialog == null) {
|
public void onNewMessage(NewMessageEvent event) {
|
||||||
dialogListAdapter.addItem(new GenericDialog(LocalDBWrapper.getChatByChatID(chatID)));
|
String chatID = event.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 onAuthenticate(AuthenticationStatusEvent event) {
|
||||||
|
if(event.authenticationStatus == AuthenticationStatusEvent.CONNECT_AND_LOGIN_SUCCESSFUL) {
|
||||||
|
loadRemoteContactList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(JSONObject object) throws JSONException {
|
public void loadRemoteContactList() {
|
||||||
switch (object.getInt("action")) {
|
CompletableFuture.supplyAsync(() -> logic.getRemoteContacts()).thenAccept((contacts) -> {
|
||||||
case ObservableActions.NEW_CHAT_CREATED: {
|
if(contacts != null) {
|
||||||
dialogListAdapter.addItem(new GenericDialog(LocalDBWrapper.getChatByChatID(object.getJSONArray("additional").optString(0))));
|
StreamSupport.stream(contacts).forEach(contact -> {
|
||||||
break;
|
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
|
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.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.contracts.CoreContracts
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper
|
import io.github.chronosx88.influence.helpers.AppHelper
|
||||||
import io.github.chronosx88.influence.helpers.LocalDBWrapper
|
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.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 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 {
|
class MainPresenter(private val view: CoreContracts.IMainViewContract) : CoreContracts.IMainPresenterContract {
|
||||||
private val logic: CoreContracts.IMainLogicContract = MainLogic()
|
private val logic: CoreContracts.IMainLogicContract = MainLogic()
|
||||||
private var broadcastReceiver: BroadcastReceiver? = null
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun initPeer() {
|
override fun initConnection() {
|
||||||
broadcastReceiver = object : BroadcastReceiver() {
|
logic.startService()
|
||||||
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 startChatWithPeer(username: String) {
|
override fun startChatWithPeer(username: String) {
|
||||||
LocalDBWrapper.createChatEntry(username, username)
|
LocalDBWrapper.createChatEntry(username, username)
|
||||||
ObservableUtils.notifyUI(ObservableActions.NEW_CHAT_CREATED, username)
|
EventBus.getDefault().post(NewChatEvent(username))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
//
|
fun onAuthenticate(event: AuthenticationStatusEvent) {
|
||||||
}
|
when(event.authenticationStatus) {
|
||||||
|
AuthenticationStatusEvent.INCORRECT_LOGIN_OR_PASSWORD -> {
|
||||||
// TODO
|
val intent = Intent(AppHelper.getContext(), LoginActivity::class.java)
|
||||||
private fun isServiceRunning(serviceClass: Class<*>): Boolean {
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
val manager = AppHelper.getContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
AppHelper.getContext().startActivity(intent)
|
||||||
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
|
|
||||||
if (serviceClass.name == service.service.className) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,39 +8,4 @@ import io.github.chronosx88.influence.logic.SettingsLogic
|
|||||||
class SettingsPresenter(private val view: CoreContracts.ISettingsView) : CoreContracts.ISettingsPresenter {
|
class SettingsPresenter(private val view: CoreContracts.ISettingsView) : CoreContracts.ISettingsPresenter {
|
||||||
private val mainThreadHandler: Handler = Handler(AppHelper.getContext().mainLooper)
|
private val mainThreadHandler: Handler = Handler(AppHelper.getContext().mainLooper)
|
||||||
private val logic: SettingsLogic = SettingsLogic()
|
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;
|
package io.github.chronosx88.influence.views;
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -30,24 +27,40 @@ import android.widget.EditText;
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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.R;
|
||||||
import io.github.chronosx88.influence.XMPPConnectionService;
|
import io.github.chronosx88.influence.XMPPConnectionService;
|
||||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||||
import io.github.chronosx88.influence.helpers.AppHelper;
|
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 {
|
public class LoginActivity extends AppCompatActivity implements CoreContracts.ILoginViewContract {
|
||||||
private EditText jidEditText;
|
private EditText jidEditText;
|
||||||
private EditText passwordEditText;
|
private EditText passwordEditText;
|
||||||
|
private TextInputLayout jidInputLayout;
|
||||||
|
private TextInputLayout passwordInputLayout;
|
||||||
private Button signInButton;
|
private Button signInButton;
|
||||||
private BroadcastReceiver broadcastReceiver;
|
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_login);
|
setContentView(R.layout.activity_login);
|
||||||
|
|
||||||
jidEditText = findViewById(R.id.login_jid);
|
jidEditText = findViewById(R.id.login_jid);
|
||||||
passwordEditText = findViewById(R.id.login_password);
|
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);
|
signInButton = findViewById(R.id.sign_in_button);
|
||||||
progressDialog = new ProgressDialog(LoginActivity.this);
|
progressDialog = new ProgressDialog(LoginActivity.this);
|
||||||
progressDialog.setCancelable(false);
|
progressDialog.setCancelable(false);
|
||||||
@ -68,39 +81,6 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
|||||||
progressDialog.dismiss();
|
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() {
|
private boolean checkLoginCredentials() {
|
||||||
jidEditText.setError(null);
|
jidEditText.setError(null);
|
||||||
passwordEditText.setError(null);
|
passwordEditText.setError(null);
|
||||||
@ -121,7 +101,7 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
|||||||
jidEditText.setError("Field is required!");
|
jidEditText.setError("Field is required!");
|
||||||
focusView = jidEditText;
|
focusView = jidEditText;
|
||||||
cancel = true;
|
cancel = true;
|
||||||
} else if (!isEmailValid(jid)) {
|
} else if (!isJidValid(jid)) {
|
||||||
jidEditText.setError("Invalid JID");
|
jidEditText.setError("Invalid JID");
|
||||||
focusView = jidEditText;
|
focusView = jidEditText;
|
||||||
cancel = true;
|
cancel = true;
|
||||||
@ -135,8 +115,8 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEmailValid(String email) {
|
private boolean isJidValid(String jid) {
|
||||||
return email.contains("@");
|
return jid.contains("@");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPasswordValid(String password) {
|
private boolean isPasswordValid(String password) {
|
||||||
@ -146,7 +126,7 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
|||||||
private void saveLoginCredentials() {
|
private void saveLoginCredentials() {
|
||||||
AppHelper.getPreferences().edit()
|
AppHelper.getPreferences().edit()
|
||||||
.putString("jid", jidEditText.getText().toString())
|
.putString("jid", jidEditText.getText().toString())
|
||||||
.putString("pass", passwordEditText.getText().toString())
|
.putString("pass", HashUtils.sha1(passwordEditText.getText().toString()))
|
||||||
.putBoolean("logged_in", true)
|
.putBoolean("logged_in", true)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
@ -155,4 +135,37 @@ public class LoginActivity extends AppCompatActivity implements CoreContracts.IL
|
|||||||
loadingScreen(true);
|
loadingScreen(true);
|
||||||
startService(new Intent(this, XMPPConnectionService.class));
|
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 com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import io.github.chronosx88.influence.R;
|
import io.github.chronosx88.influence.R;
|
||||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
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.presenters.MainPresenter;
|
||||||
import io.github.chronosx88.influence.views.fragments.DialogListFragment;
|
import io.github.chronosx88.influence.views.fragments.DialogListFragment;
|
||||||
import io.github.chronosx88.influence.views.fragments.SettingsFragment;
|
import io.github.chronosx88.influence.views.fragments.SettingsFragment;
|
||||||
import kotlin.Pair;
|
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 CoreContracts.IMainPresenterContract presenter;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
@ -84,15 +79,7 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa
|
|||||||
progressDialog = new ProgressDialog(MainActivity.this, R.style.AlertDialogTheme);
|
progressDialog = new ProgressDialog(MainActivity.this, R.style.AlertDialogTheme);
|
||||||
progressDialog.setCancelable(false);
|
progressDialog.setCancelable(false);
|
||||||
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
||||||
progressDialog.show();
|
presenter.initConnection();
|
||||||
presenter.initPeer();
|
|
||||||
AppHelper.getObservable().register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
presenter.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,7 +92,7 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if(item.getItemId() == R.id.action_reconnect_network) {
|
if(item.getItemId() == R.id.action_reconnect_network) {
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
presenter.initPeer();
|
presenter.initConnection();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -138,14 +125,4 @@ public class MainActivity extends AppCompatActivity implements CoreContracts.IMa
|
|||||||
|
|
||||||
return rootView;
|
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 {
|
class DialogListFragment : Fragment(), CoreContracts.IChatListViewContract {
|
||||||
private var presenter: CoreContracts.IDialogListPresenterContract? = null
|
private lateinit var presenter: CoreContracts.IDialogListPresenterContract
|
||||||
private var dialogList: DialogsList? = null
|
private lateinit var dialogList: DialogsList
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?): View? {
|
savedInstanceState: Bundle?): View? {
|
||||||
@ -30,10 +30,20 @@ class DialogListFragment : Fragment(), CoreContracts.IChatListViewContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setDialogAdapter(adapter: DialogsListAdapter<GenericDialog>) {
|
override fun setDialogAdapter(adapter: DialogsListAdapter<GenericDialog>) {
|
||||||
dialogList!!.setAdapter(adapter)
|
dialogList.setAdapter(adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActivityContext(): Context? {
|
override fun getActivityContext(): Context? {
|
||||||
return 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;
|
package io.github.chronosx88.influence.views.fragments;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
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.R;
|
||||||
import io.github.chronosx88.influence.contracts.CoreContracts;
|
import io.github.chronosx88.influence.contracts.CoreContracts;
|
||||||
@ -31,25 +23,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements CoreCo
|
|||||||
progressDialog.setCancelable(false);
|
progressDialog.setCancelable(false);
|
||||||
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
progressDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
|
||||||
presenter = new SettingsPresenter(this);
|
presenter = new SettingsPresenter(this);
|
||||||
// Load the Preferences from the XML file
|
|
||||||
addPreferencesFromResource(R.xml.main_settings);
|
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
|
@Override
|
||||||
@ -64,29 +38,4 @@ public class SettingsFragment extends PreferenceFragmentCompat implements CoreCo
|
|||||||
public void showMessage(@NotNull String message) {
|
public void showMessage(@NotNull String message) {
|
||||||
Toast.makeText(AppHelper.getContext(), message, Toast.LENGTH_LONG).show();
|
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"
|
<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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -25,6 +24,7 @@
|
|||||||
tools:context=".views.LoginActivity">
|
tools:context=".views.LoginActivity">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/jid_input_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
@ -40,6 +40,7 @@
|
|||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<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_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
@ -1,22 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<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>
|
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user