diff --git a/.idea/misc.xml b/.idea/misc.xml
index 84da703..2326c24 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d67808a..8008107 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -72,7 +72,7 @@ check.configure {
// Dependency versions are located in version.gradle
dependencies {
- implementation project(":xmpp_core")
+ implementation project(":xmpp_android")
implementation(project(':persistence-room')) {
transitive = true
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b9356d0..bc54f7e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,7 @@
+
@@ -9,36 +10,37 @@
-
+ android:theme="@style/AppTheme">
+
-
+
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/java/org/mercury_im/messenger/MercuryImApplication.java b/app/src/main/java/org/mercury_im/messenger/MercuryImApplication.java
index 503edf5..c74b2fd 100644
--- a/app/src/main/java/org/mercury_im/messenger/MercuryImApplication.java
+++ b/app/src/main/java/org/mercury_im/messenger/MercuryImApplication.java
@@ -12,7 +12,7 @@ import org.mercury_im.messenger.di.component.DaggerAppComponent;
import org.mercury_im.messenger.di.module.AppModule;
import org.mercury_im.messenger.di.module.RepositoryModule;
import org.mercury_im.messenger.di.module.RoomModule;
-import org.mercury_im.messenger.service.XmppService;
+import org.mercury_im.messenger.service.XmppStartedService;
public class MercuryImApplication extends Application {
@@ -39,9 +39,9 @@ public class MercuryImApplication extends Application {
appComponent.inject(this);
initializeNotificationChannels(this);
- Intent serviceIntent = new Intent(getApplicationContext(), XmppService.class);
+ Intent serviceIntent = new Intent(getApplicationContext(), XmppStartedService.class);
- serviceIntent.setAction(XmppService.ACTION_START);
+ serviceIntent.setAction(XmppStartedService.ACTION_START);
if (Build.VERSION.SDK_INT < 26) {
startService(serviceIntent);
} else {
diff --git a/app/src/main/java/org/mercury_im/messenger/Notifications.java b/app/src/main/java/org/mercury_im/messenger/Notifications.java
index b61ce28..d525ce9 100644
--- a/app/src/main/java/org/mercury_im/messenger/Notifications.java
+++ b/app/src/main/java/org/mercury_im/messenger/Notifications.java
@@ -7,6 +7,7 @@ public class Notifications {
public static final String NOTIFICATION_CHANNEL__INCOMING_CALL = "incoming_call"; // One day...
public static final String NOTIFICATION_CHANNEL__DEBUG = "debug";
+ // Notification IDs
public static final int FOREGROUND_SERVICE_ID = 1; // must not be 0
diff --git a/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java b/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java
index 08fcb25..cedb7f9 100644
--- a/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java
+++ b/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java
@@ -4,7 +4,7 @@ import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.di.module.AppModule;
import org.mercury_im.messenger.di.module.RepositoryModule;
import org.mercury_im.messenger.di.module.RoomModule;
-import org.mercury_im.messenger.service.XmppService;
+import org.mercury_im.messenger.service.XmppStartedService;
import org.mercury_im.messenger.ui.MainActivity;
import org.mercury_im.messenger.ui.chat.ChatActivity;
import org.mercury_im.messenger.ui.chat.ChatInputFragment;
@@ -13,6 +13,7 @@ import org.mercury_im.messenger.ui.chat.ChatViewModel;
import org.mercury_im.messenger.ui.login.LoginActivity;
import org.mercury_im.messenger.ui.login.LoginViewModel;
import org.mercury_im.messenger.ui.roster.RosterViewModel;
+import org.mercury_im.messenger.xmpp_database_connector.RosterConnector;
import javax.inject.Singleton;
@@ -48,5 +49,10 @@ public interface AppComponent {
// Services
- void inject(XmppService service);
+ void inject(XmppStartedService service);
+
+
+ // Connectors
+
+ void inject(RosterConnector connector);
}
diff --git a/app/src/main/java/org/mercury_im/messenger/di/module/RepositoryModule.java b/app/src/main/java/org/mercury_im/messenger/di/module/RepositoryModule.java
index cc05b52..da2f8d0 100644
--- a/app/src/main/java/org/mercury_im/messenger/di/module/RepositoryModule.java
+++ b/app/src/main/java/org/mercury_im/messenger/di/module/RepositoryModule.java
@@ -1,11 +1,11 @@
package org.mercury_im.messenger.di.module;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
-import org.mercury_im.messenger.persistence.repository.RosterEntryRepository;
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
-import org.mercury_im.messenger.persistence.room.dao.RosterEntryDao;
+import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
-import org.mercury_im.messenger.persistence.room.repository.IRosterEntryRepository;
+import org.mercury_im.messenger.persistence.room.repository.IContactRepository;
import javax.inject.Singleton;
@@ -23,7 +23,7 @@ public class RepositoryModule {
@Singleton
@Provides
- RosterEntryRepository provideRosterEntryRepository(RosterEntryDao dao) {
- return new IRosterEntryRepository(dao);
+ ContactRepository provideRosterEntryRepository(ContactDao dao) {
+ return new IContactRepository(dao);
}
}
diff --git a/app/src/main/java/org/mercury_im/messenger/di/module/RoomModule.java b/app/src/main/java/org/mercury_im/messenger/di/module/RoomModule.java
index f3e40d8..a274f0e 100644
--- a/app/src/main/java/org/mercury_im/messenger/di/module/RoomModule.java
+++ b/app/src/main/java/org/mercury_im/messenger/di/module/RoomModule.java
@@ -4,7 +4,7 @@ import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.persistence.room.AppDatabase;
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao;
-import org.mercury_im.messenger.persistence.room.dao.RosterEntryDao;
+import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -39,7 +39,7 @@ public class RoomModule {
@Singleton
@Provides
- RosterEntryDao provideRosterEntryDao() {
+ ContactDao provideRosterEntryDao() {
return mAppDatabase.rosterEntryDao();
}
diff --git a/app/src/main/java/org/mercury_im/messenger/service/MessengerXmppConnection.java b/app/src/main/java/org/mercury_im/messenger/service/MessengerXmppConnection.java
deleted file mode 100644
index 337933f..0000000
--- a/app/src/main/java/org/mercury_im/messenger/service/MessengerXmppConnection.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.mercury_im.messenger.service;
-
-import org.jivesoftware.smack.XMPPConnection;
-import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
-
-public class MessengerXmppConnection {
-
- private final XMPPConnection connection;
- private final RoomAccountModel account;
-
- public MessengerXmppConnection(XMPPConnection connection, RoomAccountModel account) {
- this.connection = connection;
- this.account = account;
- }
-
-}
diff --git a/app/src/main/java/org/mercury_im/messenger/service/XmppBoundService.java b/app/src/main/java/org/mercury_im/messenger/service/XmppBoundService.java
new file mode 100644
index 0000000..7f65413
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/service/XmppBoundService.java
@@ -0,0 +1,34 @@
+package org.mercury_im.messenger.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class XmppBoundService extends Service {
+
+ private final Binder binder = new Binder();
+ private XmppStartedService startedService;
+
+ public class Binder extends android.os.Binder {
+ public XmppBoundService getService() {
+ return XmppBoundService.this;
+ }
+ }
+
+ public XmppBoundService() {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ public void setXmppStartedService(XmppStartedService service) {
+ this.startedService = service;
+ }
+
+ public XmppStartedService getStartedService() {
+ return startedService;
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/service/XmppConnectionHolder.java b/app/src/main/java/org/mercury_im/messenger/service/XmppConnectionHolder.java
deleted file mode 100644
index 767003a..0000000
--- a/app/src/main/java/org/mercury_im/messenger/service/XmppConnectionHolder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.mercury_im.messenger.service;
-
-import android.util.LongSparseArray;
-
-import org.jivesoftware.smack.XMPPConnection;
-
-public class XmppConnectionHolder {
-
- private static XmppConnectionHolder INSTANCE;
-
- private final LongSparseArray connections = new LongSparseArray<>();
-
- private XmppConnectionHolder() {
-
- }
-
- public static XmppConnectionHolder getInstance() {
- if (INSTANCE == null) {
- INSTANCE = new XmppConnectionHolder();
- }
- return INSTANCE;
- }
-
- public LongSparseArray getConnections() {
- return connections;
- }
-}
diff --git a/app/src/main/java/org/mercury_im/messenger/service/XmppService.java b/app/src/main/java/org/mercury_im/messenger/service/XmppService.java
deleted file mode 100644
index 844e928..0000000
--- a/app/src/main/java/org/mercury_im/messenger/service/XmppService.java
+++ /dev/null
@@ -1,229 +0,0 @@
-package org.mercury_im.messenger.service;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-
-import org.jivesoftware.smack.SmackException;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.XMPPException;
-import org.jivesoftware.smack.roster.Roster;
-import org.jivesoftware.smack.roster.RosterEntry;
-import org.jivesoftware.smack.roster.RosterLoadedListener;
-import org.jivesoftware.smack.tcp.XMPPTCPConnection;
-import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
-import org.jxmpp.jid.EntityBareJid;
-import org.jxmpp.jid.impl.JidCreate;
-import org.mercury_im.messenger.MercuryImApplication;
-import org.mercury_im.messenger.Notifications;
-import org.mercury_im.messenger.R;
-import org.mercury_im.messenger.persistence.model.RosterEntryModel;
-import org.mercury_im.messenger.persistence.repository.RosterEntryRepository;
-import org.mercury_im.messenger.persistence.room.AppDatabase;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
-import org.mercury_im.messenger.ui.MainActivity;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-/**
- * Started Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
- * affiliated with registered accounts.
- */
-public class XmppService extends Service {
-
- private static final String TAG = MercuryImApplication.TAG;
-
- private static final String APP = "org.olomono.mercury";
- private static final String SERVICE = APP + ".XmppService";
-
- private static final String ACTION = SERVICE + ".ACTION";
- private static final String EVENT = SERVICE + ".EVENT";
- private static final String EXTRA = SERVICE + ".EXTRA";
- private static final String STATUS = SERVICE + ".STATUS";
-
- public static final String ACTION_START = ACTION + ".START";
- public static final String ACTION_STOP = ACTION + ".STOP";
- public static final String ACTION_CONNECT = ACTION + ".CONNECT";
- public static final String ACTION_DISCONNECT = ACTION + ".DISCONNECT";
- public static final String ACTION_PING = ACTION + ".PING";
-
- public static final String EVENT_INCOMING_MESSAGE = EVENT + ".INCOMING_MESSAGE";
- public static final String EVENT_OUTGOING_MESSAGE = EVENT + ".OUTGOING_MESSAGE";
-
- public static final String EXTRA_JID = EXTRA + ".JID";
- public static final String EXTRA_PASSWORD = EXTRA + ".PASSWORD";
- public static final String EXTRA_ACCOUNT_ID = EXTRA + ".ACCOUNT_ID";
-
- public static final String STATUS_SUCCESS = STATUS + ".SUCCESS";
- public static final String STATUS_FAILURE = STATUS + ".FAILURE";
-
- @Inject
- AppDatabase database;
-
- @Inject
- RosterEntryRepository rosterRepository;
-
- private final LongSparseArray connections = new LongSparseArray<>();
-
- @Nullable
- @Override
- public final IBinder onBind(Intent intent) {
- // We are a started service, so we don't provide a binding and always return null.
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.d(TAG, "onCreate()");
- MercuryImApplication.getApplication().getAppComponent().inject(this);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.d(TAG, "onStartCommand(" + intent + ")");
- if (intent == null) {
- startAndDisplayForegroundNotification();
- } else {
- String action = intent.getAction();
- action = action == null ? "" : action;
-
- switch (action) {
- case ACTION_START:
- startAndDisplayForegroundNotification();
- break;
- case ACTION_STOP:
- stopForeground(true);
- break;
- case ACTION_CONNECT:
- String jid = intent.getStringExtra(EXTRA_JID);
- EntityBareJid bareJid = JidCreate.entityBareFromOrNull(jid);
- if (jid == null) {
- Toast.makeText(this, "No JID provided.", Toast.LENGTH_SHORT).show();
- return START_STICKY_COMPATIBILITY;
- }
- String password = intent.getStringExtra(EXTRA_PASSWORD);
- if (password == null) {
- Toast.makeText(this, "No Password provided.", Toast.LENGTH_SHORT).show();
- return START_STICKY_COMPATIBILITY;
- }
- long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
- if (accountId == -1) {
- Toast.makeText(this, "No account ID provided.", Toast.LENGTH_SHORT).show();
- return START_STICKY_COMPATIBILITY;
- }
-
- new Thread() {
-
- public void run() {
-
- XMPPTCPConnection con = null;
- try {
- InetAddress address = InetAddress.getByName(bareJid.getDomain().toString());
- XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
- .setXmppDomain(bareJid.asDomainBareJid())
- .setUsernameAndPassword(bareJid.getLocalpart().toString(), password)
- .setHostAddress(address)
- .setConnectTimeout(2 * 60 * 1000)
- .build();
- con = new XMPPTCPConnection(conf);
- con.connect().login();
- } catch (
- XMPPException e) {
- e.printStackTrace();
- } catch (
- SmackException e) {
- e.printStackTrace();
- } catch (
- IOException e) {
- e.printStackTrace();
- } catch (
- InterruptedException e) {
- e.printStackTrace();
- }
-
- connections.put(intent.getLongExtra(EXTRA_ACCOUNT_ID, -1), con);
- NotificationManagerCompat.from(getApplicationContext()).notify(Notifications.FOREGROUND_SERVICE_ID,
- getForegroundNotification(getApplicationContext(), connections.size()));
-
- Roster roster = Roster.getInstanceFor(con);
- roster.addRosterLoadedListener(new RosterLoadedListener() {
- @Override
- public void onRosterLoaded(Roster roster) {
- Set entries = roster.getEntries();
- for (RosterEntry e : entries) {
- Log.d(TAG, "Inserting Roster entry " + e.getJid().toString());
- RoomRosterEntryModel m = new RoomRosterEntryModel(e.getJid().asEntityBareJidIfPossible(), e.getName(), e.getName());
- m.setAccountId(accountId);
- rosterRepository.updateOrInsertRosterEntry(m);
- }
- }
-
- @Override
- public void onRosterLoadingFailed(Exception exception) {
- Log.d(TAG, "Roster Loading failed", exception);
- }
- });
-
- try {
- roster.reload();
- } catch (SmackException.NotLoggedInException e) {
- e.printStackTrace();
- } catch (SmackException.NotConnectedException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- }
- }.start();
- break;
- default:
- break;
- }
- }
-
- return START_STICKY_COMPATIBILITY;
- }
-
- public void startAndDisplayForegroundNotification() {
- Log.d(TAG, "startAndDisplayForegroundNotification()");
- Notification notification = getForegroundNotification(getApplicationContext(), connections.size());
-
- startForeground(Notifications.FOREGROUND_SERVICE_ID, notification);
- }
-
- static Notification getForegroundNotification(Context context, int numConnections) {
- Intent startMainActivityIntent = new Intent(context, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
- startMainActivityIntent, 0);
- return new NotificationCompat.Builder(context, Notifications.NOTIFICATION_CHANNEL__FOREGROUND_SERVICE)
- .setContentTitle("Mercury")
- .setContentText(numConnections + " connections.")
- .setSmallIcon(R.drawable.ic_send_black_24dp)
- .setContentIntent(pendingIntent)
- .build();
- }
-
- public XMPPConnection getConnection(long accountId) {
- return connections.get(accountId);
- }
-
- public void putConnection(int accountId, XMPPConnection connection) {
- connections.put(accountId, connection);
- }
-}
diff --git a/app/src/main/java/org/mercury_im/messenger/service/XmppStartedService.java b/app/src/main/java/org/mercury_im/messenger/service/XmppStartedService.java
new file mode 100644
index 0000000..2cfbe53
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/service/XmppStartedService.java
@@ -0,0 +1,298 @@
+package org.mercury_im.messenger.service;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.chat2.Chat;
+import org.jivesoftware.smack.chat2.ChatManager;
+import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.roster.Roster;
+import org.jivesoftware.smack.roster.RosterEntry;
+import org.jivesoftware.smack.roster.RosterLoadedListener;
+import org.jivesoftware.smack.tcp.XMPPTCPConnection;
+import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
+import org.jxmpp.jid.EntityBareJid;
+import org.mercury_im.messenger.MercuryImApplication;
+import org.mercury_im.messenger.Notifications;
+import org.mercury_im.messenger.R;
+import org.mercury_im.messenger.persistence.model.AccountModel;
+import org.mercury_im.messenger.persistence.repository.AccountRepository;
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
+import org.mercury_im.messenger.persistence.room.AppDatabase;
+import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
+import org.mercury_im.messenger.ui.MainActivity;
+import org.mercury_im.messenger.xmpp_android.ParcelableXMPPTCPConnectionConfiguration;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * Started Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
+ * affiliated with registered accounts.
+ */
+public class XmppStartedService extends Service {
+
+ private static final String TAG = MercuryImApplication.TAG;
+
+ private static final String APP = "org.olomono.mercury";
+ private static final String SERVICE = APP + ".XmppStartedService";
+
+ private static final String ACTION = SERVICE + ".ACTION";
+ private static final String EVENT = SERVICE + ".EVENT";
+ private static final String EXTRA = SERVICE + ".EXTRA";
+ private static final String STATUS = SERVICE + ".STATUS";
+
+ public static final String ACTION_START = ACTION + ".START";
+ public static final String ACTION_STOP = ACTION + ".STOP";
+ public static final String ACTION_CONNECT = ACTION + ".CONNECT";
+ public static final String ACTION_DISCONNECT = ACTION + ".DISCONNECT";
+ public static final String ACTION_PING = ACTION + ".PING";
+
+ public static final String EVENT_INCOMING_MESSAGE = EVENT + ".INCOMING_MESSAGE";
+ public static final String EVENT_OUTGOING_MESSAGE = EVENT + ".OUTGOING_MESSAGE";
+
+ public static final String EXTRA_CONFIGURATION = EXTRA + ".CONFIGURATION";
+ public static final String EXTRA_ACCOUNT_ID = EXTRA + ".ACCOUNT_ID";
+
+ public static final String STATUS_SUCCESS = STATUS + ".SUCCESS";
+ public static final String STATUS_FAILURE = STATUS + ".FAILURE";
+
+ @Inject
+ AppDatabase database;
+
+ @Inject
+ ContactRepository rosterRepository;
+
+ @Inject
+ AccountRepository accountRepository;
+
+ private final LongSparseArray connections = new LongSparseArray<>();
+
+ private Handler uiHandler;
+
+ @Nullable
+ @Override
+ public final IBinder onBind(Intent intent) {
+ // We are a started service, so we don't provide a binding and always return null.
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "onCreate()");
+ MercuryImApplication.getApplication().getAppComponent().inject(this);
+
+ Intent intent = new Intent(this, XmppBoundService.class);
+ bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ XmppBoundService.Binder binder = (XmppBoundService.Binder) iBinder;
+ XmppBoundService boundService = binder.getService();
+ boundService.setXmppStartedService(XmppStartedService.this);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+
+ }
+ };
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if (uiHandler == null) {
+ uiHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case 1:
+ Message m = (Message) msg.obj;
+ Toast.makeText(XmppStartedService.this, (m.getFrom() != null ? m.getFrom().toString() : "null") + ": "
+ + m.getBody(), Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+ }
+
+ Log.d(TAG, "onStartCommand(" + intent + ")");
+ if (intent == null) {
+ startAndDisplayForegroundNotification();
+ } else {
+ String action = intent.getAction();
+ action = action == null ? "" : action;
+
+ switch (action) {
+ case ACTION_START:
+ startAndDisplayForegroundNotification();
+ startConnections();
+ break;
+ case ACTION_STOP:
+ stopForeground(true);
+ break;
+ case ACTION_CONNECT:
+ ParcelableXMPPTCPConnectionConfiguration configuration = intent.getParcelableExtra(EXTRA_CONFIGURATION);
+ if (configuration == null) {
+ Log.e(TAG, "Configuration is null.");
+ return START_STICKY_COMPATIBILITY;
+ }
+
+ long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
+ if (accountId == -1) {
+ Log.d(TAG, "No AccountID provided.");
+ return START_STICKY_COMPATIBILITY;
+ }
+
+ new Thread() {
+ public void run() {
+ XMPPConnection connection = startConnection(configuration, accountId);
+ connections.put(accountId, connection);
+ }
+ }.start();
+ break;
+ default:
+ break;
+ }
+ }
+
+ return START_STICKY_COMPATIBILITY;
+ }
+
+ public void startAndDisplayForegroundNotification() {
+ Log.d(TAG, "startAndDisplayForegroundNotification()");
+ Notification notification = getForegroundNotification(getApplicationContext(), connections.size());
+
+ startForeground(Notifications.FOREGROUND_SERVICE_ID, notification);
+ }
+
+ static Notification getForegroundNotification(Context context, int numConnections) {
+ Intent startMainActivityIntent = new Intent(context, MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
+ startMainActivityIntent, 0);
+ return new NotificationCompat.Builder(context, Notifications.NOTIFICATION_CHANNEL__FOREGROUND_SERVICE)
+ .setContentTitle("Mercury")
+ .setContentText(numConnections + " connections.")
+ .setSmallIcon(R.drawable.ic_send_black_24dp)
+ .setContentIntent(pendingIntent)
+ .build();
+ }
+
+ public void startConnections() {
+ new Thread() {
+ @Override
+ public void run() {
+ synchronized (connections) {
+ List accounts = accountRepository.getAllAccounts();
+ if (accounts == null) return;
+
+ for (AccountModel a : accounts) {
+ if (connections.get(a.getId()) != null) {
+ continue;
+ }
+
+ // XMPPConnection connection = startConnection(a.getJid(), a.getPassword(), a.getId());
+ // connections.put(a.getId(), connection);
+ }
+ }
+ }
+ }.start();
+ }
+
+ private XMPPConnection startConnection(ParcelableXMPPTCPConnectionConfiguration connectionConfiguration, long accountId) {
+ XMPPTCPConnection con = null;
+ try {
+ XMPPTCPConnectionConfiguration conf = connectionConfiguration.getConfiguration();
+ con = new XMPPTCPConnection(conf);
+ con.connect().login();
+ } catch (
+ XMPPException e) {
+ e.printStackTrace();
+ } catch (
+ SmackException e) {
+ e.printStackTrace();
+ } catch (
+ IOException e) {
+ e.printStackTrace();
+ } catch (
+ InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ NotificationManagerCompat.from(getApplicationContext()).notify(Notifications.FOREGROUND_SERVICE_ID,
+ getForegroundNotification(getApplicationContext(), connections.size()));
+
+ Roster roster = Roster.getInstanceFor(con);
+ roster.addRosterLoadedListener(new RosterLoadedListener() {
+ @Override
+ public void onRosterLoaded(Roster roster) {
+ Set entries = roster.getEntries();
+ for (RosterEntry e : entries) {
+ Log.d(TAG, "Inserting Roster entry " + e.getJid().toString());
+ RoomContactModel m = new RoomContactModel(e.getJid().asEntityBareJidIfPossible(), e.getName(), e.getName());
+ m.setAccountId(accountId);
+ rosterRepository.updateOrInsertRosterEntry(m);
+ }
+ }
+
+ @Override
+ public void onRosterLoadingFailed(Exception exception) {
+ Log.d(TAG, "Roster Loading failed", exception);
+ }
+ });
+
+ try {
+ roster.reload();
+ } catch (SmackException.NotLoggedInException e) {
+ e.printStackTrace();
+ } catch (SmackException.NotConnectedException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ ChatManager chatManager = ChatManager.getInstanceFor(con);
+ chatManager.addIncomingListener(new IncomingChatMessageListener() {
+ @Override
+ public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
+ android.os.Message msg = uiHandler.obtainMessage(1, message);
+ msg.sendToTarget();
+ }
+ });
+
+ return con;
+ }
+
+ public XMPPConnection getConnection(long accountId) {
+ return connections.get(accountId);
+ }
+
+ public void putConnection(int accountId, XMPPConnection connection) {
+ connections.put(accountId, connection);
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java b/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java
index 5ee21d8..ff8a994 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java
@@ -10,16 +10,14 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
-import org.jxmpp.jid.impl.JidCreate;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.R;
+import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.room.AppDatabase;
-import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
-import org.mercury_im.messenger.persistence.repository.RosterEntryRepository;
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
import org.mercury_im.messenger.ui.chat.ChatActivity;
import org.mercury_im.messenger.ui.login.LoginActivity;
import org.mercury_im.messenger.ui.settings.SettingsActivity;
@@ -35,7 +33,7 @@ public class MainActivity extends AppCompatActivity {
public AccountRepository accountRepository;
@Inject
- public RosterEntryRepository rosterEntryRepository;
+ public ContactRepository contactRepository;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -47,20 +45,10 @@ public class MainActivity extends AppCompatActivity {
MercuryImApplication.getApplication().getAppComponent().inject(this);
FloatingActionButton fab = findViewById(R.id.fab);
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- RoomAccountModel account = new RoomAccountModel();
- account.setJid(JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"));
- account.setPassword("swordfish");
- accountRepository.insertAccount(account);
- }
- });
-
- accountRepository.getAllAccounts().observe(this, new Observer>() {
+ accountRepository.getAllAccountsLive().observe(this, new Observer>() {
@Override
- public void onChanged(@Nullable List accountModels) {
+ public void onChanged(@Nullable List accountModels) {
if (accountModels == null || accountModels.isEmpty()) {
startActivity(new Intent(getApplicationContext(), LoginActivity.class));
}
@@ -96,11 +84,11 @@ public class MainActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
- private void addRosterEntry(AppDatabase database, RoomRosterEntryModel rosterEntry) {
+ private void addRosterEntry(AppDatabase database, RoomContactModel rosterEntry) {
new addRosterEntry(database).execute(rosterEntry);
}
- private static class addRosterEntry extends AsyncTask {
+ private static class addRosterEntry extends AsyncTask {
private AppDatabase database;
@@ -109,7 +97,7 @@ public class MainActivity extends AppCompatActivity {
}
@Override
- protected Void doInBackground(RoomRosterEntryModel... rosterEntryModels) {
+ protected Void doInBackground(RoomContactModel... rosterEntryModels) {
database.rosterEntryDao().insert(rosterEntryModels[0]);
return null;
}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatInputFragment.java b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatInputFragment.java
index 9418944..ce8aa84 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatInputFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatInputFragment.java
@@ -19,7 +19,7 @@ import org.mercury_im.messenger.R;
public class ChatInputFragment extends Fragment implements View.OnClickListener {
private EditText textInput;
- private FloatingActionButton addAttachement;
+ private ImageButton addAttachement;
private ImageButton buttonSend;
private ChatInputViewModel mViewModel;
@@ -34,7 +34,7 @@ public class ChatInputFragment extends Fragment implements View.OnClickListener
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat, container, false);
textInput = view.findViewById(R.id.chat_field__text_input);
- addAttachement = view.findViewById(R.id.chat_field__fab_add);
+ addAttachement = view.findViewById(R.id.chat_field__button_attachment);
buttonSend = view.findViewById(R.id.chat_field__button_send);
addAttachement.setOnClickListener(this);
@@ -62,7 +62,7 @@ public class ChatInputFragment extends Fragment implements View.OnClickListener
public void onClick(View view) {
switch (view.getId()) {
// Add media
- case R.id.chat_field__fab_add:
+ case R.id.chat_field__button_attachment:
Toast.makeText(getContext(), R.string.not_yet_implemented, Toast.LENGTH_SHORT).show();
break;
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java
new file mode 100644
index 0000000..4a03982
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java
@@ -0,0 +1,7 @@
+package org.mercury_im.messenger.ui.chatlist;
+
+import androidx.lifecycle.ViewModel;
+
+public class ChatListViewModel extends ViewModel {
+
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/login/LoginActivity.java b/app/src/main/java/org/mercury_im/messenger/ui/login/LoginActivity.java
index 8901776..8113821 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/login/LoginActivity.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/login/LoginActivity.java
@@ -1,5 +1,6 @@
package org.mercury_im.messenger.ui.login;
+import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
@@ -17,9 +18,10 @@ import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.R;
+import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
-import org.mercury_im.messenger.service.XmppService;
+import org.mercury_im.messenger.service.XmppStartedService;
import javax.inject.Inject;
@@ -51,19 +53,7 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
mPasswordView = findViewById(R.id.password);
viewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
- viewModel.getAccount().observe(this, accountModel -> {
- if (accountModel == null) {
- return;
- }
-
- if (accountModel.getJid() != null) {
- mJidView.setText(accountModel.getJid().toString());
- }
-
- if (accountModel.getPassword() != null) {
- mPasswordView.setText(accountModel.getPassword());
- }
- });
+ displayCredentials(viewModel.getAccount());
mJidView.setOnEditorActionListener(this);
mPasswordView.setOnEditorActionListener(this);
@@ -80,6 +70,22 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
mProgressView = findViewById(R.id.login_progress);
}
+ private void displayCredentials(LiveData extends AccountModel> account) {
+ account.observe(this, accountModel -> {
+ if (accountModel == null) {
+ return;
+ }
+
+ if (accountModel.getJid() != null) {
+ mJidView.setText(accountModel.getJid().toString());
+ }
+
+ if (accountModel.getPassword() != null) {
+ mPasswordView.setText(accountModel.getPassword());
+ }
+ });
+ }
+
private void loginDetailsEntered() {
boolean loginIntact = true;
String jidInput = mJidView.getText().toString();
@@ -112,11 +118,9 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
}
private void attemptLogin(EntityBareJid jid, String password, long accountId) {
- Intent connectIntent = new Intent(getApplicationContext(), XmppService.class);
- connectIntent.setAction(XmppService.ACTION_CONNECT);
- connectIntent.putExtra(XmppService.EXTRA_JID, jid.toString());
- connectIntent.putExtra(XmppService.EXTRA_PASSWORD, password);
- connectIntent.putExtra(XmppService.EXTRA_ACCOUNT_ID, accountId);
+ Intent connectIntent = new Intent(getApplicationContext(), XmppStartedService.class);
+ connectIntent.setAction(XmppStartedService.ACTION_CONNECT);
+ connectIntent.putExtra(XmppStartedService.EXTRA_ACCOUNT_ID, accountId);
startService(connectIntent);
}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterFragment.java b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterFragment.java
index f948edb..0636883 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterFragment.java
@@ -11,7 +11,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.mercury_im.messenger.R;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import java.util.ArrayList;
import java.util.List;
@@ -45,9 +45,9 @@ public class RosterFragment extends Fragment {
}
private void observeViewModel(RosterViewModel viewModel) {
- viewModel.getRosterEntryList().observe(this, new Observer>() {
+ viewModel.getRosterEntryList().observe(this, new Observer>() {
@Override
- public void onChanged(@Nullable List rosterEntries) {
+ public void onChanged(@Nullable List rosterEntries) {
if (rosterEntries == null) {
return;
}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterRecyclerViewAdapter.java b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterRecyclerViewAdapter.java
index 8ee6e7d..8220d34 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterRecyclerViewAdapter.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterRecyclerViewAdapter.java
@@ -8,16 +8,16 @@ import android.view.ViewGroup;
import android.widget.TextView;
import org.mercury_im.messenger.R;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import java.util.List;
public class RosterRecyclerViewAdapter
extends RecyclerView.Adapter {
- private List entryModelList;
+ private List entryModelList;
- public RosterRecyclerViewAdapter(List entryModelList) {
+ public RosterRecyclerViewAdapter(List entryModelList) {
this.entryModelList = entryModelList;
}
@@ -30,7 +30,7 @@ public class RosterRecyclerViewAdapter
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
- RoomRosterEntryModel model = entryModelList.get(position);
+ RoomContactModel model = entryModelList.get(position);
holder.jidView.setText(model.getJid().toString());
holder.nicknameView.setText(model.getNickname());
holder.itemView.setTag(model);
@@ -41,7 +41,7 @@ public class RosterRecyclerViewAdapter
return entryModelList.size();
}
- public void setItems(List rosterEntryModels) {
+ public void setItems(List rosterEntryModels) {
this.entryModelList = rosterEntryModels;
notifyDataSetChanged();
}
diff --git a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterViewModel.java
index 8ab880c..d743860 100644
--- a/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterViewModel.java
+++ b/app/src/main/java/org/mercury_im/messenger/ui/roster/RosterViewModel.java
@@ -6,8 +6,8 @@ import androidx.lifecycle.LiveData;
import androidx.annotation.NonNull;
import org.mercury_im.messenger.MercuryImApplication;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
-import org.mercury_im.messenger.persistence.repository.RosterEntryRepository;
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import java.util.List;
@@ -16,18 +16,18 @@ import javax.inject.Inject;
public class RosterViewModel extends AndroidViewModel {
@Inject
- RosterEntryRepository rosterEntryRepository;
+ ContactRepository contactRepository;
- private final LiveData> rosterEntryList;
+ private final LiveData> rosterEntryList;
@Inject
public RosterViewModel(@NonNull Application application) {
super(application);
MercuryImApplication.getApplication().getAppComponent().inject(this);
- this.rosterEntryList = rosterEntryRepository.getAllRosterEntries();
+ this.rosterEntryList = contactRepository.getAllRosterEntries();
}
- public LiveData> getRosterEntryList() {
+ public LiveData> getRosterEntryList() {
return rosterEntryList;
}
}
diff --git a/app/src/main/java/org/mercury_im/messenger/xmpp_database_connector/RosterConnector.java b/app/src/main/java/org/mercury_im/messenger/xmpp_database_connector/RosterConnector.java
new file mode 100644
index 0000000..61b1c6a
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/xmpp_database_connector/RosterConnector.java
@@ -0,0 +1,78 @@
+package org.mercury_im.messenger.xmpp_database_connector;
+
+import android.util.Log;
+
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.roster.Roster;
+import org.jivesoftware.smack.roster.RosterEntry;
+import org.jxmpp.jid.Jid;
+import org.mercury_im.messenger.MercuryImApplication;
+import org.mercury_im.messenger.persistence.model.AccountModel;
+import org.mercury_im.messenger.persistence.repository.AccountRepository;
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
+import org.mercury_im.messenger.xmpp_core.MercuryConnection;
+import org.mercury_im.messenger.xmpp_core.RosterHandler;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import static androidx.constraintlayout.widget.Constraints.TAG;
+
+public class RosterConnector implements RosterHandler {
+
+ @Inject
+ ContactRepository contactRepository;
+
+ @Inject
+ AccountRepository accountRepository;
+
+ private AccountModel account;
+
+ private final MercuryConnection connection;
+ private final Roster roster;
+
+ public RosterConnector(MercuryConnection connection) {
+ this.connection = connection;
+ this.roster = connection.getRoster();
+
+ MercuryImApplication.getApplication().getAppComponent().inject(this);
+
+ account = (AccountModel) accountRepository.getAccount(connection.getAccountId()).getValue();
+ }
+
+ @Override
+ public void entriesAdded(Collection addresses) {
+ for (Jid j : addresses) {
+ RosterEntry entry = roster.getEntry(j.asBareJid());
+ // ContactModel
+ }
+ }
+
+ @Override
+ public void entriesUpdated(Collection addresses) {
+
+ }
+
+ @Override
+ public void entriesDeleted(Collection addresses) {
+
+ }
+
+ @Override
+ public void presenceChanged(Presence presence) {
+
+ }
+
+ @Override
+ public void onRosterLoaded(Roster roster) {
+ if (roster == connection.getRoster()) {
+
+ }
+ }
+
+ @Override
+ public void onRosterLoadingFailed(Exception exception) {
+ Log.e(TAG, "Loading roster for " + account.getJid().toString() + " failed.", exception);
+ }
+}
diff --git a/app/src/main/res/layout/fragment_chat.xml b/app/src/main/res/layout/fragment_chat.xml
index 2c2ab75..30a6526 100644
--- a/app/src/main/res/layout/fragment_chat.xml
+++ b/app/src/main/res/layout/fragment_chat.xml
@@ -1,5 +1,6 @@
-
-
-
-
+ card_view:layout_constraintStart_toStartOf="parent">
+
+
@@ -63,7 +57,7 @@
android:tint="?attr/colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_send_black_24dp"
- card_view:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toBottomOf="parent" />
diff --git a/persistence-room/build.gradle b/persistence-room/build.gradle
index abfe8e4..a1fb13b 100644
--- a/persistence-room/build.gradle
+++ b/persistence-room/build.gradle
@@ -12,6 +12,12 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+ }
+ }
+
}
buildTypes {
@@ -36,6 +42,8 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ androidTestImplementation "androidx.test:core:1.2.0-beta01"
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1-beta01'
// Room
api "androidx.room:room-runtime:$roomVersion"
diff --git a/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json b/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json
new file mode 100644
index 0000000..42f97cf
--- /dev/null
+++ b/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json
@@ -0,0 +1,175 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "378204e8bb2f9f4c8bf432291835718c",
+ "entities": [
+ {
+ "tableName": "RoomContactModel",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `jid` TEXT NOT NULL, `rosterName` TEXT, `nickname` TEXT, PRIMARY KEY(`accountId`, `jid`), FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "jid",
+ "columnName": "jid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rosterName",
+ "columnName": "rosterName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "nickname",
+ "columnName": "nickname",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "accountId",
+ "jid"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_RoomContactModel_accountId_jid",
+ "unique": true,
+ "columnNames": [
+ "accountId",
+ "jid"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_RoomContactModel_accountId_jid` ON `${TABLE_NAME}` (`accountId`, `jid`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "accounts",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "accountId"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jid` TEXT, `password` TEXT, `enabled` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "jid",
+ "columnName": "jid",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "enabled",
+ "columnName": "enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountId` INTEGER NOT NULL, `body` TEXT, `sendDate` INTEGER, `from` TEXT, `to` TEXT, FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "body",
+ "columnName": "body",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sendDate",
+ "columnName": "sendDate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "from",
+ "columnName": "from",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "to",
+ "columnName": "to",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": [
+ {
+ "table": "accounts",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "accountId"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"378204e8bb2f9f4c8bf432291835718c\")"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java
index 5277bb2..7fdfbb4 100644
--- a/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java
+++ b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java
@@ -2,13 +2,21 @@ package org.mercury_im.messenger.persistence.room;
import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.lifecycle.LiveData;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.jxmpp.jid.impl.JidCreate;
+import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
+import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
+import org.mercury_im.messenger.persistence.room.repository.IContactRepository;
+import org.mercury_im.messenger.persistence.room.repository.IMessageRepository;
-import static org.junit.Assert.*;
+import java.util.List;
+
+import static org.junit.Assert.assertNull;
/**
* Instrumented test, which will execute on an Android device.
@@ -17,11 +25,25 @@ import static org.junit.Assert.*;
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
+
@Test
public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
+ // Context of the app under test
- assertEquals("org.mercury_im.messenger.persistence.room", appContext.getPackageName());
+ Context context = ApplicationProvider.getApplicationContext();
+
+ AppDatabase appDatabase = AppDatabase.getDatabase(context);
+ IAccountRepository accountRepository = new IAccountRepository(appDatabase.accountDao());
+ IContactRepository rosterRepository = new IContactRepository(appDatabase.rosterEntryDao());
+ IMessageRepository messageRepository = new IMessageRepository(appDatabase.messageDao());
+
+ LiveData> accounts = accountRepository.getAllAccountsLive();
+ assertNull(accounts.getValue());
+
+ RoomAccountModel a1 = new RoomAccountModel();
+ a1.setJid(JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"));
+ a1.setPassword("5w0rdf1sh");
+ a1.setEnabled(false);
+ accountRepository.insertAccount(a1);
}
}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java
index fb997fa..0cc08e3 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java
@@ -8,15 +8,15 @@ import androidx.room.RoomDatabase;
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao;
-import org.mercury_im.messenger.persistence.room.dao.RosterEntryDao;
+import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
-@Database(entities = {RoomRosterEntryModel.class, RoomAccountModel.class, RoomMessageModel.class}, version = 1)
+@Database(entities = {RoomContactModel.class, RoomAccountModel.class, RoomMessageModel.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
- public static final String DB_NAME = "app_db";
+ private static final String DB_NAME = "mercury_db";
private static AppDatabase INSTANCE;
public static AppDatabase getDatabase(Context context) {
@@ -27,7 +27,7 @@ public abstract class AppDatabase extends RoomDatabase {
return INSTANCE;
}
- public abstract RosterEntryDao rosterEntryDao();
+ public abstract ContactDao rosterEntryDao();
public abstract MessageDao messageDao();
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java
index 3d5c6ce..41dea43 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java
@@ -28,7 +28,12 @@ public interface AccountDao extends BaseDao {
* @return live updating account list
*/
@Query("select * from accounts")
- LiveData> getAllAccounts();
+ LiveData> getAllAccountsLive();
+
+ @Query("select * from accounts")
+ List getAllAccounts();
+
+
/**
* Return the {@link RoomAccountModel Account} which is identified by the given id.
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java
index 72e3fdf..4cb877a 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java
@@ -10,8 +10,8 @@ public interface BaseDao {
long insert(T entity);
@Update
- void update(T entity);
+ void upddate(T... entity);
@Delete
- void delete(T entity);
+ void delete(T... entity);
}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java
new file mode 100644
index 0000000..b67bb0a
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java
@@ -0,0 +1,20 @@
+package org.mercury_im.messenger.persistence.room.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Query;
+
+import org.jxmpp.jid.EntityBareJid;
+import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
+
+import java.util.List;
+
+public interface ChatDao extends BaseDao {
+
+ @Query("SELECT * FROM RoomChatModel")
+ LiveData> getAllChats();
+
+ @Query("SELECT * FROM RoomChatModel WHERE ")
+ LiveData getChatWith(EntityBareJid contact);
+
+
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java
new file mode 100644
index 0000000..5dc909b
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java
@@ -0,0 +1,31 @@
+package org.mercury_im.messenger.persistence.room.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Query;
+import androidx.room.TypeConverters;
+
+import org.jxmpp.jid.EntityBareJid;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
+import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
+
+import java.util.List;
+
+@Dao
+@TypeConverters(EntityBareJidConverter.class)
+public interface ContactDao extends BaseDao {
+
+ /**
+ * Return a {@link LiveData} wrapping a {@link List} of all {@link RoomContactModel RosterEntries}
+ * which are currently found in the database.
+ * @return
+ */
+ @Query("select * from RoomContactModel")
+ LiveData> getAllRosterEntries();
+
+ @Query("select * from RoomContactModel where jid = :jid")
+ RoomContactModel getRosterEntryByJid(EntityBareJid jid);
+
+ @Query("select * from RoomContactModel where accountId = :accountId")
+ LiveData> getRosterEntriesForAccount(long accountId);
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/RosterEntryDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/RosterEntryDao.java
deleted file mode 100644
index d6f88d9..0000000
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/RosterEntryDao.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.mercury_im.messenger.persistence.room.dao;
-
-import androidx.lifecycle.LiveData;
-import androidx.room.Dao;
-import androidx.room.Delete;
-import androidx.room.Insert;
-import androidx.room.Query;
-import androidx.room.TypeConverters;
-
-import org.mercury_im.messenger.persistence.model.RosterEntryModel;
-import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
-
-import java.util.List;
-
-import static androidx.room.OnConflictStrategy.REPLACE;
-
-@Dao
-@TypeConverters(EntityBareJidConverter.class)
-public interface RosterEntryDao extends BaseDao {
-
- /**
- * Return a {@link LiveData} wrapping a {@link List} of all {@link RoomRosterEntryModel RosterEntries}
- * which are currently found in the database.
- * @return
- */
- @Query("select * from roster")
- LiveData> getAllRosterEntries();
-
- @Query("select * from roster where id = :id")
- RoomRosterEntryModel getRosterEntryById(long id);
-
- @Query("select * from roster where accountId = :accountId")
- LiveData> getRosterEntriesForAccount(long accountId);
-}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java
new file mode 100644
index 0000000..e0a0d24
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java
@@ -0,0 +1,23 @@
+package org.mercury_im.messenger.persistence.room.model;
+
+import androidx.lifecycle.LiveData;
+import androidx.paging.PagedList;
+import androidx.room.Entity;
+
+import org.mercury_im.messenger.persistence.model.ChatModel;
+import org.mercury_im.messenger.persistence.model.ContactModel;
+import org.mercury_im.messenger.persistence.model.MessageModel;
+
+@Entity
+public class RoomChatModel implements ChatModel {
+
+ @Override
+ public LiveData getContact() {
+ return null;
+ }
+
+ @Override
+ public LiveData> getMessages() {
+ return null;
+ }
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterEntryModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java
similarity index 50%
rename from persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterEntryModel.java
rename to persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java
index f4d4713..5011f7f 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterEntryModel.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java
@@ -1,28 +1,37 @@
package org.mercury_im.messenger.persistence.room.model;
+import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
-import androidx.room.PrimaryKey;
+import androidx.room.Index;
import androidx.room.TypeConverters;
import org.jxmpp.jid.EntityBareJid;
-import org.mercury_im.messenger.persistence.model.RosterEntryModel;
+import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
+import java.io.File;
+
import static androidx.room.ForeignKey.CASCADE;
+import static androidx.room.ForeignKey.RESTRICT;
-@Entity(tableName = "roster", foreignKeys = @ForeignKey(entity = RoomAccountModel.class,
- parentColumns = "id",
- childColumns = "accountId",
- onDelete = CASCADE))
-public class RoomRosterEntryModel implements RosterEntryModel {
-
- @PrimaryKey(autoGenerate = true)
- public long id;
+@Entity(primaryKeys = {"accountId", "jid"},
+ indices = {@Index(value = {"accountId", "jid"}, unique = true)},
+ foreignKeys = {
+ @ForeignKey(entity = RoomAccountModel.class,
+ parentColumns = "id",
+ childColumns = "accountId",
+ onDelete = CASCADE),
+ @ForeignKey(entity = RoomXmppIdentityModel.class,
+ parentColumns = {"accountId", "jid"},
+ childColumns = {"accountId", "jid"},
+ onDelete = RESTRICT)})
+public class RoomContactModel implements ContactModel {
private long accountId;
+ @NonNull
@TypeConverters(EntityBareJidConverter.class)
private EntityBareJid jid;
@@ -30,27 +39,27 @@ public class RoomRosterEntryModel implements RosterEntryModel {
private String nickname;
- public RoomRosterEntryModel(EntityBareJid jid, String rosterName, String nickname) {
+ private File avatarFile;
+
+ public RoomContactModel(@NonNull EntityBareJid jid,
+ String rosterName,
+ String nickname) {
this.jid = jid;
this.nickname = nickname;
this.rosterName = rosterName;
}
- @Override
- public long getId() {
- return id;
- }
-
- @Override
- public void setId(long id) {
- this.id = id;
- }
-
+ @NonNull
@Override
public EntityBareJid getJid() {
return jid;
}
+ @Override
+ public void setJid(EntityBareJid jid) {
+
+ }
+
@Override
public String getRosterName() {
return rosterName;
@@ -80,4 +89,15 @@ public class RoomRosterEntryModel implements RosterEntryModel {
public void setAccountId(long accountId) {
this.accountId = accountId;
}
+
+ @Override
+ public File getAvatarFile() {
+ return avatarFile;
+ }
+
+ @Override
+ public void setAvatarFile(File file) {
+ this.avatarFile = file;
+ }
+
}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomXmppIdentityModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomXmppIdentityModel.java
new file mode 100644
index 0000000..c79bc08
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomXmppIdentityModel.java
@@ -0,0 +1,62 @@
+package org.mercury_im.messenger.persistence.room.model;
+
+import android.graphics.Bitmap;
+
+import androidx.annotation.NonNull;
+import androidx.room.Entity;
+import androidx.room.ForeignKey;
+import androidx.room.Index;
+
+import org.jxmpp.jid.EntityBareJid;
+import org.mercury_im.messenger.persistence.model.XmppIdentityModel;
+
+import java.io.File;
+
+import static androidx.room.ForeignKey.CASCADE;
+
+@Entity(tableName = "entities", primaryKeys = {"accountId", "jid"},
+ indices = {@Index(value = {"accountId", "jid"}, unique = true)},
+ foreignKeys = @ForeignKey(entity = RoomAccountModel.class,
+ parentColumns = "id",
+ childColumns = "accountId",
+ onDelete = CASCADE))
+public class RoomXmppIdentityModel implements XmppIdentityModel {
+
+ protected long accountId;
+
+ protected EntityBareJid jid;
+
+ protected File avatarFile;
+
+ @NonNull
+ @Override
+ public EntityBareJid getJid() {
+ return jid;
+ }
+
+ @Override
+ public void setJid(EntityBareJid jid) {
+ this.jid = jid;
+ }
+
+ @Override
+ public long getAccountId() {
+ return accountId;
+ }
+
+ @Override
+ public void setAccountId(long accountId) {
+ this.accountId = accountId;
+ }
+
+ @Override
+ public File getAvatarFile() {
+ return avatarFile;
+ }
+
+ @Override
+ public void setAvatarFile(File file) {
+ this.avatarFile = file;
+ }
+
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java
index a752ddb..e390d89 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java
@@ -28,10 +28,16 @@ public class IAccountRepository implements AccountRepository {
}
@Override
- public LiveData> getAllAccounts() {
+ public LiveData> getAllAccountsLive() {
+ return accountDao.getAllAccountsLive();
+ }
+
+ @Override
+ public List getAllAccounts() {
return accountDao.getAllAccounts();
}
+
@Override
public long insertAccount(RoomAccountModel accountModel) {
InsertAsyncTask task = new InsertAsyncTask(accountDao);
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java
new file mode 100644
index 0000000..49cac45
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java
@@ -0,0 +1,7 @@
+package org.mercury_im.messenger.persistence.room.repository;
+
+import org.mercury_im.messenger.persistence.repository.ChatRepository;
+
+public class IChatRepository implements ChatRepository {
+
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IContactRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IContactRepository.java
new file mode 100644
index 0000000..3b6cfe0
--- /dev/null
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IContactRepository.java
@@ -0,0 +1,30 @@
+package org.mercury_im.messenger.persistence.room.repository;
+
+import androidx.lifecycle.LiveData;
+
+import org.mercury_im.messenger.persistence.repository.ContactRepository;
+import org.mercury_im.messenger.persistence.room.dao.ContactDao;
+import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
+
+import java.util.List;
+
+public class IContactRepository implements ContactRepository {
+
+ private final ContactDao contactDao;
+
+ public IContactRepository(ContactDao dao) {
+ this.contactDao = dao;
+ }
+
+ @Override
+ public LiveData> getAllRosterEntries() {
+ return contactDao.getAllRosterEntries();
+ }
+
+ @Override
+ public void updateOrInsertRosterEntry(RoomContactModel rosterEntryModel) {
+ contactDao.insert(rosterEntryModel);
+ }
+
+
+}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java
index 1ae2906..d2edf64 100644
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java
+++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java
@@ -1,7 +1,35 @@
package org.mercury_im.messenger.persistence.room.repository;
+import androidx.lifecycle.LiveData;
+
+import org.jxmpp.jid.EntityBareJid;
+import org.jxmpp.jid.EntityFullJid;
import org.mercury_im.messenger.persistence.repository.MessageRepository;
+import org.mercury_im.messenger.persistence.room.dao.MessageDao;
import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
+import java.util.List;
+
public class IMessageRepository implements MessageRepository {
+
+ private final MessageDao messageDao;
+
+ public IMessageRepository(MessageDao messageDao) {
+ this.messageDao = messageDao;
+ }
+
+ @Override
+ public long insertMessage(RoomMessageModel message) {
+ return messageDao.insert(message);
+ }
+
+ @Override
+ public LiveData> getAllMessagesOf(long accountId) {
+ return messageDao.getAllMessagesOf(accountId);
+ }
+
+ @Override
+ public LiveData> getAllMessagesFrom(long accountId, EntityFullJid contact) {
+ return messageDao.getAllMessagesFrom(accountId, contact);
+ }
}
diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterEntryRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterEntryRepository.java
deleted file mode 100644
index aedc2c2..0000000
--- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterEntryRepository.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.mercury_im.messenger.persistence.room.repository;
-
-import androidx.lifecycle.LiveData;
-
-import org.mercury_im.messenger.persistence.model.RosterEntryModel;
-import org.mercury_im.messenger.persistence.repository.RosterEntryRepository;
-import org.mercury_im.messenger.persistence.room.dao.RosterEntryDao;
-import org.mercury_im.messenger.persistence.room.model.RoomRosterEntryModel;
-
-import java.util.List;
-
-public class IRosterEntryRepository implements RosterEntryRepository {
-
- private final RosterEntryDao rosterEntryDao;
-
- public IRosterEntryRepository(RosterEntryDao dao) {
- this.rosterEntryDao = dao;
- }
-
- @Override
- public LiveData> getAllRosterEntries() {
- return rosterEntryDao.getAllRosterEntries();
- }
-
- @Override
- public void updateOrInsertRosterEntry(RoomRosterEntryModel rosterEntryModel) {
- rosterEntryDao.insert(rosterEntryModel);
- }
-
-
-}
diff --git a/persistence/build.gradle b/persistence/build.gradle
index 88db8a2..cc645e7 100644
--- a/persistence/build.gradle
+++ b/persistence/build.gradle
@@ -37,4 +37,5 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ api "androidx.paging:paging-runtime:$pagingVersion"
}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java
new file mode 100644
index 0000000..b802f9a
--- /dev/null
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java
@@ -0,0 +1,14 @@
+package org.mercury_im.messenger.persistence.model;
+
+import org.jxmpp.jid.EntityBareJid;
+
+public interface ChatModel {
+
+ EntityBareJid getJid();
+
+ void setJid(EntityBareJid jid);
+
+ long getAccountId();
+
+ void setAccountId();
+}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/RosterEntryModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java
similarity index 70%
rename from persistence/src/main/java/org/mercury_im/messenger/persistence/model/RosterEntryModel.java
rename to persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java
index a539305..1d8382d 100644
--- a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/RosterEntryModel.java
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java
@@ -2,14 +2,16 @@ package org.mercury_im.messenger.persistence.model;
import org.jxmpp.jid.EntityBareJid;
-public interface RosterEntryModel {
+public interface ContactModel {
- long getId();
+ long getAccountId();
- void setId(long id);
+ void setAccountId(long id);
EntityBareJid getJid();
+ void setJid(EntityBareJid jid);
+
String getRosterName();
void setRosterName(String rosterName);
@@ -17,8 +19,4 @@ public interface RosterEntryModel {
String getNickname();
void setNickname(String nickname);
-
- long getAccountId();
-
- void setAccountId(long accountId);
}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/MessageModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/MessageModel.java
index f1914b7..7795238 100644
--- a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/MessageModel.java
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/MessageModel.java
@@ -1,6 +1,6 @@
package org.mercury_im.messenger.persistence.model;
-import org.jxmpp.jid.EntityFullJid;
+import org.jxmpp.jid.EntityBareJid;
import java.util.Date;
@@ -22,11 +22,11 @@ public interface MessageModel {
void setSendDate(Date date);
- EntityFullJid getFrom();
+ EntityBareJid getFrom();
- void setFrom(EntityFullJid sender);
+ void setFrom(EntityBareJid sender);
- EntityFullJid getTo();
+ EntityBareJid getTo();
- void setTo(EntityFullJid recipient);
+ void setTo(EntityBareJid recipient);
}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/XmppIdentityModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/XmppIdentityModel.java
new file mode 100644
index 0000000..38ac1e8
--- /dev/null
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/XmppIdentityModel.java
@@ -0,0 +1,40 @@
+package org.mercury_im.messenger.persistence.model;
+
+import androidx.annotation.NonNull;
+
+import org.jxmpp.jid.EntityBareJid;
+
+import java.io.File;
+
+/**
+ * An {@link XmppIdentityModel} represents an XMPP user as seen by an account.
+ * Its primary key should be composited of its {@link EntityBareJid} and the primary key of the
+ * {@link AccountModel} which is communicating with the user.
+ */
+public interface XmppIdentityModel {
+
+ /**
+ * Return the JID of this identity.
+ * Should be part of the composited primary key.
+ *
+ * @return jid
+ */
+ @NonNull
+ EntityBareJid getJid();
+
+ void setJid(EntityBareJid jid);
+
+ /**
+ * Return the primary key of the account which is communicating with the
+ * {@link XmppIdentityModel}.
+ *
+ * @return accountId
+ */
+ long getAccountId();
+
+ void setAccountId(long accountId);
+
+ File getAvatarFile();
+
+ void setAvatarFile(File file);
+}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java
index 8edf448..62419f3 100644
--- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java
@@ -10,7 +10,9 @@ public interface AccountRepository {
LiveData getAccount(long accountId);
- LiveData> getAllAccounts();
+ LiveData> getAllAccountsLive();
+
+ List getAllAccounts();
long insertAccount(E accountModel);
}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java
new file mode 100644
index 0000000..9e1dbe9
--- /dev/null
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java
@@ -0,0 +1,18 @@
+package org.mercury_im.messenger.persistence.repository;
+
+import androidx.lifecycle.LiveData;
+
+import org.mercury_im.messenger.persistence.model.ChatModel;
+import org.mercury_im.messenger.persistence.model.ContactModel;
+import org.mercury_im.messenger.persistence.model.XmppIdentityModel;
+
+import java.util.List;
+
+public interface ChatRepository {
+
+ LiveData> getAllChats();
+
+ void getChatWith(XmppIdentityModel identity);
+
+ void closeChat(ChatModel chat);
+}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterEntryRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ContactRepository.java
similarity index 62%
rename from persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterEntryRepository.java
rename to persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ContactRepository.java
index c91f510..dff6450 100644
--- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterEntryRepository.java
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ContactRepository.java
@@ -2,11 +2,11 @@ package org.mercury_im.messenger.persistence.repository;
import androidx.lifecycle.LiveData;
-import org.mercury_im.messenger.persistence.model.RosterEntryModel;
+import org.mercury_im.messenger.persistence.model.ContactModel;
import java.util.List;
-public interface RosterEntryRepository {
+public interface ContactRepository {
LiveData> getAllRosterEntries();
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java
index 6c1dbbf..35c605c 100644
--- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java
@@ -1,7 +1,20 @@
package org.mercury_im.messenger.persistence.repository;
+import androidx.lifecycle.LiveData;
+
+import org.jxmpp.jid.EntityBareJid;
+import org.jxmpp.jid.EntityFullJid;
import org.mercury_im.messenger.persistence.model.MessageModel;
+import java.util.List;
+
public interface MessageRepository {
+ long insertMessage(E message);
+
+ LiveData> getAllMessagesOf(long accountId);
+
+ LiveData> getAllMessagesFrom(long accountId, EntityBareJid contact);
+
+ LiveData> findMessageByQuery(String query);
}
diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/XmppIdentityRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/XmppIdentityRepository.java
new file mode 100644
index 0000000..e663a26
--- /dev/null
+++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/XmppIdentityRepository.java
@@ -0,0 +1,12 @@
+package org.mercury_im.messenger.persistence.repository;
+
+import androidx.lifecycle.LiveData;
+
+import org.mercury_im.messenger.persistence.model.ContactModel;
+import org.mercury_im.messenger.persistence.model.XmppIdentityModel;
+
+public interface XmppIdentityRepository {
+
+ LiveData getIdentityOf(C contact);
+
+}
diff --git a/settings.gradle b/settings.gradle
index c9fd981..b17374e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':xmpp_core', ':persistence-room', ':persistence'
+include ':app', ':xmpp_core', ':persistence-room', ':persistence', ':xmpp_android'
diff --git a/version.gradle b/version.gradle
index 14482b4..cbf10cf 100644
--- a/version.gradle
+++ b/version.gradle
@@ -38,6 +38,7 @@ ext {
// Architecture Components
lifecycleVersion = "2.0.0"
roomVersion = "2.1.0-beta01"
+ pagingVersion = "2.1.0"
// Dagger 2
daggerVersion = "2.17"
diff --git a/xmpp_android/.gitignore b/xmpp_android/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/xmpp_android/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/xmpp_android/build.gradle b/xmpp_android/build.gradle
new file mode 100644
index 0000000..032be9b
--- /dev/null
+++ b/xmpp_android/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ api project(":xmpp_core")
+
+ // Android related Smack libraries
+ api "org.igniterealtime.smack:smack-android:$smackAndroidVersion"
+ api "org.igniterealtime.smack:smack-android-extensions:$smackAndroidExtensionsVersion"
+
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+}
diff --git a/xmpp_android/proguard-rules.pro b/xmpp_android/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/xmpp_android/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/xmpp_android/src/androidTest/java/org/mercury_im/messenger/xmpp_android/ExampleInstrumentedTest.java b/xmpp_android/src/androidTest/java/org/mercury_im/messenger/xmpp_android/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..ae33fbb
--- /dev/null
+++ b/xmpp_android/src/androidTest/java/org/mercury_im/messenger/xmpp_android/ExampleInstrumentedTest.java
@@ -0,0 +1,27 @@
+package org.mercury_im.messenger.xmpp_android;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("org.mercury_im.messenger.xmpp_android.test", appContext.getPackageName());
+ }
+}
diff --git a/xmpp_android/src/main/AndroidManifest.xml b/xmpp_android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..75e04bc
--- /dev/null
+++ b/xmpp_android/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/AndroidMercuryConnection.java b/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/AndroidMercuryConnection.java
new file mode 100644
index 0000000..08a6a50
--- /dev/null
+++ b/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/AndroidMercuryConnection.java
@@ -0,0 +1,11 @@
+package org.mercury_im.messenger.xmpp_android;
+
+import org.jivesoftware.smack.XMPPConnection;
+import org.mercury_im.messenger.xmpp_core.MercuryConnection;
+
+public class AndroidMercuryConnection extends MercuryConnection {
+
+ public AndroidMercuryConnection(XMPPConnection connection, long accountId) {
+ super(connection, accountId);
+ }
+}
diff --git a/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/ParcelableXMPPTCPConnectionConfiguration.java b/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/ParcelableXMPPTCPConnectionConfiguration.java
new file mode 100644
index 0000000..4178aeb
--- /dev/null
+++ b/xmpp_android/src/main/java/org/mercury_im/messenger/xmpp_android/ParcelableXMPPTCPConnectionConfiguration.java
@@ -0,0 +1,86 @@
+package org.mercury_im.messenger.xmpp_android;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
+import org.jxmpp.stringprep.XmppStringprepException;
+
+public class ParcelableXMPPTCPConnectionConfiguration implements Parcelable {
+
+ private final String username;
+ private final String password;
+ private final String xmppDomain;
+ private final String resourcePart;
+ private final String host;
+ private final int port;
+
+ private XMPPTCPConnectionConfiguration configuration;
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public ParcelableXMPPTCPConnectionConfiguration createFromParcel(Parcel in) {
+ return new ParcelableXMPPTCPConnectionConfiguration(in);
+ }
+
+ @Override
+ public ParcelableXMPPTCPConnectionConfiguration[] newArray(int size) {
+ return new ParcelableXMPPTCPConnectionConfiguration[size];
+ }
+ };
+
+ public ParcelableXMPPTCPConnectionConfiguration(String username,
+ String password,
+ String xmppDomain,
+ String resourcePart,
+ String host,
+ int port) {
+ this.username = username;
+ this.password = password;
+ this.xmppDomain = xmppDomain;
+ this.resourcePart = resourcePart;
+ this.host = host;
+ this.port = port;
+ }
+
+ private ParcelableXMPPTCPConnectionConfiguration(Parcel in) {
+ this(in.readString(), // username
+ in.readString(), // password
+ in.readString(), // xmppDomain
+ in.readString(), // resourcePart
+ in.readString(), // host
+ in.readInt()); // port
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(username);
+ parcel.writeString(password);
+ parcel.writeString(xmppDomain);
+ parcel.writeString(resourcePart);
+ parcel.writeString(host);
+ parcel.writeInt(port);
+ }
+
+ public XMPPTCPConnectionConfiguration getConfiguration() throws XmppStringprepException {
+ if (configuration != null) {
+ return configuration;
+ }
+
+ XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
+
+ builder.setUsernameAndPassword(username, password);
+ if (xmppDomain != null) builder.setXmppDomain(xmppDomain);
+ if (resourcePart != null) builder.setResource(resourcePart);
+ if (host != null) builder.setHost(host);
+ if (port != -1) builder.setPort(port);
+
+ configuration = builder.build();
+ return configuration;
+ }
+}
diff --git a/xmpp_android/src/main/res/values/strings.xml b/xmpp_android/src/main/res/values/strings.xml
new file mode 100644
index 0000000..15ade09
--- /dev/null
+++ b/xmpp_android/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ XmppAndroid
+
diff --git a/xmpp_android/src/test/java/org/mercury_im/messenger/xmpp_android/ExampleUnitTest.java b/xmpp_android/src/test/java/org/mercury_im/messenger/xmpp_android/ExampleUnitTest.java
new file mode 100644
index 0000000..c352d5a
--- /dev/null
+++ b/xmpp_android/src/test/java/org/mercury_im/messenger/xmpp_android/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package org.mercury_im.messenger.xmpp_android;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/xmpp_core/build.gradle b/xmpp_core/build.gradle
index f423b31..1b50911 100644
--- a/xmpp_core/build.gradle
+++ b/xmpp_core/build.gradle
@@ -1,10 +1,9 @@
apply plugin: 'java-library'
dependencies {
+
// Smack
// Not all of those are needed, but it may be a good idea to define those versions explicitly
- api "org.igniterealtime.smack:smack-android:$smackAndroidVersion"
- api "org.igniterealtime.smack:smack-android-extensions:$smackAndroidExtensionsVersion"
api "org.igniterealtime.smack:smack-core:$smackCoreVersion"
api "org.igniterealtime.smack:smack-experimental:$smackExperimentalVersion"
api "org.igniterealtime.smack:smack-extensions:$smackExtensionsVersion"
diff --git a/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/ConnectionState.java b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/ConnectionState.java
new file mode 100644
index 0000000..536123e
--- /dev/null
+++ b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/ConnectionState.java
@@ -0,0 +1,43 @@
+package org.mercury_im.messenger.xmpp_core;
+
+/**
+ * {@link MercuryConnection} modeled as a finite state machine.
+ * Below enums represent the states of the machine.
+ */
+public enum ConnectionState {
+
+ /**
+ * Connection is disconnected.
+ * This is the initial state of the machine.
+ */
+ DISCONNECTED,
+
+ /**
+ * The connection is in the process of connecting to the server.
+ * This state can be reached by issuing a connect() call from within the {@link #DISCONNECTED}
+ * state.
+ */
+ CONNECTING,
+
+ /**
+ * The connection is successfully connected to the server and the stream has been initiated.
+ * In this state the connection is ready to send and receive stanzas.
+ */
+ CONNECTED,
+
+ /**
+ * The connection is in the process of shutting down.
+ */
+ DISCONNECTING,
+
+ /**
+ * The device doesn't have usable network connectivity.
+ */
+ WAITING_FOR_NETWORK,
+
+ /**
+ * The connection already (unsuccessfully) tried to connect, but failed due to lack of network
+ * connectivity and is now waiting to retry connecting.
+ */
+ WAIRING_FOR_RETRY
+}
diff --git a/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/MercuryConnection.java b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/MercuryConnection.java
new file mode 100644
index 0000000..5dd380b
--- /dev/null
+++ b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/MercuryConnection.java
@@ -0,0 +1,36 @@
+package org.mercury_im.messenger.xmpp_core;
+
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.roster.Roster;
+
+public class MercuryConnection {
+
+ private final XMPPConnection connection;
+ private final long accountId;
+ private final Roster roster;
+
+ public MercuryConnection(XMPPConnection connection, long accountId) {
+ this.connection = connection;
+ this.accountId = accountId;
+
+ this.roster = Roster.getInstanceFor(connection);
+ roster.setRosterLoadedAtLogin(true);
+ }
+
+ public XMPPConnection getConnection() {
+ return connection;
+ }
+
+ public void setRosterHandler(RosterHandler handler) {
+ roster.addRosterListener(handler);
+ roster.addRosterLoadedListener(handler);
+ }
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ public Roster getRoster() {
+ return roster;
+ }
+}
diff --git a/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/RosterHandler.java b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/RosterHandler.java
new file mode 100644
index 0000000..1b54bd2
--- /dev/null
+++ b/xmpp_core/src/main/java/org/mercury_im/messenger/xmpp_core/RosterHandler.java
@@ -0,0 +1,8 @@
+package org.mercury_im.messenger.xmpp_core;
+
+import org.jivesoftware.smack.roster.RosterListener;
+import org.jivesoftware.smack.roster.RosterLoadedListener;
+
+public interface RosterHandler extends RosterListener, RosterLoadedListener {
+
+}
diff --git a/xmpp_core/src/main/java/org/mercury_im/xmpp_core/MercuryConnection.java b/xmpp_core/src/main/java/org/mercury_im/xmpp_core/MercuryConnection.java
deleted file mode 100644
index 23b2ec5..0000000
--- a/xmpp_core/src/main/java/org/mercury_im/xmpp_core/MercuryConnection.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.mercury_im.xmpp_core;
-
-import org.jivesoftware.smack.XMPPConnection;
-
-public class MercuryConnection {
-
- private final XMPPConnection connection;
-
- public MercuryConnection(XMPPConnection connection) {
- this.connection = connection;
- }
-
- public XMPPConnection getConnection() {
- return connection;
- }
-}