This commit is contained in:
Paul Schaub 2019-06-21 04:44:59 +02:00
parent 8ceeef70b0
commit da2d5a42cf
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
20 changed files with 201 additions and 31 deletions

View file

@ -5,7 +5,6 @@ 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

@ -3,6 +3,7 @@ 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.ChatDao;
import org.mercury_im.messenger.persistence.room.dao.ContactAndEntityDao;
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
import org.mercury_im.messenger.persistence.room.dao.MessageDao;
@ -46,6 +47,12 @@ public class RoomModule {
return mAppDatabase.rosterEntryDao();
}
@Singleton
@Provides
ChatDao provideChatDao() {
return mAppDatabase.chatDao();
}
@Singleton
@Provides
MessageDao provideMessageDao() {

View file

@ -12,6 +12,8 @@ import org.mercury_im.messenger.persistence.repository.MessageRepository;
import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
import org.mercury_im.messenger.xmpp_core.PlainMessageHandler;
import java.util.Date;
import javax.inject.Inject;
import static org.mercury_im.messenger.MercuryImApplication.TAG;
@ -37,6 +39,7 @@ public class RoomPlainMessageHandler implements PlainMessageHandler {
messageModel.setTo(message.getTo().asEntityBareJidIfPossible());
messageModel.setIncoming(true);
messageModel.setBody(message.getBody());
messageModel.setSendDate(new Date());
long messageId = messageRepository.insertMessage(messageModel);
Log.d(TAG, "Inserted incoming message " + messageId);
}
@ -49,6 +52,7 @@ public class RoomPlainMessageHandler implements PlainMessageHandler {
messageModel.setTo(chat.getXmppAddressOfChatPartner());
messageModel.setIncoming(false);
messageModel.setBody(message.getBody());
messageModel.setSendDate(new Date());
long messageId = messageRepository.insertMessage(messageModel);
Log.d(TAG, "Inserted outgoing message " + messageId);
}
@ -63,6 +67,7 @@ public class RoomPlainMessageHandler implements PlainMessageHandler {
messageModel.setIncoming(direction == CarbonExtension.Direction.received);
messageModel.setBody(carbonCopy.getBody());
messageModel.setSendDate(new Date());
long messageId = messageRepository.insertMessage(messageModel);
Log.d(TAG, "Inserted carbon message " + messageId);
}

View file

@ -34,9 +34,12 @@ 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.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.Notifications;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.handler.RoomPlainMessageHandler;
import org.mercury_im.messenger.handler.RoomRosterHandler;
import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.model.ContactModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
@ -129,6 +132,18 @@ public class XmppConnectionService extends Service implements ConnectionHolder {
Log.d(TAG, "onCreate()");
MercuryImApplication.getApplication().getAppComponent().inject(this);
new Thread() {
@Override
public void run() {
List<AccountModel> accounts = accountRepository.getAllAccounts();
for(AccountModel accountModel : accounts) {
startConnection(accountModel);
}
}
}.start();
// Begin life cycle of Ping Manager.
// The Manager will automatically detect newly created connections and ping the server
// every half hour if necessary.
@ -198,6 +213,41 @@ public class XmppConnectionService extends Service implements ConnectionHolder {
.build();
}
public void startConnection(AccountModel accountModel) {
MercuryConnection connection = connections.get(accountModel.getId());
if (connection != null) {
return;
}
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());
putConnection(accountModel.getId(), mercuryConnection);
mercuryConnection.setRosterHandler(new RoomRosterHandler(accountModel.getId(), Roster.getInstanceFor(connection)));
mercuryConnection.setPlainMessageHandler(new RoomPlainMessageHandler(accountModel.getId()));
connection.connect().login();
Log.d(MercuryImApplication.TAG, "Logged in for " + accountModel.getJid().toString());
} 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();
}
public class Binder extends android.os.Binder {
private final XmppConnectionService service;

View file

@ -84,10 +84,6 @@ public class MainActivity extends BindingActivity {
startActivity(new Intent(getApplicationContext(), LoginActivity.class));
return true;
case R.id.action_chat:
startActivity(new Intent(getApplicationContext(), ChatActivity.class));
return true;
case R.id.action_accounts:
startActivity(new Intent(getApplicationContext(), AccountsActivity.class));
return true;

View file

@ -18,8 +18,10 @@ import org.jxmpp.jid.impl.JidCreate;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.model.ChatModel;
import org.mercury_im.messenger.persistence.model.MessageModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.repository.ChatRepository;
import org.mercury_im.messenger.persistence.repository.MessageRepository;
import org.mercury_im.messenger.ui.BindingActivity;
@ -53,7 +55,9 @@ public class ChatActivity extends BindingActivity implements ChatInputFragment.O
if (savedInstanceState == null) {
savedInstanceState = getIntent().getExtras();
if (savedInstanceState == null) return;
}
String jidString = savedInstanceState.getString("JID");
if (jidString != null) {
jid = JidCreate.entityBareFromOrThrowUnchecked(jidString);
@ -69,12 +73,13 @@ public class ChatActivity extends BindingActivity implements ChatInputFragment.O
}
});
LiveData<List<MessageModel>> messages = messageRepository.getAllMessagesFrom(accountId, jid);
LiveData<List<MessageModel>> messages = messageRepository.getAllMessagesOfChat(accountId, jid);
messages.observe(this, new Observer<List<MessageModel>>() {
@Override
public void onChanged(List<MessageModel> messageModels) {
Log.d(MercuryImApplication.TAG, "Updating messages: " + messageModels.size());
adapter.updateMessages(messageModels);
recyclerView.scrollToPosition(messageModels.size() - 1);
}
});

