This commit is contained in:
Paul Schaub 2019-06-20 17:20:23 +02:00
parent 3254cb4b97
commit 9477059b0b
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
43 changed files with 577 additions and 393 deletions

View File

@ -90,12 +90,12 @@ dependencies {
annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnifeVersion" annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnifeVersion"
// support libraries // support libraries
implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.0.1' implementation 'androidx.vectordrawable:vectordrawable:1.0.1'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

View File

@ -5,6 +5,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build; import android.os.Build;
import org.mercury_im.messenger.di.component.AppComponent; import org.mercury_im.messenger.di.component.AppComponent;

View File

@ -6,6 +6,7 @@ import org.mercury_im.messenger.di.module.RepositoryModule;
import org.mercury_im.messenger.di.module.RoomModule; import org.mercury_im.messenger.di.module.RoomModule;
import org.mercury_im.messenger.service.XmppConnectionService; import org.mercury_im.messenger.service.XmppConnectionService;
import org.mercury_im.messenger.ui.MainActivity; import org.mercury_im.messenger.ui.MainActivity;
import org.mercury_im.messenger.ui.RoomRosterHandler;
import org.mercury_im.messenger.ui.chat.ChatActivity; import org.mercury_im.messenger.ui.chat.ChatActivity;
import org.mercury_im.messenger.ui.chat.ChatInputFragment; import org.mercury_im.messenger.ui.chat.ChatInputFragment;
import org.mercury_im.messenger.ui.chat.ChatInputViewModel; import org.mercury_im.messenger.ui.chat.ChatInputViewModel;
@ -15,7 +16,6 @@ import org.mercury_im.messenger.ui.login.AccountsViewModel;
import org.mercury_im.messenger.ui.login.LoginActivity; import org.mercury_im.messenger.ui.login.LoginActivity;
import org.mercury_im.messenger.ui.login.LoginViewModel; import org.mercury_im.messenger.ui.login.LoginViewModel;
import org.mercury_im.messenger.ui.roster.RosterViewModel; import org.mercury_im.messenger.ui.roster.RosterViewModel;
import org.mercury_im.messenger.xmpp_database_connector.RosterConnector;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -63,5 +63,5 @@ public interface AppComponent {
// Connectors // Connectors
void inject(RosterConnector connector); void inject(RoomRosterHandler roomRosterHandler);
} }

View File

@ -1,11 +1,20 @@
package org.mercury_im.messenger.di.module; package org.mercury_im.messenger.di.module;
import org.mercury_im.messenger.persistence.repository.AccountRepository; import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.repository.ChatRepository;
import org.mercury_im.messenger.persistence.repository.ContactRepository; import org.mercury_im.messenger.persistence.repository.ContactRepository;
import org.mercury_im.messenger.persistence.repository.EntityRepository;
import org.mercury_im.messenger.persistence.repository.MessageRepository;
import org.mercury_im.messenger.persistence.room.dao.AccountDao; import org.mercury_im.messenger.persistence.room.dao.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.ChatDao;
import org.mercury_im.messenger.persistence.room.dao.ContactDao; import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao;
import org.mercury_im.messenger.persistence.room.repository.IAccountRepository; import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
import org.mercury_im.messenger.persistence.room.repository.IChatRepository;
import org.mercury_im.messenger.persistence.room.repository.IContactRepository; import org.mercury_im.messenger.persistence.room.repository.IContactRepository;
import org.mercury_im.messenger.persistence.room.repository.IEntityRepository;
import org.mercury_im.messenger.persistence.room.repository.IMessageRepository;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -23,7 +32,26 @@ public class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
ContactRepository provideRosterEntryRepository(ContactDao dao) { EntityRepository provideEntityRepository(EntityDao dao) {
return new IEntityRepository(dao);
}
@Singleton
@Provides
ContactRepository provideContactRepository(ContactDao dao) {
return new IContactRepository(dao); return new IContactRepository(dao);
} }
@Singleton
@Provides
ChatRepository provideChatRepository(ChatDao dao) {
return new IChatRepository(dao);
}
@Singleton
@Provides
MessageRepository provideMessageRepository(MessageDao dao) {
return new IMessageRepository(dao);
}
} }

View File

@ -3,8 +3,10 @@ package org.mercury_im.messenger.di.module;
import org.mercury_im.messenger.MercuryImApplication; import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.persistence.room.AppDatabase; 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.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao; import org.mercury_im.messenger.persistence.room.dao.MessageDao;
import org.mercury_im.messenger.persistence.room.dao.ContactDao; import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.w3c.dom.Entity;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -48,4 +50,10 @@ public class RoomModule {
MessageDao provideMessageDao() { MessageDao provideMessageDao() {
return mAppDatabase.messageDao(); return mAppDatabase.messageDao();
} }
@Singleton
@Provides
EntityDao provideEntityDao() {
return mAppDatabase.entityDao();
}
} }

View File

