This commit is contained in:
Paul Schaub 2019-07-03 00:56:55 +02:00
parent 184c0777c1
commit 0b27a66dcd
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
27 changed files with 542 additions and 170 deletions

View File

@ -72,7 +72,7 @@ check.configure {
// Dependency versions are located in version.gradle
dependencies {
implementation project(":xmpp_android")
implementation(project(':xmpp_core'))
implementation(project(':persistence-room')) {
transitive = true
}
@ -104,4 +104,6 @@ dependencies {
// circular image viewer for avatars
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation "org.igniterealtime.smack:smack-android-extensions:$smackAndroidExtensionsVersion"
}

View File

@ -0,0 +1,41 @@
package org.mercury_im.messenger;
import androidx.lifecycle.MutableLiveData;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.mercury_im.messenger.xmpp_core.ConnectionState;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
public class AndroidMercuryConnection extends MercuryConnection {
private MutableLiveData<ConnectionState> connectionState = new MutableLiveData<>();
public AndroidMercuryConnection(XMPPTCPConnection connection, long accountId) {
super(connection, accountId);
}
@Override
public void connected(XMPPConnection connection) {
super.connected(connection);
connectionState.postValue(ConnectionState.CONNECTED);
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {
super.authenticated(connection, resumed);
connectionState.postValue(ConnectionState.CONNECTED);
}
@Override
public void connectionClosed() {
super.connectionClosed();
connectionState.postValue(ConnectionState.DISCONNECTED);
}
@Override
public void connectionClosedOnError(Exception e) {
super.connectionClosedOnError(e);
connectionState.postValue(ConnectionState.WAIRING_FOR_RETRY);
}
}

View File

@ -1,4 +1,4 @@
package org.mercury_im.messenger.xmpp_android;
package org.mercury_im.messenger;
import android.os.Parcel;
import android.os.Parcelable;

View File

@ -12,6 +12,8 @@ import android.util.LongSparseArray;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
@ -20,6 +22,7 @@ import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.AndroidMercuryConnection;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.Notifications;
import org.mercury_im.messenger.R;
@ -32,8 +35,8 @@ 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.ui.MainActivity;
import org.mercury_im.messenger.xmpp_android.AndroidMercuryConnection;
import org.mercury_im.messenger.xmpp_core.ConnectionHolder;
import org.mercury_im.messenger.xmpp_core.ConnectionState;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
import java.io.IOException;
@ -45,7 +48,7 @@ import javax.inject.Inject;
* Started, Bound Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
* affiliated with registered accounts.
*/
public class XmppConnectionService extends Service implements ConnectionHolder {
public class XmppConnectionService extends Service implements ConnectionHolder, MercuryConnection.ConnectionStateListener {
private static final String TAG = MercuryImApplication.TAG;
@ -142,6 +145,7 @@ public class XmppConnectionService extends Service implements ConnectionHolder {
@Override
public void putConnection(long accountId, MercuryConnection connection) {
connections.put(accountId, connection);
connection.setConnectionStateListener(this);
}
@Override
@ -230,6 +234,11 @@ public class XmppConnectionService extends Service implements ConnectionHolder {
}.start();
}
@Override
public void stateChanged(MercuryConnection connection, ConnectionState state) {
accountRepository.updateState(connection.getAccountId(), state.toString());
}
public class Binder extends android.os.Binder {
private final XmppConnectionService service;

View File

@ -12,10 +12,12 @@ 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.repository.AccountRepository;
import org.mercury_im.messenger.service.XmppConnectionService;
import org.mercury_im.messenger.ui.BindingActivity;
import javax.inject.Inject;
public class AccountsActivity extends FragmentActivity implements AccountsFragment.OnListFragmentInteractionListener {
public class AccountsActivity extends BindingActivity implements AccountsFragment.OnListFragmentInteractionListener {
@Inject
AccountRepository accountRepository;
@ -42,4 +44,13 @@ public class AccountsActivity extends FragmentActivity implements AccountsFragme
});
builder.create().show();
}
@Override
protected void onServiceBound() {
super.onServiceBound();
}
public XmppConnectionService getConnectionService() {
return connectionService;
}
}

View File

@ -3,11 +3,13 @@ package org.mercury_im.messenger.ui.login;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.util.LongSparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -15,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView;
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.service.XmppConnectionService;
import org.mercury_im.messenger.xmpp_core.ConnectionState;
/**
* A fragment representing a list of Items.
@ -30,6 +34,8 @@ public class AccountsFragment extends Fragment {
private AccountsRecyclerViewAdapter adapter;
private RecyclerView recyclerView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
@ -58,8 +64,8 @@ public class AccountsFragment extends Fragment {
// Set the adapter
if (view instanceof RecyclerView) {
recyclerView = (RecyclerView) view;
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
this.adapter = new AccountsRecyclerViewAdapter(viewModel.getAccounts().getValue(), mListener);
viewModel.getAccounts().observe(this, roomAccountModels -> adapter.setValues(roomAccountModels));

View File

@ -43,6 +43,7 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
holder.jid.setText(account.getJid());
holder.enabled.setChecked(account.getEnabled());
holder.accountModel = account;
holder.status.setText(account.getState());
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -75,6 +76,7 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
public final CircleImageView avatar;
public final TextView jid;
public final Switch enabled;
public final TextView status;
public ViewHolder(View view) {
super(view);
@ -82,6 +84,7 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
avatar = view.findViewById(R.id.avatar_account);
jid = view.findViewById(R.id.text_account_jid);
enabled = view.findViewById(R.id.switch_account_enabled);
status = view.findViewById(R.id.text_account_status);
}
}
}

View File

@ -29,7 +29,7 @@ import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
import org.mercury_im.messenger.ui.BindingActivity;
import org.mercury_im.messenger.xmpp_android.AndroidMercuryConnection;
import org.mercury_im.messenger.AndroidMercuryConnection;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
import java.io.IOException;

View File

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "c54702e8cd69b3410ca84bab6d4d3aa6",
"identityHash": "1149b7d295726ec2043486e660dcf75d",
"entities": [
{
"tableName": "contacts",
@ -107,7 +107,7 @@
},
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_account_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jid` TEXT, `password` TEXT, `enabled` INTEGER NOT NULL)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_account_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jid` TEXT, `password` TEXT, `enabled` INTEGER NOT NULL, `state` TEXT)",
"fields": [
{
"fieldPath": "id",
@ -132,6 +132,12 @@
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
@ -370,7 +376,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, 'c54702e8cd69b3410ca84bab6d4d3aa6')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1149b7d295726ec2043486e660dcf75d')"
]
}
}

View File

@ -0,0 +1,382 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "1149b7d295726ec2043486e660dcf75d",
"entities": [
{
"tableName": "contacts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_contact_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_account_id` INTEGER NOT NULL, `fk_entity_id` INTEGER NOT NULL, `rostername` TEXT, `nickname` TEXT, FOREIGN KEY(`fk_account_id`) REFERENCES `accounts`(`pk_account_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE RESTRICT )",
"fields": [
{
"fieldPath": "id",
"columnName": "pk_contact_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "accountId",
"columnName": "fk_account_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entityId",
"columnName": "fk_entity_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rosterName",
"columnName": "rostername",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "nickname",
"columnName": "nickname",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"pk_contact_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_contacts_pk_contact_id",
"unique": false,
"columnNames": [
"pk_contact_id"
],
"createSql": "CREATE INDEX `index_contacts_pk_contact_id` ON `${TABLE_NAME}` (`pk_contact_id`)"
},
{
"name": "index_contacts_fk_account_id",
"unique": false,
"columnNames": [
"fk_account_id"
],
"createSql": "CREATE INDEX `index_contacts_fk_account_id` ON `${TABLE_NAME}` (`fk_account_id`)"
},
{
"name": "index_contacts_fk_entity_id",
"unique": false,
"columnNames": [
"fk_entity_id"
],
"createSql": "CREATE INDEX `index_contacts_fk_entity_id` ON `${TABLE_NAME}` (`fk_entity_id`)"
},
{
"name": "index_contacts_pk_contact_id_fk_entity_id",
"unique": true,
"columnNames": [
"pk_contact_id",
"fk_entity_id"
],
"createSql": "CREATE UNIQUE INDEX `index_contacts_pk_contact_id_fk_entity_id` ON `${TABLE_NAME}` (`pk_contact_id`, `fk_entity_id`)"
}
],
"foreignKeys": [
{
"table": "accounts",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"fk_account_id"
],
"referencedColumns": [
"pk_account_id"
]
},
{
"table": "entities",
"onDelete": "RESTRICT",
"onUpdate": "NO ACTION",
"columns": [
"fk_entity_id"
],
"referencedColumns": [
"pk_entity_id"
]
}
]
},
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_account_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jid` TEXT, `password` TEXT, `enabled` INTEGER NOT NULL, `state` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "pk_account_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "jid",
"columnName": "jid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"pk_account_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_accounts_pk_account_id",
"unique": false,
"columnNames": [
"pk_account_id"
],
"createSql": "CREATE INDEX `index_accounts_pk_account_id` ON `${TABLE_NAME}` (`pk_account_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "chats",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_chat_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_entity_id` INTEGER NOT NULL, `active` INTEGER NOT NULL, FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "pk_chat_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "peerEntityId",
"columnName": "fk_entity_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isActive",
"columnName": "active",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"pk_chat_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_chats_pk_chat_id",
"unique": false,
"columnNames": [
"pk_chat_id"
],
"createSql": "CREATE INDEX `index_chats_pk_chat_id` ON `${TABLE_NAME}` (`pk_chat_id`)"
},
{
"name": "index_chats_fk_entity_id",
"unique": false,
"columnNames": [
"fk_entity_id"
],
"createSql": "CREATE INDEX `index_chats_fk_entity_id` ON `${TABLE_NAME}` (`fk_entity_id`)"
}
],
"foreignKeys": [
{
"table": "entities",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"fk_entity_id"
],
"referencedColumns": [
"pk_entity_id"
]
}
]
},
{
"tableName": "messages",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_message_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_account_id` INTEGER NOT NULL, `body` TEXT, `send_date` INTEGER, `from` TEXT, `to` TEXT, `incoming` INTEGER NOT NULL, FOREIGN KEY(`fk_account_id`) REFERENCES `accounts`(`pk_account_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "pk_message_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "accountId",
"columnName": "fk_account_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "body",
"columnName": "body",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sendDate",
"columnName": "send_date",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "from",
"columnName": "from",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "to",
"columnName": "to",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "incoming",
"columnName": "incoming",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"pk_message_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_messages_pk_message_id",
"unique": false,
"columnNames": [
"pk_message_id"
],
"createSql": "CREATE INDEX `index_messages_pk_message_id` ON `${TABLE_NAME}` (`pk_message_id`)"
},
{
"name": "index_messages_fk_account_id",
"unique": false,
"columnNames": [
"fk_account_id"
],
"createSql": "CREATE INDEX `index_messages_fk_account_id` ON `${TABLE_NAME}` (`fk_account_id`)"
}
],
"foreignKeys": [
{
"table": "accounts",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"fk_account_id"
],
"referencedColumns": [
"pk_account_id"
]
}
]
},
{
"tableName": "entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_entity_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_account_id` INTEGER NOT NULL, `jid` TEXT NOT NULL, `avatar` TEXT, FOREIGN KEY(`fk_account_id`) REFERENCES `accounts`(`pk_account_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "pk_entity_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "accountId",
"columnName": "fk_account_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "jid",
"columnName": "jid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "avatarFile",
"columnName": "avatar",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"pk_entity_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_entities_pk_entity_id",
"unique": false,
"columnNames": [
"pk_entity_id"
],
"createSql": "CREATE INDEX `index_entities_pk_entity_id` ON `${TABLE_NAME}` (`pk_entity_id`)"
},
{
"name": "index_entities_fk_account_id_jid",
"unique": true,
"columnNames": [
"fk_account_id",
"jid"
],
"createSql": "CREATE UNIQUE INDEX `index_entities_fk_account_id_jid` ON `${TABLE_NAME}` (`fk_account_id`, `jid`)"
}
],
"foreignKeys": [
{
"table": "accounts",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"fk_account_id"
],
"referencedColumns": [
"pk_account_id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1149b7d295726ec2043486e660dcf75d')"
]
}
}

View File

@ -2,9 +2,12 @@ package org.mercury_im.messenger.persistence.room;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
import org.mercury_im.messenger.persistence.room.dao.ChatDao;
@ -18,7 +21,7 @@ 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;
@Database(version = 1,
@Database(version = 2,
entities = {
RoomContactModel.class,
RoomAccountModel.class,
@ -31,10 +34,19 @@ public abstract class AppDatabase extends RoomDatabase {
private static final String DB_NAME = "mercury_db";
private static AppDatabase INSTANCE;
static Migration m1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE accounts ADD COLUMN state TEXT");
}
};
public static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, DB_NAME).build();
AppDatabase.class, DB_NAME)
.addMigrations(m1_2)
.build();
}
return INSTANCE;
}

View File

@ -6,6 +6,7 @@ import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.TypeConverters;
import androidx.room.Update;
@ -46,4 +47,7 @@ public interface AccountDao extends BaseDao<RoomAccountModel> {
@Query("select * from accounts where jid = :jid")
LiveData<RoomAccountModel> getAccountByJid(EntityBareJid jid);
@Query("update accounts set state = :state where pk_account_id = :accountId")
void updateConnectionState(long accountId, String state);
}

View File

@ -22,6 +22,7 @@ public class RoomAccountModel implements AccountModel {
public static final String KEY_JID = "jid";
public static final String KEY_PASSWORD = "password";
public static final String KEY_ENABLED = "enabled";
public static final String KEY_STATE = "state";
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = KEY_ID)
@ -37,6 +38,9 @@ public class RoomAccountModel implements AccountModel {
@ColumnInfo(name = KEY_ENABLED)
private boolean enabled;
@ColumnInfo(name = KEY_STATE)
private String state;
@Override
public long getId() {
return id;
@ -76,4 +80,14 @@ public class RoomAccountModel implements AccountModel {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public String getState() {
return state;
}
@Override
public void setState(String state) {
this.state = state;
}
}

View File

@ -3,6 +3,7 @@ package org.mercury_im.messenger.persistence.room.repository;
import android.os.AsyncTask;
import androidx.lifecycle.LiveData;
import androidx.room.Transaction;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
@ -51,6 +52,11 @@ public class IAccountRepository implements AccountRepository<RoomAccountModel> {
return -1;
}
@Override
public void updateState(long accountId, String state) {
accountDao.updateConnectionState(accountId, state);
}
private static class InsertAsyncTask extends AsyncTask<RoomAccountModel, Void, Long> {
private final AccountDao accountDao;

View File

@ -19,4 +19,8 @@ public interface AccountModel {
boolean getEnabled();
void setEnabled(boolean enabled);
String getState();
void setState(String state);
}

View File

@ -15,4 +15,6 @@ public interface AccountRepository<E extends AccountModel> {
List<E> getAllAccounts();
long insertAccount(E accountModel);
void updateState(long accountId, String state);
}

View File

@ -1 +1,4 @@
include ':app', ':xmpp_core', ':persistence-room', ':persistence', ':xmpp_android'
include ':app',
':xmpp_core',
':persistence-room',
':persistence'

View File

@ -1 +0,0 @@
/build

View File

@ -1,39 +0,0 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api project(":xmpp_core")
// Android related Smack libraries
api "org.igniterealtime.smack:smack-android:$smackAndroidVersion"
api "org.igniterealtime.smack:smack-android-extensions:$smackAndroidExtensionsVersion"
implementation 'androidx.appcompat:appcompat:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

View File

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -1,27 +0,0 @@
package org.mercury_im.messenger.xmpp_android;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("org.mercury_im.messenger.xmpp_android.test", appContext.getPackageName());
}
}

View File

@ -1,2 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mercury_im.messenger.xmpp_android" />

View File

@ -1,21 +0,0 @@
package org.mercury_im.messenger.xmpp_android;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import org.mercury_im.messenger.xmpp_core.ConnectionState;
import org.mercury_im.messenger.xmpp_core.ConnectionStateHolder;
public class AndroidConnectionStateHolder implements ConnectionStateHolder {
private MutableLiveData<ConnectionState> connectionState = new MutableLiveData<>();
@Override
public void updateConnectionState(ConnectionState state) {
this.connectionState.postValue(state);
}
public LiveData<ConnectionState> getConnectionState() {
return connectionState;
}
}

View File

@ -1,18 +0,0 @@
package org.mercury_im.messenger.xmpp_android;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.mercury_im.messenger.xmpp_core.ConnectionStateHolder;
import org.mercury_im.messenger.xmpp_core.MercuryConnection;
public class AndroidMercuryConnection extends MercuryConnection {
public AndroidMercuryConnection(XMPPTCPConnection connection, long accountId) {
super(connection, accountId);
this.stateHolder = new AndroidConnectionStateHolder();
}
@Override
public ConnectionStateHolder getState() {
return stateHolder;
}
}

View File

@ -1,3 +0,0 @@
<resources>
<string name="app_name">XmppAndroid</string>
</resources>

View File

@ -1,17 +0,0 @@
package org.mercury_im.messenger.xmpp_android;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@ -42,6 +42,8 @@ public abstract class MercuryConnection implements ConnectionListener {
protected ConnectionStateHolder stateHolder;
protected ConnectionStateListener stateListener = null;
public MercuryConnection(XMPPConnection connection, long accountId) {
this.connection = connection;
connection.addConnectionListener(this);
@ -72,6 +74,10 @@ public abstract class MercuryConnection implements ConnectionListener {
return connection;
}
public void setConnectionStateListener(ConnectionStateListener stateListener) {
this.stateListener = stateListener;
}
public void setRosterHandler(RosterHandler handler) {
roster.addRosterListener(handler);
roster.addRosterLoadedListener(handler);
@ -83,8 +89,6 @@ public abstract class MercuryConnection implements ConnectionListener {
chatManager.addOutgoingListener(plainMessageHandler);
}
public abstract ConnectionStateHolder getState();
public long getAccountId() {
return accountId;
}
@ -95,11 +99,13 @@ public abstract class MercuryConnection implements ConnectionListener {
@Override
public void connected(XMPPConnection connection) {
getState().updateConnectionState(ConnectionState.CONNECTED);
LOGGER.info("Connection " + getAccountId() + " connected.");
notifyStateListener(ConnectionState.CONNECTING);
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {
LOGGER.info("Connection " + getAccountId() + " authenticated (" + (resumed ? "resumed" : "first time") + ")");
if (connection == this.connection && !resumed) {
LOGGER.info("Enabling carbons!");
carbonManager.enableCarbonsAsync(exception -> {
@ -107,18 +113,28 @@ public abstract class MercuryConnection implements ConnectionListener {
exception.printStackTrace();
});
}
getState().updateConnectionState(ConnectionState.CONNECTED);
notifyStateListener(ConnectionState.CONNECTED);
}
@Override
public void connectionClosed() {
LOGGER.fine("Connection closed.");
getState().updateConnectionState(ConnectionState.DISCONNECTED);
LOGGER.fine("Connection " + accountId + " closed.");
notifyStateListener(ConnectionState.DISCONNECTED);
}
@Override
public void connectionClosedOnError(Exception e) {
LOGGER.severe("Connection closed on error: " + e.getMessage());
getState().updateConnectionState(ConnectionState.DISCONNECTED);
LOGGER.severe("Connection " + accountId + " closed on error: " + e.getMessage());
notifyStateListener(ConnectionState.WAIRING_FOR_RETRY);
}
private void notifyStateListener(ConnectionState newState) {
if (stateListener != null) {
stateListener.stateChanged(this, newState);
}
}
public interface ConnectionStateListener {
void stateChanged(MercuryConnection connection, ConnectionState state);
}
}