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"
// 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 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.0.1'
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'
testImplementation 'junit:junit:4.12'

View File

@ -5,6 +5,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
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.service.XmppConnectionService;
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.ChatInputFragment;
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.LoginViewModel;
import org.mercury_im.messenger.ui.roster.RosterViewModel;
import org.mercury_im.messenger.xmpp_database_connector.RosterConnector;
import javax.inject.Singleton;
@ -63,5 +63,5 @@ public interface AppComponent {
// Connectors
void inject(RosterConnector connector);
void inject(RoomRosterHandler roomRosterHandler);
}

View File

@ -1,11 +1,20 @@
package org.mercury_im.messenger.di.module;
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.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.ChatDao;
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.IChatRepository;
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;
@ -23,7 +32,26 @@ public class RepositoryModule {
@Singleton
@Provides
ContactRepository provideRosterEntryRepository(ContactDao dao) {
EntityRepository provideEntityRepository(EntityDao dao) {
return new IEntityRepository(dao);
}
@Singleton
@Provides
ContactRepository provideContactRepository(ContactDao 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.persistence.room.AppDatabase;
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.ContactDao;
import org.w3c.dom.Entity;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -48,4 +50,10 @@ public class RoomModule {
MessageDao provideMessageDao() {
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.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@ -16,9 +13,10 @@ import android.util.LongSparseArray;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import org.jivesoftware.smack.SmackException;
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.IncomingChatMessageListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
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.smackx.ping.android.ServerPingWithAlarmManager;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
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.model.ContactModel;
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.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.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.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.util.Collection;
import java.util.List;
import java.util.Set;
@ -56,11 +67,11 @@ import javax.inject.Inject;
* Started, Bound Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
* 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 APP = "org.olomono.mercury";
private static final String APP = "org.mercury-im.messenger";
private static final String SERVICE = APP + ".XmppConnectionService";
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_FAILURE = STATUS + ".FAILURE";
@Inject
ContactRepository rosterRepository;
ContactRepository contactRepository;
@Inject
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;
@ -125,23 +144,18 @@ public class XmppConnectionService extends Service {
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
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 + ")");
if (intent == null) {
startAndDisplayForegroundNotification();
@ -152,30 +166,10 @@ public class XmppConnectionService extends Service {
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();
stopSelf();
break;
default:
break;
@ -204,100 +198,6 @@ public class XmppConnectionService extends Service {
.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 {
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;
import androidx.lifecycle.Observer;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.IBinder;
import android.view.Menu;
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.repository.AccountRepository;
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.login.AccountsActivity;
import org.mercury_im.messenger.ui.login.LoginActivity;
@ -27,7 +34,7 @@ import java.util.List;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
public class MainActivity extends BindingActivity {
@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.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
@ -16,14 +18,24 @@ import android.widget.TextView;
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.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
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.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;
@ -33,7 +45,7 @@ import butterknife.ButterKnife;
/**
* 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
AccountRepository accountRepository;
@ -45,6 +57,9 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
@BindView(R.id.password)
TextInputEditText mPasswordView;
@BindView(R.id.sign_in_button)
Button mSignInView;
private LoginViewModel viewModel;
@Override
@ -63,13 +78,25 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
mJidView.setOnEditorActionListener(this);
mPasswordView.setOnEditorActionListener(this);
Button mEmailSignInButton = findViewById(R.id.sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
mSignInView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
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) {
@ -115,16 +142,39 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
accountModel.setJid(jid);
accountModel.setPassword(password);
long id = accountRepository.insertAccount(accountModel);
accountModel.setId(id);
Log.d(MercuryImApplication.TAG, "LoginActivity.loginDetailsEntered: Account " + id + " inserted.");
attemptLogin(jid, password, id);
attemptLogin(accountModel);
}
}
private void attemptLogin(EntityBareJid jid, String password, long accountId) {
Intent connectIntent = new Intent(getApplicationContext(), XmppConnectionService.class);
connectIntent.setAction(XmppConnectionService.ACTION_CONNECT);
connectIntent.putExtra(XmppConnectionService.EXTRA_ACCOUNT_ID, accountId);
startService(connectIntent);
private void attemptLogin(AccountModel accountModel) {
new Thread() {
@Override
public void run() {
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;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
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.persistence.room.model.RoomContactModel;
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.
@ -22,8 +29,10 @@ import java.util.List;
public class RosterFragment extends Fragment {
private RosterViewModel rosterViewModel;
private RosterRecyclerViewAdapter recyclerViewAdapter;
private RecyclerView recyclerView;
RecyclerView recyclerView;
public RosterFragment() {
@ -38,21 +47,24 @@ public class RosterFragment extends Fragment {
recyclerViewAdapter = new RosterRecyclerViewAdapter(new ArrayList<>());
recyclerView.setAdapter(recyclerViewAdapter);
rosterViewModel = ViewModelProviders.of(getActivity()).get(RosterViewModel.class);
observeViewModel(rosterViewModel);
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) {
viewModel.getRosterEntryList().observe(this, new Observer<List<RoomContactModel>>() {
@Override
public void onChanged(@Nullable List<RoomContactModel> rosterEntries) {
if (rosterEntries == null) {
return;
}
recyclerViewAdapter.setItems(rosterEntries);
viewModel.getRosterEntryList().observe(this, rosterEntries -> {
if (rosterEntries == null) {
Log.d(TAG, "Displaying null roster entries");
return;
}
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) {
this.contact = contactRepository.getRosterEntry(id);
this.contact = contactRepository.getContact(id);
return this;
}
}

View File

@ -16,7 +16,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
public class RosterRecyclerViewAdapter
extends RecyclerView.Adapter<RosterRecyclerViewAdapter.AbstractRosterItem> {
extends RecyclerView.Adapter<RosterRecyclerViewAdapter.RosterItemViewHolder> {
private List<RoomContactModel> entryModelList;
@ -26,28 +26,15 @@ public class RosterRecyclerViewAdapter
@NonNull
@Override
public AbstractRosterItem onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// switch (viewType) {
// case RosterItemViewHolder.TYPE:
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));
// }
public RosterItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new RosterItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item_roster_entry, parent, false));
}
@Override
public void onBindViewHolder(@NonNull AbstractRosterItem holder, int position) {
public void onBindViewHolder(@NonNull RosterItemViewHolder holder, int position) {
RoomContactModel model = entryModelList.get(position);
// switch (holder.getItemViewType()) {
// case RosterItemViewHolder.TYPE:
RosterItemViewHolder rosterItem = (RosterItemViewHolder) holder;
//rosterItem.jidView.setText(model.getJid().toString());
rosterItem.nicknameView.setText(model.getNickname());
rosterItem.itemView.setTag(model);
// break;
// }
holder.bind(model);
}
@Override
@ -60,31 +47,26 @@ public class RosterRecyclerViewAdapter
notifyDataSetChanged();
}
public abstract class AbstractRosterItem extends RecyclerView.ViewHolder {
public class RosterItemViewHolder extends RecyclerView.ViewHolder {
public AbstractRosterItem(@NonNull View itemView) {
super(itemView);
}
private View view;
abstract void bind(RosterViewModel viewModel);
}
public class RosterItemViewHolder extends AbstractRosterItem {
@BindView(R.id.roster_entry__jid)
TextView jidView;
@BindView(R.id.roster_entry__nickname)
TextView nicknameView;
public RosterItemViewHolder(View 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(RosterViewModel viewModel) {
// viewModel.getRosterEntryList().observe();
void bind(RoomContactModel contactModel) {
// jidView.setText("" + contactModel.getEntityId());
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) {
super(application);
MercuryImApplication.getApplication().getAppComponent().inject(this);
this.rosterEntryList = contactRepository.getAllRosterEntries();
this.rosterEntryList = contactRepository.getAllContacts();
}
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"
app:layoutManager="LinearLayoutManager"
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"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
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
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -29,7 +24,6 @@
android:id="@+id/roster_entry__nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.rosterEntryList."
tools:text="@tools:sample/full_names"
android:textSize="20sp"
android:textColor="@android:color/black"

View File

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

View File

@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "fb56e6a5615c4d1baa6c08d919560267",
"identityHash": "51a887b7e249b513a08a53f3065136cf",
"entities": [
{
"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": [
{
"fieldPath": "id",
@ -21,8 +21,8 @@
"notNull": true
},
{
"fieldPath": "xmppIdentityId",
"columnName": "xmppId",
"fieldPath": "entityId",
"columnName": "entityId",
"affinity": "INTEGER",
"notNull": true
},
@ -63,12 +63,21 @@
"createSql": "CREATE INDEX `index_contacts_accountId` ON `${TABLE_NAME}` (`accountId`)"
},
{
"name": "index_contacts_xmppId",
"name": "index_contacts_entityId",
"unique": false,
"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": [
@ -88,7 +97,7 @@
"onDelete": "RESTRICT",
"onUpdate": "NO ACTION",
"columns": [
"xmppId"
"entityId"
],
"referencedColumns": [
"id"
@ -145,7 +154,7 @@
},
{
"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": [
{
"fieldPath": "id",
@ -182,6 +191,12 @@
"columnName": "to",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "incoming",
"columnName": "incoming",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
@ -294,7 +309,7 @@
"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, '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 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.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.RoomEntityModel;
import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
import org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel;
@Database(entities = {
RoomContactModel.class,
RoomAccountModel.class,
RoomMessageModel.class,
RoomXmppIdentityModel.class},
version = 1)
@Database(version = 1,
entities = {
RoomContactModel.class,
RoomAccountModel.class,
RoomMessageModel.class,
RoomEntityModel.class
})
public abstract class AppDatabase extends RoomDatabase {
private static final String DB_NAME = "mercury_db";
@ -38,4 +40,6 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract MessageDao messageDao();
public abstract AccountDao accountDao();
public abstract EntityDao entityDao();
}

View File

@ -1,14 +1,18 @@
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.model.XmppIdentityModel;
import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
import java.util.List;
@Dao
@TypeConverters(EntityBareJidConverter.class)
public interface ChatDao extends BaseDao<RoomChatModel> {
@Query("SELECT * FROM chats")

View File

@ -15,6 +15,18 @@ import java.util.List;
@TypeConverters(EntityBareJidConverter.class)
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}
* which are currently found in the database.
@ -22,11 +34,11 @@ public interface ContactDao extends BaseDao<RoomContactModel> {
* @return
*/
@Query("SELECT * FROM contacts")
LiveData<List<RoomContactModel>> getAllRosterEntries();
LiveData<List<RoomContactModel>> getAllContacts();
@Query("SELECT contacts.* FROM contacts JOIN entities WHERE jid = :jid")
RoomContactModel getRosterEntryByJid(EntityBareJid jid);
@Query("SELECT contacts.* FROM contacts JOIN entities WHERE contacts.accountId = :accountId AND jid = :jid")
RoomContactModel getContactByJid(long accountId, EntityBareJid jid);
@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 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 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,
indices = {@Index(KEY_ID), @Index(KEY_XMPPID)},
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 {
@ -43,18 +43,20 @@ public class RoomChatModel implements ChatModel {
}
@Override
public void setId(long id) {
public RoomChatModel setId(long id) {
this.id = id;
return this;
}
@Override
public long getXmppIdentityId() {
public long getPeerIdentityId() {
return xmppIdentityId;
}
@Override
public void setXmppIdentityId(long id) {
public RoomChatModel setPeerIdentityId(long id) {
this.xmppIdentityId = id;
return this;
}
@Override
@ -63,7 +65,8 @@ public class RoomChatModel implements ChatModel {
}
@Override
public void setOpened(boolean opened) {
public RoomChatModel setOpened(boolean 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 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_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;
@Entity(tableName = TABLE,
indices = {
@Index(value = KEY_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 = {
@ForeignKey(entity = RoomAccountModel.class,
parentColumns = RoomAccountModel.KEY_ID,
childColumns = KEY_ACCOUNT_ID,
onDelete = CASCADE),
@ForeignKey(entity = RoomXmppIdentityModel.class,
parentColumns = RoomXmppIdentityModel.KEY_ID,
childColumns = KEY_XMPP_ID,
@ForeignKey(entity = RoomEntityModel.class,
parentColumns = RoomEntityModel.KEY_ID,
childColumns = KEY_ENTITY_ID,
onDelete = RESTRICT)})
public class RoomContactModel implements ContactModel {
public static final String TABLE = "contacts";
public static final String KEY_ID = "id";
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_NICKNAME = "nickname";
@ -47,8 +48,8 @@ public class RoomContactModel implements ContactModel {
@ColumnInfo(name = KEY_ACCOUNT_ID)
private long accountId;
@ColumnInfo(name = KEY_XMPP_ID)
private long xmppIdentityId;
@ColumnInfo(name = KEY_ENTITY_ID)
private long entityId;
@ColumnInfo(name = KEY_ROSTER_NAME)
private String rosterName;
@ -97,12 +98,12 @@ public class RoomContactModel implements ContactModel {
}
@Override
public long getXmppIdentityId() {
return xmppIdentityId;
public long getEntityId() {
return entityId;
}
@Override
public void setXmppIdentityId(long id) {
this.xmppIdentityId = id;
public void setEntityId(long id) {
this.entityId = id;
}
}

View File

@ -9,17 +9,17 @@ import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
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.FileConverter;
import java.io.File;
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.RoomXmppIdentityModel.KEY_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.KEY_JID;
import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityModel.TABLE;
import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_ACCOUNT_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_ID;
import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.KEY_JID;
import static org.mercury_im.messenger.persistence.room.model.RoomEntityModel.TABLE;
@Entity(tableName = TABLE,
indices = {
@ -32,7 +32,7 @@ import static org.mercury_im.messenger.persistence.room.model.RoomXmppIdentityMo
childColumns = KEY_ACCOUNT_ID,
onDelete = CASCADE)
})
public class RoomXmppIdentityModel implements XmppIdentityModel {
public class RoomEntityModel implements EntityModel {
public static final String TABLE = "entities";
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_FROM = "from";
public static final String KEY_TO = "to";
public static final String KEY_INCOMING = "incoming";
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = KEY_ID)
@ -61,6 +62,9 @@ public class RoomMessageModel implements MessageModel {
@ColumnInfo(name = KEY_TO)
private EntityBareJid to;
@ColumnInfo(name = KEY_INCOMING)
private boolean incoming;
public RoomMessageModel() {
}
@ -124,4 +128,14 @@ public class RoomMessageModel implements MessageModel {
public void setTo(EntityBareJid 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;
import androidx.lifecycle.LiveData;
import androidx.room.Index;
import org.jxmpp.jid.EntityBareJid;
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.XmppIdentityModel;
import org.mercury_im.messenger.persistence.model.EntityModel;
import org.mercury_im.messenger.persistence.repository.ChatRepository;
import org.mercury_im.messenger.persistence.room.dao.ChatDao;
import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
@ -36,7 +34,7 @@ public class IChatRepository implements ChatRepository<RoomChatModel> {
}
@Override
public LiveData<RoomChatModel> getChatWith(XmppIdentityModel identity) {
public LiveData<RoomChatModel> getChatWith(EntityModel identity) {
return chatDao.getChatWithIdentity(identity.getId());
}

View File

@ -17,18 +17,23 @@ public class IContactRepository implements ContactRepository<RoomContactModel> {
}
@Override
public LiveData<List<RoomContactModel>> getAllRosterEntries() {
return contactDao.getAllRosterEntries();
public LiveData<List<RoomContactModel>> getAllContacts() {
return contactDao.getAllContacts();
}
@Override
public void updateOrInsertRosterEntry(RoomContactModel rosterEntryModel) {
public void updateOrInsertContact(RoomContactModel rosterEntryModel) {
contactDao.insert(rosterEntryModel);
}
@Override
public LiveData<RoomContactModel> getRosterEntry(long id) {
return null;
public LiveData<RoomContactModel> getContact(long id) {
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
public static String toString(File file) {
if (file == null) {
return null;
}
return file.getAbsolutePath();
}
@TypeConverter
public static File toFile(String string) {
if (string == null) {
return null;
}
return new File(string);
}
}

View File

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

View File

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

View File

@ -7,11 +7,11 @@ import org.jxmpp.jid.EntityBareJid;
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
* {@link AccountModel} which is communicating with the user.
*/
public interface XmppIdentityModel {
public interface EntityModel {
long getId();

View File

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

View File

@ -8,9 +8,11 @@ import java.util.List;
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
smackOmemoVersion = smackVersion
smackOmemoSignalVersion = smackVersion
smackOpenPGPVersion = smackVersion
smackOpenpgpVersion = smackVersion
smackResolverMiniDnsVersion = smackVersion
smackTcpVersion = smackVersion
@ -54,12 +54,12 @@ ext {
// Other libraries
// Architecture Components
lifecycleVersion = "2.0.0"
roomVersion = "2.1.0-rc01"
lifecycleVersion = '2.2.0-alpha01'
roomVersion = '2.1.0'
pagingVersion = "2.1.0"
// Dagger 2
daggerVersion = "2.17"
daggerVersion = '2.23.1'
// Android Support Library
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-openpgp:$smackOpenpgpVersion"
// 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
// 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);
}