@ -3,11 +3,8 @@ package org.mercury_im.messenger.service;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
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;
@ -16,9 +13,10 @@ import android.util.LongSparseArray;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
@ -27,6 +25,7 @@ import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.chat2.ChatManager;
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.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.RosterLoadedListener; import org.jivesoftware.smack.roster.RosterLoadedListener;
@ -34,19 +33,31 @@ import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager; import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.mercury_im.messenger.MercuryImApplication; import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.Notifications; import org.mercury_im.messenger.Notifications;
import org.mercury_im.messenger.R; import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.AccountModel; import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository; import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.repository.ContactRepository; import org.mercury_im.messenger.persistence.repository.ContactRepository;
import org.mercury_im.messenger.persistence.room.AppDatabase; import org.mercury_im.messenger.persistence.repository.EntityRepository;
import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel; 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.RoomContactModel;
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
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.IEntityRepository;
import org.mercury_im.messenger.ui.MainActivity; import org.mercury_im.messenger.ui.MainActivity;
import org.mercury_im.messenger.xmpp_android.ParcelableXMPPTCPConnectionConfiguration; import org.mercury_im.messenger.xmpp_android.ParcelableXMPPTCPConnectionConfiguration;
import org.mercury_im.messenger.xmpp_core.ConnectionHolder;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
import org.mercury_im.messenger.xmpp_core.RosterHandler;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -56,11 +67,11 @@ import javax.inject.Inject;
* Started, Bound Service, which is responsible for managing {@link XMPPConnection XMPPConnections} * Started, Bound Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
* affiliated with registered accounts. * affiliated with registered accounts.
*/ */
public class XmppConnectionService extends Service { public class XmppConnectionService extends Service implements ConnectionHolder {
private static final String TAG = MercuryImApplication.TAG; private static final String TAG = MercuryImApplication.TAG;
private static final String APP = "org.olomono.mercury"; private static final String APP = "org.mercury-im.messenger";
private static final String SERVICE = APP + ".XmppConnectionService"; private static final String SERVICE = APP + ".XmppConnectionService";
private static final String ACTION = SERVICE + ".ACTION"; private static final String ACTION = SERVICE + ".ACTION";
@ -87,14 +98,22 @@ public class XmppConnectionService extends Service {
public static final String STATUS_SUCCESS = STATUS + ".SUCCESS"; public static final String STATUS_SUCCESS = STATUS + ".SUCCESS";
public static final String STATUS_FAILURE = STATUS + ".FAILURE"; public static final String STATUS_FAILURE = STATUS + ".FAILURE";
@Inject @Inject
ContactRepository rosterRepository; ContactRepository contactRepository;
@Inject @Inject
AccountRepository accountRepository; AccountRepository accountRepository;
private final LongSparseArray<XMPPConnection> connections = new LongSparseArray<>(); @Inject
EntityRepository entityRepository;
@Inject
EntityDao entityDao;
@Inject
ContactDao contactDao;
private final LongSparseArray<MercuryConnection> connections = new LongSparseArray<>();
private Handler uiHandler; private Handler uiHandler;
@ -125,23 +144,18 @@ public class XmppConnectionService extends Service {
ServerPingWithAlarmManager.onDestroy(); ServerPingWithAlarmManager.onDestroy();
} }
@Override
public MercuryConnection getConnection(long accountId) {
return connections.get(accountId);
}
@Override
public void putConnection(long accountId, MercuryConnection connection) {
connections.put(accountId, connection);
}
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { 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(XmppConnectionService.this, (m.getFrom() != null ? m.getFrom().toString() : "null") + ": "
+ m.getBody(), Toast.LENGTH_LONG).show();
}
}
};
}
Log.d(TAG, "onStartCommand(" + intent + ")"); Log.d(TAG, "onStartCommand(" + intent + ")");
if (intent == null) { if (intent == null) {
startAndDisplayForegroundNotification(); startAndDisplayForegroundNotification();
@ -152,30 +166,10 @@ public class XmppConnectionService extends Service {
switch (action) { switch (action) {
case ACTION_START: case ACTION_START:
startAndDisplayForegroundNotification(); startAndDisplayForegroundNotification();
startConnections();
break; break;
case ACTION_STOP: case ACTION_STOP:
stopForeground(true); stopForeground(true);
break; stopSelf();
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; break;
default: default:
break; break;
@ -204,100 +198,6 @@ public class XmppConnectionService extends Service {
.build(); .build();
} }
public void startConnections() {
new Thread() {
@Override
public void run() {
synchronized (connections) {
List<RoomAccountModel> 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<RosterEntry> entries = roster.getEntries();
for (RosterEntry e : entries) {
Log.d(TAG, "Inserting Roster entry " + e.getJid().toString());
RoomContactModel contact = new RoomContactModel();
contact.setAccountId(accountId);
// TODO: Fix
}
}
@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);
}
public class Binder extends android.os.Binder { public class Binder extends android.os.Binder {
private final XmppConnectionService service; private final XmppConnectionService service;

View File

@ -0,0 +1,55 @@
package org.mercury_im.messenger.ui;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import androidx.appcompat.app.AppCompatActivity;
import org.mercury_im.messenger.service.XmppConnectionService;
public abstract class BindingActivity extends AppCompatActivity {
protected XmppConnectionService connectionService;
protected boolean bound;
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, XmppConnectionService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(serviceConnection);
bound = false;
}
protected void onServiceBound() {
}
protected void onServiceUnbound() {
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
XmppConnectionService.Binder binder = (XmppConnectionService.Binder) iBinder;
connectionService = binder.getService();
bound = true;
onServiceBound();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
onServiceUnbound();
}
};
}

View File

@ -1,13 +1,19 @@
package org.mercury_im.messenger.ui; package org.mercury_im.messenger.ui;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import android.os.IBinder;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -18,6 +24,7 @@ import org.mercury_im.messenger.persistence.room.AppDatabase;
import org.mercury_im.messenger.persistence.room.model.RoomContactModel; import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository; import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.repository.ContactRepository; import org.mercury_im.messenger.persistence.repository.ContactRepository;
import org.mercury_im.messenger.service.XmppConnectionService;
import org.mercury_im.messenger.ui.chat.ChatActivity; import org.mercury_im.messenger.ui.chat.ChatActivity;
import org.mercury_im.messenger.ui.login.AccountsActivity; import org.mercury_im.messenger.ui.login.AccountsActivity;
import org.mercury_im.messenger.ui.login.LoginActivity; import org.mercury_im.messenger.ui.login.LoginActivity;
@ -27,7 +34,7 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
public class MainActivity extends AppCompatActivity { public class MainActivity extends BindingActivity {
@Inject @Inject

View File

@ -0,0 +1,100 @@
package org.mercury_im.messenger.ui;
import android.util.Log;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.persistence.room.dao.ContactDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
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 RoomRosterHandler implements RosterHandler {
@Inject
ContactDao contactDao;
@Inject
EntityDao entityDao;
private final long accountId;
private final Roster roster;
public RoomRosterHandler(long accountId, Roster roster) {
this.accountId = accountId;
this.roster = roster;
MercuryImApplication.getApplication().getAppComponent().inject(this);
}
@Override
public void entriesAdded(Collection<Jid> addresses) {
for (Jid jid : addresses) {
RoomContactModel contactModel = getContactModel(jid);
contactModel.setNickname(roster.getEntry(jid.asBareJid()).getName());
contactDao.insert(contactModel);
}
}
@Override
public void entriesUpdated(Collection<Jid> addresses) {
for (Jid jid : addresses) {
RoomContactModel contactModel = getContactModel(jid);
contactModel.setNickname(roster.getEntry(jid.asBareJid()).getName());
contactDao.update(contactModel);
}
}
@Override
public void entriesDeleted(Collection<Jid> addresses) {
for (Jid jid : addresses) {
RoomContactModel contactModel = getContactModel(jid);
contactDao.delete(contactModel);
}
}
@Override
public void presenceChanged(Presence presence) {
}
@Override
public void onRosterLoaded(Roster roster) {
}
@Override
public void onRosterLoadingFailed(Exception exception) {
}
private RoomContactModel getContactModel(Jid jid) {
Log.d(TAG, "Build entity " + jid + " " + accountId);
EntityBareJid entityBareJid = jid.asEntityBareJidOrThrow();
RoomEntityModel entityModel = entityDao.getEntityForSync(accountId, entityBareJid);
if (entityModel == null) {
entityModel = new RoomEntityModel();
entityModel.setAccountId(accountId);
entityModel.setJid(entityBareJid);
entityModel.setId(entityDao.insert(entityModel));
}
RoomContactModel contactModel = contactDao.syncGetContactForEntityId(entityModel.getId());
if (contactModel == null) {
contactModel = new RoomContactModel();
contactModel.setAccountId(accountId);
contactModel.setEntityId(entityModel.getId());
}
return contactModel;
}
}

View File

@ -2,6 +2,8 @@ package org.mercury_im.messenger.ui.login;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@ -16,14 +18,24 @@ import android.widget.TextView;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.MercuryImApplication; import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.R; import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.AccountModel; import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel; import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository; import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.service.XmppConnectionService; import org.mercury_im.messenger.service.XmppConnectionService;
import org.mercury_im.messenger.ui.BindingActivity;
import org.mercury_im.messenger.ui.RoomRosterHandler;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
@ -33,7 +45,7 @@ import butterknife.ButterKnife;
/** /**
* A login screen that offers login via email/password. * A login screen that offers login via email/password.
*/ */
public class LoginActivity extends AppCompatActivity implements TextView.OnEditorActionListener { public class LoginActivity extends BindingActivity implements TextView.OnEditorActionListener {
@Inject @Inject
AccountRepository accountRepository; AccountRepository accountRepository;
@ -45,6 +57,9 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
@BindView(R.id.password) @BindView(R.id.password)
TextInputEditText mPasswordView; TextInputEditText mPasswordView;
@BindView(R.id.sign_in_button)
Button mSignInView;
private LoginViewModel viewModel; private LoginViewModel viewModel;
@Override @Override
@ -63,13 +78,25 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
mJidView.setOnEditorActionListener(this); mJidView.setOnEditorActionListener(this);
mPasswordView.setOnEditorActionListener(this); mPasswordView.setOnEditorActionListener(this);
Button mEmailSignInButton = findViewById(R.id.sign_in_button); mSignInView.setOnClickListener(new OnClickListener() {
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
loginDetailsEntered(); loginDetailsEntered();
} }
}); });
mSignInView.setEnabled(false);
}
@Override
protected void onServiceBound() {
super.onServiceBound();
mSignInView.setEnabled(true);
}
@Override
protected void onServiceUnbound() {
super.onServiceUnbound();
mSignInView.setEnabled(false);
} }
private void displayCredentials(LiveData<? extends AccountModel> account) { private void displayCredentials(LiveData<? extends AccountModel> account) {
@ -115,16 +142,39 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
accountModel.setJid(jid); accountModel.setJid(jid);
accountModel.setPassword(password); accountModel.setPassword(password);
long id = accountRepository.insertAccount(accountModel); long id = accountRepository.insertAccount(accountModel);
accountModel.setId(id);
Log.d(MercuryImApplication.TAG, "LoginActivity.loginDetailsEntered: Account " + id + " inserted."); Log.d(MercuryImApplication.TAG, "LoginActivity.loginDetailsEntered: Account " + id + " inserted.");
attemptLogin(jid, password, id); attemptLogin(accountModel);
} }
} }
private void attemptLogin(EntityBareJid jid, String password, long accountId) { private void attemptLogin(AccountModel accountModel) {
Intent connectIntent = new Intent(getApplicationContext(), XmppConnectionService.class); new Thread() {
connectIntent.setAction(XmppConnectionService.ACTION_CONNECT); @Override
connectIntent.putExtra(XmppConnectionService.EXTRA_ACCOUNT_ID, accountId); public void run() {
startService(connectIntent); try {
XMPPTCPConnection connection = new XMPPTCPConnection(accountModel.getJid().getLocalpart().asUnescapedString(),
accountModel.getPassword(), accountModel.getJid().getDomain().toString());
MercuryConnection mercuryConnection = new MercuryConnection(connection, accountModel.getId());
connection.connect().login();
if (bound) {
connectionService.putConnection(accountModel.getId(), mercuryConnection);
}
} catch (XmppStringprepException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SmackException e) {
e.printStackTrace();
} catch (XMPPException e) {
e.printStackTrace();
}
}
}.start();
super.finish();
} }
/** /**

View File

@ -1,20 +1,27 @@
package org.mercury_im.messenger.ui.roster; package org.mercury_im.messenger.ui.roster;
import androidx.lifecycle.Observer; import android.content.Context;
import androidx.lifecycle.ViewModelProviders;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.os.Bundle; import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import org.bouncycastle.jcajce.provider.symmetric.Serpent;
import org.mercury_im.messenger.R; import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import static androidx.constraintlayout.widget.Constraints.TAG;
/** /**
* A placeholder fragment containing a simple view. * A placeholder fragment containing a simple view.
@ -22,8 +29,10 @@ import java.util.List;
public class RosterFragment extends Fragment { public class RosterFragment extends Fragment {
private RosterViewModel rosterViewModel; private RosterViewModel rosterViewModel;
private RosterRecyclerViewAdapter recyclerViewAdapter; private RosterRecyclerViewAdapter recyclerViewAdapter;
private RecyclerView recyclerView;
RecyclerView recyclerView;
public RosterFragment() { public RosterFragment() {
@ -38,21 +47,24 @@ public class RosterFragment extends Fragment {
recyclerViewAdapter = new RosterRecyclerViewAdapter(new ArrayList<>()); recyclerViewAdapter = new RosterRecyclerViewAdapter(new ArrayList<>());
recyclerView.setAdapter(recyclerViewAdapter); recyclerView.setAdapter(recyclerViewAdapter);
rosterViewModel = ViewModelProviders.of(getActivity()).get(RosterViewModel.class);
observeViewModel(rosterViewModel);
return view; return view;
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
rosterViewModel = ViewModelProviders.of(getActivity()).get(RosterViewModel.class);
observeViewModel(rosterViewModel);
}
private void observeViewModel(RosterViewModel viewModel) { private void observeViewModel(RosterViewModel viewModel) {
viewModel.getRosterEntryList().observe(this, new Observer<List<RoomContactModel>>() { viewModel.getRosterEntryList().observe(this, rosterEntries -> {
@Override if (rosterEntries == null) {
public void onChanged(@Nullable List<RoomContactModel> rosterEntries) { Log.d(TAG, "Displaying null roster entries");
if (rosterEntries == null) { return;
return;
}
recyclerViewAdapter.setItems(rosterEntries);
} }
recyclerViewAdapter.setItems(rosterEntries);
Log.d(TAG, "Displaying " + rosterEntries.size() + " roster entries");
}); });
} }
} }

View File

@ -24,7 +24,7 @@ public class RosterItemViewModel extends AndroidViewModel {
} }
public RosterItemViewModel setContactId(long id) { public RosterItemViewModel setContactId(long id) {
this.contact = contactRepository.getRosterEntry(id); this.contact = contactRepository.getContact(id);
return this; return this;
} }
} }

View File

@ -16,7 +16,7 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
public class RosterRecyclerViewAdapter public class RosterRecyclerViewAdapter
extends RecyclerView.Adapter<RosterRecyclerViewAdapter.AbstractRosterItem> { extends RecyclerView.Adapter<RosterRecyclerViewAdapter.RosterItemViewHolder> {
private List<RoomContactModel> entryModelList; private List<RoomContactModel> entryModelList;
@ -26,28 +26,15 @@ public class RosterRecyclerViewAdapter
@NonNull @NonNull
@Override @Override
public AbstractRosterItem onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public RosterItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// switch (viewType) { return new RosterItemViewHolder(LayoutInflater.from(parent.getContext())
// case RosterItemViewHolder.TYPE: .inflate(R.layout.recycler_view_item_roster_entry, parent, false));
return new RosterItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item_roster_entry, parent, false));
// default:
// return new RosterItemViewHolder(LayoutInflater.from(parent.getContext())
// .inflate(R.layout.recycler_view_item_roster_entry, parent, false));
// }
} }
@Override @Override
public void onBindViewHolder(@NonNull AbstractRosterItem holder, int position) { public void onBindViewHolder(@NonNull RosterItemViewHolder holder, int position) {
RoomContactModel model = entryModelList.get(position); RoomContactModel model = entryModelList.get(position);
// switch (holder.getItemViewType()) { holder.bind(model);
// case RosterItemViewHolder.TYPE:
RosterItemViewHolder rosterItem = (RosterItemViewHolder) holder;
//rosterItem.jidView.setText(model.getJid().toString());
rosterItem.nicknameView.setText(model.getNickname());
rosterItem.itemView.setTag(model);
// break;
// }
} }
@Override @Override
@ -60,31 +47,26 @@ public class RosterRecyclerViewAdapter
notifyDataSetChanged(); notifyDataSetChanged();
} }
public abstract class AbstractRosterItem extends RecyclerView.ViewHolder { public class RosterItemViewHolder extends RecyclerView.ViewHolder {
public AbstractRosterItem(@NonNull View itemView) { private View view;
super(itemView);
}
abstract void bind(RosterViewModel viewModel);
}
public class RosterItemViewHolder extends AbstractRosterItem {
@BindView(R.id.roster_entry__jid)
TextView jidView; TextView jidView;
@BindView(R.id.roster_entry__nickname)
TextView nicknameView; TextView nicknameView;
public RosterItemViewHolder(View itemView) { public RosterItemViewHolder(View itemView) {
super(itemView); super(itemView);
ButterKnife.bind(itemView); this.view = itemView;
this.jidView = itemView.findViewById(R.id.roster_entry__jid);
this.nicknameView = itemView.findViewById(R.id.roster_entry__nickname);
} }
@Override void bind(RoomContactModel contactModel) {
void bind(RosterViewModel viewModel) { // jidView.setText("" + contactModel.getEntityId());
// viewModel.getRosterEntryList().observe(); String nick = contactModel.getNickname();
nicknameView.setText(nick != null ? nick : "");
} }
} }
} }

View File

@ -24,7 +24,7 @@ public class RosterViewModel extends AndroidViewModel {
public RosterViewModel(@NonNull Application application) { public RosterViewModel(@NonNull Application application) {
super(application); super(application);
MercuryImApplication.getApplication().getAppComponent().inject(this); MercuryImApplication.getApplication().getAppComponent().inject(this);
this.rosterEntryList = contactRepository.getAllRosterEntries(); this.rosterEntryList = contactRepository.getAllContacts();
} }
public LiveData<List<RoomContactModel>> getRosterEntryList() { public LiveData<List<RoomContactModel>> getRosterEntryList() {

View File

@ -1,78 +0,0 @@
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<Jid> addresses) {
for (Jid j : addresses) {
RosterEntry entry = roster.getEntry(j.asBareJid());
// ContactModel
}
}
@Override
public void entriesUpdated(Collection<Jid> addresses) {
}
@Override
public void entriesDeleted(Collection<Jid> 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);
}
}

View File

@ -10,4 +10,4 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
tools:context="org.mercury_im.messenger.ui.login.AccountsFragment" tools:context="org.mercury_im.messenger.ui.login.AccountsFragment"
tools:listitem="@layout/fragment_account" /> tools:listitem="@layout/recycler_view_item_account" />

View File

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="org.mercury_im.messenger.ui.roster.RosterViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +24,6 @@
android:id="@+id/roster_entry__nickname" android:id="@+id/roster_entry__nickname"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{viewmodel.rosterEntryList."
tools:text="@tools:sample/full_names" tools:text="@tools:sample/full_names"
android:textSize="20sp" android:textSize="20sp"
android:textColor="@android:color/black" android:textColor="@android:color/black"

View File

@ -1,7 +1,7 @@
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light"> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>

View File

@ -2,11 +2,11 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "fb56e6a5615c4d1baa6c08d919560267", "identityHash": "51a887b7e249b513a08a53f3065136cf",
"entities": [ "entities": [
{ {
"tableName": "contacts", "tableName": "contacts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `accountId` INTEGER NOT NULL, `xmppId` INTEGER NOT NULL, `rosterName` TEXT, `nickname` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`xmppId`) REFERENCES `entities`(`id`) ON UPDATE NO ACTION ON DELETE RESTRICT )", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `accountId` INTEGER NOT NULL, `entityId` INTEGER NOT NULL, `rosterName` TEXT, `nickname` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`entityId`) REFERENCES `entities`(`id`) ON UPDATE NO ACTION ON DELETE RESTRICT )",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -21,8 +21,8 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "xmppIdentityId", "fieldPath": "entityId",
"columnName": "xmppId", "columnName": "entityId",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
@ -63,12 +63,21 @@
"createSql": "CREATE INDEX `index_contacts_accountId` ON `${TABLE_NAME}` (`accountId`)" "createSql": "CREATE INDEX `index_contacts_accountId` ON `${TABLE_NAME}` (`accountId`)"
}, },
{ {
"name": "index_contacts_xmppId", "name": "index_contacts_entityId",
"unique": false, "unique": false,
"columnNames": [ "columnNames": [
"xmppId" "entityId"
], ],
"createSql": "CREATE INDEX `index_contacts_xmppId` ON `${TABLE_NAME}` (`xmppId`)" "createSql": "CREATE INDEX `index_contacts_entityId` ON `${TABLE_NAME}` (`entityId`)"
},
{
"name": "index_contacts_id_entityId",
"unique": true,
"columnNames": [
"id",
"entityId"
],
"createSql": "CREATE UNIQUE INDEX `index_contacts_id_entityId` ON `${TABLE_NAME}` (`id`, `entityId`)"
} }
], ],
"foreignKeys": [ "foreignKeys": [
@ -88,7 +97,7 @@
"onDelete": "RESTRICT", "onDelete": "RESTRICT",
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"columns": [ "columns": [
"xmppId" "entityId"
], ],
"referencedColumns": [ "referencedColumns": [
"id" "id"
@ -145,7 +154,7 @@
}, },
{ {
"tableName": "messages", "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 )", "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, `incoming` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -182,6 +191,12 @@
"columnName": "to", "columnName": "to",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": false "notNull": false
},
{
"fieldPath": "incoming",
"columnName": "incoming",
"affinity": "INTEGER",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@ -294,7 +309,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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, 'fb56e6a5615c4d1baa6c08d919560267')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '51a887b7e249b513a08a53f3065136cf')"
] ]
} }
} }

View File

@ -7,19 +7,21 @@ import androidx.room.Room;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
import org.mercury_im.messenger.persistence.room.dao.AccountDao; import org.mercury_im.messenger.persistence.room.dao.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao; import org.mercury_im.messenger.persistence.room.dao.MessageDao;
import org.mercury_im.messenger.persistence.room.dao.ContactDao; 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.RoomAccountModel;
import org.mercury_im.messenger.persistence.room.model.RoomContactModel; import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
import org.mercury_im.messenger.persistence.room.model.RoomMessageModel; import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
import org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel;
@Database(entities = { @Database(version = 1,
RoomContactModel.class, entities = {
RoomAccountModel.class, RoomContactModel.class,
RoomMessageModel.class, RoomAccountModel.class,
RoomXmppIdentityModel.class}, RoomMessageModel.class,
version = 1) RoomEntityModel.class
})
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
private static final String DB_NAME = "mercury_db"; private static final String DB_NAME = "mercury_db";
@ -38,4 +40,6 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract MessageDao messageDao(); public abstract MessageDao messageDao();
public abstract AccountDao accountDao(); public abstract AccountDao accountDao();
public abstract EntityDao entityDao();
} }

View File

@ -1,14 +1,18 @@
package org.mercury_im.messenger.persistence.room.dao; package org.mercury_im.messenger.persistence.room.dao;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.TypeConverters;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.XmppIdentityModel;
import org.mercury_im.messenger.persistence.room.model.RoomChatModel; import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
import java.util.List; import java.util.List;
@Dao
@TypeConverters(EntityBareJidConverter.class)
public interface ChatDao extends BaseDao<RoomChatModel> { public interface ChatDao extends BaseDao<RoomChatModel> {
@Query("SELECT * FROM chats") @Query("SELECT * FROM chats")

View File

@ -15,6 +15,18 @@ import java.util.List;
@TypeConverters(EntityBareJidConverter.class) @TypeConverters(EntityBareJidConverter.class)
public interface ContactDao extends BaseDao<RoomContactModel> { public interface ContactDao extends BaseDao<RoomContactModel> {
@Query("SELECT * FROM contacts WHERE id = :id")
LiveData<RoomContactModel> getContact(long id);
@Query("SELECT * FROM contacts WHERE id = :id")
RoomContactModel syncGetContact(long id);
@Query("SELECT * FROM contacts WHERE entityId = :entityId")
LiveData<RoomContactModel> getContactForEntityId(long entityId);
@Query("SELECT * FROM contacts WHERE entityId = :entityId")
RoomContactModel syncGetContactForEntityId(long entityId);
/** /**
* Return a {@link LiveData} wrapping a {@link List} of all {@link RoomContactModel RosterEntries} * Return a {@link LiveData} wrapping a {@link List} of all {@link RoomContactModel RosterEntries}
* which are currently found in the database. * which are currently found in the database.
@ -22,11 +34,11 @@ public interface ContactDao extends BaseDao<RoomContactModel> {
* @return * @return
*/ */
@Query("SELECT * FROM contacts") @Query("SELECT * FROM contacts")
LiveData<List<RoomContactModel>> getAllRosterEntries(); LiveData<List<RoomContactModel>> getAllContacts();
@Query("SELECT contacts.* FROM contacts JOIN entities WHERE jid = :jid") @Query("SELECT contacts.* FROM contacts JOIN entities WHERE contacts.accountId = :accountId AND jid = :jid")
RoomContactModel getRosterEntryByJid(EntityBareJid jid); RoomContactModel getContactByJid(long accountId, EntityBareJid jid);
@Query("SELECT * FROM contacts WHERE accountId = :accountId") @Query("SELECT * FROM contacts WHERE accountId = :accountId")
LiveData<List<RoomContactModel>> getRosterEntriesForAccount(long accountId); LiveData<List<RoomContactModel>> getContactsForAccount(long accountId);
} }