View file

@ -31,7 +31,7 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerVi
@NonNull
@Override
public ChatItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ChatItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_message_text_in, parent, false));
return new ChatItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false));
}
@Override
@ -40,6 +40,15 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerVi
holder.body.setText(message.getBody());
}
@Override
public int getItemViewType(int position) {
if (messages.get(position).isIncoming()) {
return R.layout.view_message_text_in;
} else {
return R.layout.view_message_text_out;
}
}
@Override
public int getItemCount() {
return messages.size();

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -8,9 +8,11 @@ android:layout_height="match_parent"
tools:context=".ui.chat.ChatActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
android:theme="@style/AppTheme.AppBarOverlay"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
@ -21,16 +23,24 @@ tools:context=".ui.chat.ChatActivity">
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_chat" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar_layout">
<include layout="@layout/content_chat" />
</FrameLayout>
<FrameLayout
android:id="@+id/fab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
app:layout_constraintBottom_toBottomOf="parent">
<include layout="@layout/view_chat_field"/>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -11,6 +11,8 @@
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="100dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -24,7 +24,7 @@
android:minHeight="48dp"
app:layout_constraintStart_toEndOf="@+id/msg_avatar"
app:layout_constraintTop_toTopOf="parent"
card_view:cardCornerRadius="24dp">
card_view:cardCornerRadius="12dp">
<TextView
android:id="@+id/msg_body"

View file

@ -4,7 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "51a887b7e249b513a08a53f3065136cf",
"identityHash": "63a5e8d6de4b6d8eb30e820be9ab9a69",
"entities": [
{
"tableName": "contacts",
@ -152,6 +152,67 @@
],
"foreignKeys": []
},
{
"tableName": "chats",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `xmppId` INTEGER NOT NULL, `open` INTEGER NOT NULL, FOREIGN KEY(`xmppId`) REFERENCES `entities`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "peerIdentityId",
"columnName": "xmppId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isOpened",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_chats_id",
"unique": false,
"columnNames": [
"id"
],
"createSql": "CREATE INDEX `index_chats_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_chats_xmppId",
"unique": false,
"columnNames": [
"xmppId"
],
"createSql": "CREATE INDEX `index_chats_xmppId` ON `${TABLE_NAME}` (`xmppId`)"
}
],
"foreignKeys": [
{
"table": "entities",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"xmppId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"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, `incoming` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
@ -309,7 +370,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, '51a887b7e249b513a08a53f3065136cf')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '63a5e8d6de4b6d8eb30e820be9ab9a69')"
]
}
}

View file

@ -7,11 +7,13 @@ 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.ChatDao;
import org.mercury_im.messenger.persistence.room.dao.ContactAndEntityDao;
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.RoomChatModel;
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;
@ -20,6 +22,7 @@ import org.mercury_im.messenger.persistence.room.model.RoomMessageModel;
entities = {
RoomContactModel.class,
RoomAccountModel.class,
RoomChatModel.class,
RoomMessageModel.class,
RoomEntityModel.class
})
@ -38,6 +41,8 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract ContactDao rosterEntryDao();
public abstract ChatDao chatDao();
public abstract MessageDao messageDao();
public abstract AccountDao accountDao();

View file