View File

@ -0,0 +1,28 @@
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.RoomEntityModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
@Dao
@TypeConverters(EntityBareJidConverter.class)
public interface EntityDao extends BaseDao<RoomEntityModel> {
@Query("SELECT * FROM entities WHERE id = :id")
LiveData<RoomEntityModel> getEntity(long id);
@Query("SELECT * FROM entities WHERE id = :id")
RoomEntityModel getEntitySync(long id);
@Query("SELECT * FROM entities WHERE accountId = :accountId AND jid = :jid")
LiveData<RoomEntityModel> getEntityFor(long accountId, EntityBareJid jid);
@Query("SELECT * FROM entities WHERE accountId = :accountId AND jid = :jid")
RoomEntityModel getEntityForSync(long accountId, EntityBareJid jid);
}

View File

@ -7,7 +7,7 @@ import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.mercury_im.messenger.persistence.model.ChatModel; import org.mercury_im.messenger.persistence.model.ChatModel;
import org.mercury_im.messenger.persistence.model.XmppIdentityModel; import org.mercury_im.messenger.persistence.model.EntityModel;
import static androidx.room.ForeignKey.CASCADE; import static androidx.room.ForeignKey.CASCADE;
import static org.mercury_im.messenger.persistence.room.model.RoomChatModel.KEY_ID; import static org.mercury_im.messenger.persistence.room.model.RoomChatModel.KEY_ID;
@ -17,7 +17,7 @@ import static org.mercury_im.messenger.persistence.room.model.RoomChatModel.TABL
@Entity(tableName = TABLE, @Entity(tableName = TABLE,
indices = {@Index(KEY_ID), @Index(KEY_XMPPID)}, indices = {@Index(KEY_ID), @Index(KEY_XMPPID)},
foreignKeys = { foreignKeys = {
@ForeignKey(entity = XmppIdentityModel.class, parentColumns = "id", childColumns = "xmppId", onDelete = CASCADE) @ForeignKey(entity = EntityModel.class, parentColumns = "id", childColumns = "xmppId", onDelete = CASCADE)
}) })
public class RoomChatModel implements ChatModel { public class RoomChatModel implements ChatModel {
@ -43,18 +43,20 @@ public class RoomChatModel implements ChatModel {
} }
@Override @Override
public void setId(long id) { public RoomChatModel setId(long id) {
this.id = id; this.id = id;
return this;
} }
@Override @Override
public long getXmppIdentityId() { public long getPeerIdentityId() {
return xmppIdentityId; return xmppIdentityId;
} }
@Override @Override
public void setXmppIdentityId(long id) { public RoomChatModel setPeerIdentityId(long id) {
this.xmppIdentityId = id; this.xmppIdentityId = id;
return this;
} }
@Override @Override
@ -63,7 +65,8 @@ public class RoomChatModel implements ChatModel {
} }
@Override @Override
public void setOpened(boolean opened) { public RoomChatModel setOpened(boolean opened) {
this.isOpened = opened; this.isOpened = opened;
return this;
} }
} }

View File

@ -13,30 +13,31 @@ import static androidx.room.ForeignKey.CASCADE;
import static androidx.room.ForeignKey.RESTRICT; import static androidx.room.ForeignKey.RESTRICT;
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ACCOUNT_ID; import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ACCOUNT_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ID; import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_XMPP_ID; import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ENTITY_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.TABLE; import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.TABLE;
@Entity(tableName = TABLE, @Entity(tableName = TABLE,
indices = { indices = {
@Index(value = KEY_ID), @Index(value = KEY_ID),
@Index(value = KEY_ACCOUNT_ID), @Index(value = KEY_ACCOUNT_ID),
@Index(value = KEY_XMPP_ID) @Index(value = KEY_ENTITY_ID),
@Index(value = {KEY_ID, KEY_ENTITY_ID}, unique = true)
}, },
foreignKeys = { foreignKeys = {
@ForeignKey(entity = RoomAccountModel.class, @ForeignKey(entity = RoomAccountModel.class,
parentColumns = RoomAccountModel.KEY_ID, parentColumns = RoomAccountModel.KEY_ID,
childColumns = KEY_ACCOUNT_ID, childColumns = KEY_ACCOUNT_ID,
onDelete = CASCADE), onDelete = CASCADE),
@ForeignKey(entity = RoomXmppIdentityModel.class, @ForeignKey(entity = RoomEntityModel.class,
parentColumns = RoomXmppIdentityModel.KEY_ID, parentColumns = RoomEntityModel.KEY_ID,
childColumns = KEY_XMPP_ID, childColumns = KEY_ENTITY_ID,
onDelete = RESTRICT)}) onDelete = RESTRICT)})
public class RoomContactModel implements ContactModel { public class RoomContactModel implements ContactModel {
public static final String TABLE = "contacts"; public static final String TABLE = "contacts";
public static final String KEY_ID = "id"; public static final String KEY_ID = "id";
public static final String KEY_ACCOUNT_ID = "accountId"; public static final String KEY_ACCOUNT_ID = "accountId";
public static final String KEY_XMPP_ID = "xmppId"; public static final String KEY_ENTITY_ID = "entityId";
public static final String KEY_ROSTER_NAME = "rosterName"; public static final String KEY_ROSTER_NAME = "rosterName";
public static final String KEY_NICKNAME = "nickname"; public static final String KEY_NICKNAME = "nickname";
@ -47,8 +48,8 @@ public class RoomContactModel implements ContactModel {
@ColumnInfo(name = KEY_ACCOUNT_ID) @ColumnInfo(name = KEY_ACCOUNT_ID)
private long accountId; private long accountId;
@ColumnInfo(name = KEY_XMPP_ID) @ColumnInfo(name = KEY_ENTITY_ID)
private long xmppIdentityId; private long entityId;
@ColumnInfo(name = KEY_ROSTER_NAME) @ColumnInfo(name = KEY_ROSTER_NAME)
private String rosterName; private String rosterName;
@ -97,12 +98,12 @@ public class RoomContactModel implements ContactModel {
} }
@Override @Override
public long getXmppIdentityId() { public long getEntityId() {
return xmppIdentityId; return entityId;
} }
@Override @Override
public void setXmppIdentityId(long id) { public void setEntityId(long id) {
this.xmppIdentityId = id; this.entityId = id;
} }
} }