@ -2,6 +2,7 @@ package org.mercury_im.messenger.persistence.room.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.TypeConverters;
@ -11,10 +12,16 @@ import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidCon
import java.util.List;
import static androidx.room.OnConflictStrategy.REPLACE;
@Dao
@TypeConverters(EntityBareJidConverter.class)
public interface ContactDao extends BaseDao<RoomContactModel> {
@Override
@Insert(onConflict = REPLACE)
long insert(RoomContactModel entity);
@Query("SELECT * FROM contacts WHERE id = :id")
LiveData<RoomContactModel> getContact(long id);

View file

@ -2,6 +2,7 @@ package org.mercury_im.messenger.persistence.room.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.TypeConverters;
@ -9,10 +10,16 @@ import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
import org.mercury_im.messenger.persistence.room.type_converter.EntityBareJidConverter;
import static androidx.room.OnConflictStrategy.REPLACE;
@Dao
@TypeConverters(EntityBareJidConverter.class)
public interface EntityDao extends BaseDao<RoomEntityModel> {
@Override
@Insert(onConflict = REPLACE)
long insert(RoomEntityModel entity);
@Query("SELECT * FROM entities WHERE id = :id")
LiveData<RoomEntityModel> getEntity(long id);

View file

@ -17,10 +17,13 @@ import java.util.List;
@TypeConverters(EntityBareJidConverter.class)
public interface MessageDao extends BaseDao<RoomMessageModel> {
@Query("SELECT * FROM messages WHERE accountId=:accountId ORDER BY sendDate DESC")
@Query("SELECT * FROM messages WHERE accountId=:accountId ORDER BY sendDate ASC")
LiveData<List<RoomMessageModel>> getAllMessagesOf(long accountId);
@Query("SELECT * FROM messages WHERE accountId=:accountId AND `from`=:sender ORDER BY sendDate DESC")
@Query("SELECT * FROM messages WHERE accountId=:accountId AND `from`=:sender ORDER BY sendDate ASC")
LiveData<List<RoomMessageModel>> getAllMessagesFrom(long accountId, EntityBareJid sender);
@Query("SELECT * FROM messages WHERE accountId = :accountId AND (`from` = :peer OR `to` = :peer) ORDER BY sendDate ASC")
LiveData<List<RoomMessageModel>> getAllMessagesInConversation(long accountId, EntityBareJid peer);
}

View file

@ -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 = EntityModel.class, parentColumns = "id", childColumns = "xmppId", onDelete = CASCADE)
@ForeignKey(entity = RoomEntityModel.class, parentColumns = "id", childColumns = "xmppId", onDelete = CASCADE)
})
public class RoomChatModel implements ChatModel {
@ -32,7 +32,7 @@ public class RoomChatModel implements ChatModel {
private long id;
@ColumnInfo(name = KEY_XMPPID)
private long xmppIdentityId;
private long peerIdentityId;
@ColumnInfo(name = KEY_OPEN)
private boolean isOpened;
@ -43,20 +43,18 @@ public class RoomChatModel implements ChatModel {
}
@Override
public RoomChatModel setId(long id) {
public void setId(long id) {
this.id = id;
return this;
}
@Override
public long getPeerIdentityId() {
return xmppIdentityId;
return peerIdentityId;
}
@Override
public RoomChatModel setPeerIdentityId(long id) {
this.xmppIdentityId = id;
return this;
public void setPeerIdentityId(long id) {
this.peerIdentityId = id;
}
@Override
@ -65,8 +63,7 @@ public class RoomChatModel implements ChatModel {
}
@Override
public RoomChatModel setOpened(boolean opened) {
public void setIsOpened(boolean opened) {
this.isOpened = opened;
return this;
}
}

View file

@ -33,6 +33,11 @@ public class IMessageRepository implements MessageRepository<RoomMessageModel> {
return messageDao.getAllMessagesFrom(accountId, contact);
}
@Override
public LiveData<List<RoomMessageModel>> getAllMessagesOfChat(long accountId, EntityBareJid peer) {
return messageDao.getAllMessagesInConversation(accountId, peer);
}
@Override
public LiveData<List<RoomMessageModel>> findMessageByQuery(String query) {
return null;

View file

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

View file

@ -14,6 +14,8 @@ public interface MessageRepository<E extends MessageModel> {
LiveData<List<E>> getAllMessagesOf(long accountId);
LiveData<List<E>> getAllMessagesOfChat(long accountId, EntityBareJid peer);
LiveData<List<E>> getAllMessagesFrom(long accountId, EntityBareJid contact);
LiveData<List<E>> findMessageByQuery(String query);