View File

@ -9,17 +9,17 @@ import androidx.room.PrimaryKey;
import androidx.room.TypeConverters; import androidx.room.TypeConverters;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.XmppIdentityModel; import org.mercury_im.messenger.persistence.model.EntityModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter; import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
import org.mercury_im.messenger.persistence.room.type_converter.FileConverter; import org.mercury_im.messenger.persistence.room.type_converter.FileConverter;
import java.io.File; import java.io.File;
import static androidx.room.ForeignKey.CASCADE; import static androidx.room.ForeignKey.CASCADE;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.KEY_ACCOUNT_ID; import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_ACCOUNT_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.KEY_ID; import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.KEY_JID; import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_JID;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.TABLE; import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.TABLE;
@Entity(tableName = TABLE, @Entity(tableName = TABLE,
indices = { indices = {
@ -32,7 +32,7 @@ import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityMo
childColumns = KEY_ACCOUNT_ID, childColumns = KEY_ACCOUNT_ID,
onDelete = CASCADE) onDelete = CASCADE)
}) })
public class RoomXmppIdentityModel implements XmppIdentityModel { public class RoomEntityModel implements EntityModel {
public static final String TABLE = "entities"; public static final String TABLE = "entities";
public static final String KEY_ID = "id"; public static final String KEY_ID = "id";

View File

@ -38,6 +38,7 @@ public class RoomMessageModel implements MessageModel {
public static final String KEY_SEND_DATE = "sendDate"; public static final String KEY_SEND_DATE = "sendDate";
public static final String KEY_FROM = "from"; public static final String KEY_FROM = "from";
public static final String KEY_TO = "to"; public static final String KEY_TO = "to";
public static final String KEY_INCOMING = "incoming";
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = KEY_ID) @ColumnInfo(name = KEY_ID)
@ -61,6 +62,9 @@ public class RoomMessageModel implements MessageModel {
@ColumnInfo(name = KEY_TO) @ColumnInfo(name = KEY_TO)
private EntityBareJid to; private EntityBareJid to;
@ColumnInfo(name = KEY_INCOMING)
private boolean incoming;
public RoomMessageModel() { public RoomMessageModel() {
} }
@ -124,4 +128,14 @@ public class RoomMessageModel implements MessageModel {
public void setTo(EntityBareJid recipient) { public void setTo(EntityBareJid recipient) {
this.to = recipient; this.to = recipient;
} }
@Override
public boolean isIncoming() {
return incoming;
}
@Override
public void setIncoming(boolean isIncoming) {
this.incoming = isIncoming;
}
} }

View File

@ -1,13 +1,11 @@
package org.mercury_im.messenger.persistence.room.repository; package org.mercury_im.messenger.persistence.room.repository;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.room.Index;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.AccountModel; import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.model.ChatModel;
import org.mercury_im.messenger.persistence.model.ContactModel; import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.model.XmppIdentityModel; import org.mercury_im.messenger.persistence.model.EntityModel;
import org.mercury_im.messenger.persistence.repository.ChatRepository; import org.mercury_im.messenger.persistence.repository.ChatRepository;
import org.mercury_im.messenger.persistence.room.dao.ChatDao; import org.mercury_im.messenger.persistence.room.dao.ChatDao;
import org.mercury_im.messenger.persistence.room.model.RoomChatModel; import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
@ -36,7 +34,7 @@ public class IChatRepository implements ChatRepository<RoomChatModel> {
} }
@Override @Override
public LiveData<RoomChatModel> getChatWith(XmppIdentityModel identity) { public LiveData<RoomChatModel> getChatWith(EntityModel identity) {
return chatDao.getChatWithIdentity(identity.getId()); return chatDao.getChatWithIdentity(identity.getId());
} }

View File

@ -17,18 +17,23 @@ public class IContactRepository implements ContactRepository<RoomContactModel> {
} }
@Override @Override
public LiveData<List<RoomContactModel>> getAllRosterEntries() { public LiveData<List<RoomContactModel>> getAllContacts() {
return contactDao.getAllRosterEntries(); return contactDao.getAllContacts();
} }
@Override @Override
public void updateOrInsertRosterEntry(RoomContactModel rosterEntryModel) { public void updateOrInsertContact(RoomContactModel rosterEntryModel) {
contactDao.insert(rosterEntryModel); contactDao.insert(rosterEntryModel);
} }
@Override @Override
public LiveData<RoomContactModel> getRosterEntry(long id) { public LiveData<RoomContactModel> getContact(long id) {
return null; return contactDao.getContact(id);
}
@Override
public LiveData<RoomContactModel> getContactForEntity(long entityId) {
return contactDao.getContactForEntityId(entityId);
} }

View File

@ -0,0 +1,31 @@
package org.mercury_im.messenger.persistence.room.repository;
import androidx.lifecycle.LiveData;
import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.repository.EntityRepository;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
import javax.inject.Inject;
public class IEntityRepository implements EntityRepository<RoomEntityModel> {
private final EntityDao dao;
@Inject
public IEntityRepository(EntityDao dao) {
this.dao = dao;
}
@Override
public <C extends ContactModel> LiveData<RoomEntityModel> getEntity(C contact) {
return dao.getEntity(contact.getId());
}
@Override
public LiveData<RoomEntityModel> getEntity(long accountId, EntityBareJid jid) {
return dao.getEntityFor(accountId, jid);
}
}

View File

@ -8,11 +8,17 @@ public class FileConverter {
@TypeConverter @TypeConverter
public static String toString(File file) { public static String toString(File file) {
if (file == null) {
return null;
}
return file.getAbsolutePath(); return file.getAbsolutePath();
} }
@TypeConverter @TypeConverter
public static File toFile(String string) { public static File toFile(String string) {
if (string == null) {
return null;
}
return new File(string); return new File(string);
} }
} }

View File

@ -4,13 +4,13 @@ public interface ChatModel {
long getId(); long getId();
void setId(long id); ChatModel setId(long id);
long getXmppIdentityId(); long getPeerIdentityId();
void setXmppIdentityId(long id); ChatModel setPeerIdentityId(long id);
boolean isOpened(); boolean isOpened();
void setOpened(boolean opened); ChatModel setOpened(boolean opened);
} }

View File

@ -10,9 +10,9 @@ public interface ContactModel {
void setAccountId(long id); void setAccountId(long id);
long getXmppIdentityId(); long getEntityId();
void setXmppIdentityId(long id); void setEntityId(long id);
String getRosterName(); String getRosterName();

View File

@ -7,11 +7,11 @@ import org.jxmpp.jid.EntityBareJid;
import java.io.File; import java.io.File;
/** /**
* An {@link XmppIdentityModel} represents an XMPP user as seen by an account. * An {@link EntityModel} 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 * Its primary key should be composited of its {@link EntityBareJid} and the primary key of the
* {@link AccountModel} which is communicating with the user. * {@link AccountModel} which is communicating with the user.
*/ */
public interface XmppIdentityModel { public interface EntityModel {
long getId(); long getId();

View File

@ -29,4 +29,8 @@ public interface MessageModel {
EntityBareJid getTo(); EntityBareJid getTo();
void setTo(EntityBareJid recipient); void setTo(EntityBareJid recipient);
boolean isIncoming();
void setIncoming(boolean isIncoming);
} }

View File

@ -6,8 +6,7 @@ import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.AccountModel; import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.model.ChatModel; import org.mercury_im.messenger.persistence.model.ChatModel;
import org.mercury_im.messenger.persistence.model.ContactModel; import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.model.XmppIdentityModel; import org.mercury_im.messenger.persistence.model.EntityModel;
import org.w3c.dom.Entity;
import java.util.List; import java.util.List;
@ -19,7 +18,7 @@ public interface ChatRepository<E extends ChatModel> {
LiveData<E> getChatWith(AccountModel account, EntityBareJid jid); LiveData<E> getChatWith(AccountModel account, EntityBareJid jid);
LiveData<E> getChatWith(XmppIdentityModel identity); LiveData<E> getChatWith(EntityModel identity);
LiveData<E> getChatWith(ContactModel contact); LiveData<E> getChatWith(ContactModel contact);

View File

@ -8,9 +8,11 @@ import java.util.List;
public interface ContactRepository<E extends ContactModel> { public interface ContactRepository<E extends ContactModel> {
LiveData<List<E>> getAllRosterEntries(); LiveData<List<E>> getAllContacts();
void updateOrInsertRosterEntry(E rosterEntryModel); void updateOrInsertContact(E rosterEntryModel);
LiveData<E> getRosterEntry(long id); LiveData<E> getContact(long id);
LiveData<E> getContactForEntity(long entityId);
} }

View File

@ -0,0 +1,15 @@
package org.mercury_im.messenger.persistence.repository;
import androidx.lifecycle.LiveData;
import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.model.EntityModel;
public interface EntityRepository<E extends EntityModel> {
<C extends ContactModel> LiveData<E> getEntity(C contact);
LiveData<E> getEntity(long accountId, EntityBareJid jid);
}

View File

@ -1,12 +0,0 @@
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<E extends XmppIdentityModel> {
<C extends ContactModel> LiveData<E> getIdentityOf(C contact);
}

View File

@ -14,7 +14,7 @@ ext {
smackImVersion = smackVersion smackImVersion = smackVersion
smackOmemoVersion = smackVersion smackOmemoVersion = smackVersion
smackOmemoSignalVersion = smackVersion smackOmemoSignalVersion = smackVersion
smackOpenPGPVersion = smackVersion smackOpenpgpVersion = smackVersion
smackResolverMiniDnsVersion = smackVersion smackResolverMiniDnsVersion = smackVersion
smackTcpVersion = smackVersion smackTcpVersion = smackVersion
@ -54,12 +54,12 @@ ext {
// Other libraries // Other libraries
// Architecture Components // Architecture Components
lifecycleVersion = "2.0.0" lifecycleVersion = '2.2.0-alpha01'
roomVersion = "2.1.0-rc01" roomVersion = '2.1.0'
pagingVersion = "2.1.0" pagingVersion = "2.1.0"
// Dagger 2 // Dagger 2
daggerVersion = "2.17" daggerVersion = '2.23.1'
// Android Support Library // Android Support Library
supportLibVersion = "28.0.0" supportLibVersion = "28.0.0"

View File

@ -1,12 +0,0 @@
package org.mercury_im.messenger.xmpp_android;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
public class AndroidMercuryConnection extends MercuryConnection {
public AndroidMercuryConnection(XMPPConnection connection, long accountId) {
super(connection, accountId);
}
}

View File

@ -12,7 +12,7 @@ dependencies {
api "org.igniterealtime.smack:smack-omemo-signal:$smackOmemoSignalVersion" api "org.igniterealtime.smack:smack-omemo-signal:$smackOmemoSignalVersion"
api "org.igniterealtime.smack:smack-openpgp:$smackOpenpgpVersion" api "org.igniterealtime.smack:smack-openpgp:$smackOpenpgpVersion"
// api "org.igniterealtime.smack:smack-resolver-minidns:$smackResolverMiniDnsVersion" // api "org.igniterealtime.smack:smack-resolver-minidns:$smackResolverMiniDnsVersion"
// api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion" api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion"
// Exclude XmlPullParser from Smack dependencies, as its now provided by Android // Exclude XmlPullParser from Smack dependencies, as its now provided by Android
// https://stackoverflow.com/questions/48488563/gradle-xpp3-error/48746294#48746294 // https://stackoverflow.com/questions/48488563/gradle-xpp3-error/48746294#48746294

View File

@ -0,0 +1,8 @@
package org.mercury_im.messenger.xmpp_core;
public interface ConnectionHolder {
MercuryConnection getConnection(long accountId);
void putConnection(long accountId, MercuryConnection connection);
}