mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2024-06-17 00:54:52 +02:00
Too lazy to comment. Simplifications and rewrites of login
This commit is contained in:
parent
ccddad2e31
commit
3569462a78
|
@ -4,7 +4,6 @@ import org.mercury_im.messenger.MercuryImApplication;
|
||||||
import org.mercury_im.messenger.data.di.RepositoryModule;
|
import org.mercury_im.messenger.data.di.RepositoryModule;
|
||||||
import org.mercury_im.messenger.di.module.AndroidPersistenceModule;
|
import org.mercury_im.messenger.di.module.AndroidPersistenceModule;
|
||||||
import org.mercury_im.messenger.di.module.AppModule;
|
import org.mercury_im.messenger.di.module.AppModule;
|
||||||
import org.mercury_im.messenger.di.module.MercuryModule;
|
|
||||||
import org.mercury_im.messenger.service.MercuryConnectionService;
|
import org.mercury_im.messenger.service.MercuryConnectionService;
|
||||||
import org.mercury_im.messenger.ui.MainActivity;
|
import org.mercury_im.messenger.ui.MainActivity;
|
||||||
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
||||||
|
@ -30,8 +29,7 @@ import dagger.Component;
|
||||||
modules = {
|
modules = {
|
||||||
AppModule.class,
|
AppModule.class,
|
||||||
AndroidPersistenceModule.class,
|
AndroidPersistenceModule.class,
|
||||||
RepositoryModule.class,
|
RepositoryModule.class
|
||||||
MercuryModule.class
|
|
||||||
})
|
})
|
||||||
public interface AppComponent {
|
public interface AppComponent {
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.mercury_im.messenger.ui.login;
|
package org.mercury_im.messenger.ui.login;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
@ -14,7 +13,6 @@ import androidx.lifecycle.ViewModelProvider;
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
|
||||||
import org.mercury_im.messenger.MercuryImApplication;
|
import org.mercury_im.messenger.MercuryImApplication;
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.R;
|
import org.mercury_im.messenger.R;
|
||||||
import org.mercury_im.messenger.account.error.PasswordError;
|
import org.mercury_im.messenger.account.error.PasswordError;
|
||||||
import org.mercury_im.messenger.account.error.UsernameError;
|
import org.mercury_im.messenger.account.error.UsernameError;
|
||||||
|
@ -54,42 +52,42 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
||||||
viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
|
viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
|
||||||
observeViewModel();
|
observeViewModel();
|
||||||
setupTextInputFields();
|
setupTextInputFields();
|
||||||
mSignInView.setOnClickListener(view -> viewModel.loginDetailsEntered());
|
mSignInView.setOnClickListener(view -> viewModel.login());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observeViewModel() {
|
private void observeViewModel() {
|
||||||
observeUsernameError();
|
observeUsernameError();
|
||||||
observePasswordError();
|
observePasswordError();
|
||||||
finishOnceLoginWasSuccessful();
|
finishOnceLoginWasSuccessful();
|
||||||
displayCredentials(viewModel.getAccount());
|
enableLoginButtonOncePossible();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observeUsernameError() {
|
private void observeUsernameError() {
|
||||||
viewModel.getUsernameError().observe(this, usernameError -> {
|
viewModel.getUsernameError().observe(this, usernameError -> {
|
||||||
Optional<String> errorMessage = getUsernameError(usernameError);
|
mUsernameView.setError(usernameError.isError() ? usernameError.getErrorMessage() : null);
|
||||||
if (errorMessage.isPresent()) {
|
|
||||||
mUsernameView.setError(errorMessage.getItem());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observePasswordError() {
|
private void observePasswordError() {
|
||||||
viewModel.getPasswordError().observe(this, passwordError -> {
|
viewModel.getPasswordError().observe(this, passwordError -> {
|
||||||
Optional<String> errorMessage = getPasswordError(passwordError);
|
mPasswordView.setError(passwordError.isError() ? passwordError.getErrorMessage() : null);
|
||||||
if (errorMessage.isPresent()) {
|
|
||||||
mPasswordView.setError(errorMessage.getItem());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishOnceLoginWasSuccessful() {
|
private void finishOnceLoginWasSuccessful() {
|
||||||
viewModel.getSigninSuccessful().observe(this, successful -> {
|
viewModel.isLoginSuccessful().observe(this, successful -> {
|
||||||
if (Boolean.TRUE.equals(successful)) {
|
if (Boolean.TRUE.equals(successful)) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableLoginButtonOncePossible() {
|
||||||
|
viewModel.isLoginEnabled().observe(this, isEnabled -> {
|
||||||
|
mSignInView.setEnabled(isEnabled);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<String> getUsernameError(UsernameError usernameError) {
|
private Optional<String> getUsernameError(UsernameError usernameError) {
|
||||||
switch (usernameError) {
|
switch (usernameError) {
|
||||||
case none:
|
case none:
|
||||||
|
@ -130,8 +128,8 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
||||||
mUsernameView.setText(accountEvent.getAddress());
|
mUsernameView.setText(accountEvent.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountEvent.getAuthentication() != null) {
|
if (accountEvent.getPassword() != null) {
|
||||||
mPasswordView.setText(accountEvent.getAuthentication().getPassword());
|
mPasswordView.setText(accountEvent.getPassword());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -143,16 +141,14 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
||||||
mUsernameView.addTextChangedListener(new TextChangedListener() {
|
mUsernameView.addTextChangedListener(new TextChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
viewModel.onUsernameInputChanged(charSequence.toString());
|
viewModel.setUsername(charSequence.toString());
|
||||||
Log.d(Messenger.TAG, "onTextChanged");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mPasswordView.addTextChangedListener(new TextChangedListener() {
|
mPasswordView.addTextChangedListener(new TextChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
viewModel.onPasswordInputChanged(charSequence.toString());
|
viewModel.setPassword(charSequence.toString());
|
||||||
Log.d(Messenger.TAG, "onTextChanged");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -169,7 +165,7 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
||||||
|
|
||||||
case R.id.password:
|
case R.id.password:
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
|
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
|
||||||
viewModel.loginDetailsEntered();
|
viewModel.login();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,140 +1,138 @@
|
||||||
package org.mercury_im.messenger.ui.login;
|
package org.mercury_im.messenger.ui.login;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.app.Application;
|
||||||
import android.util.Log;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.ViewModel;
|
|
||||||
|
|
||||||
import io.reactivex.Single;
|
|
||||||
import io.reactivex.observers.DisposableSingleObserver;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
import org.mercury_im.messenger.MercuryImApplication;
|
import org.mercury_im.messenger.MercuryImApplication;
|
||||||
import org.mercury_im.messenger.account.error.PasswordError;
|
import org.mercury_im.messenger.Messenger;
|
||||||
import org.mercury_im.messenger.account.error.UsernameError;
|
import org.mercury_im.messenger.R;
|
||||||
import org.mercury_im.messenger.data.repository.AccountRepository;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.entity.IAccount;
|
import org.mercury_im.messenger.entity.IAccount;
|
||||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class LoginViewModel extends ViewModel {
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
@Inject
|
public class LoginViewModel extends AndroidViewModel {
|
||||||
AccountRepository accountRepository;
|
|
||||||
|
|
||||||
// @Inject
|
private MutableLiveData<Error> usernameError = new MutableLiveData<>(new Error());
|
||||||
// ConnectionCenter connectionCenter;
|
private MutableLiveData<Error> passwordError = new MutableLiveData<>(new Error());
|
||||||
|
private MutableLiveData<Boolean> loginEnabled = new MutableLiveData<>(false);
|
||||||
|
private MutableLiveData<Boolean> loginSuccessful = new MutableLiveData<>(false);
|
||||||
|
|
||||||
private String username;
|
private EntityBareJid username;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private MutableLiveData<UsernameError> usernameError = new MutableLiveData<>(UsernameError.none);
|
@Inject
|
||||||
private MutableLiveData<PasswordError> passwordError = new MutableLiveData<>(PasswordError.none);
|
Messenger messenger;
|
||||||
|
|
||||||
private MutableLiveData<Account> account = new MutableLiveData<>();
|
public LoginViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
private MutableLiveData<Boolean> signinSuccessful = new MutableLiveData<>();
|
((MercuryImApplication) application).getAppComponent().inject(this);
|
||||||
|
|
||||||
public LoginViewModel() {
|
|
||||||
super();
|
|
||||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
|
||||||
init(new IAccount());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Boolean> getSigninSuccessful() {
|
public void setUsername(String username) {
|
||||||
return signinSuccessful;
|
if (username == null || username.isEmpty()) {
|
||||||
|
usernameError.setValue(new Error(getApplication().getResources().getString(R.string.error_field_required)));
|
||||||
|
this.username = null;
|
||||||
|
updateLoginEnabled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.username = JidCreate.entityBareFrom(username);
|
||||||
|
updateLoginEnabled();
|
||||||
|
} catch (XmppStringprepException e) {
|
||||||
|
usernameError.setValue(new Error(getApplication().getResources().getString(R.string.error_invalid_username)));
|
||||||
|
this.username = null;
|
||||||
|
updateLoginEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUsernameInputChanged(String input) {
|
public void setPassword(String password) {
|
||||||
this.username = input;
|
this.password = password;
|
||||||
|
updateLoginEnabled();
|
||||||
|
if (password == null || password.isEmpty()) {
|
||||||
|
passwordError.setValue(new Error(getApplication().getResources().getString(R.string.error_field_required)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPasswordInputChanged(String input) {
|
private void updateLoginEnabled() {
|
||||||
this.password = input;
|
loginEnabled.setValue(username != null && !(password == null || password.isEmpty()));
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<UsernameError> getUsernameError() {
|
|
||||||
return usernameError;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<PasswordError> getPasswordError() {
|
|
||||||
return passwordError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to parse the input string into a {@link EntityBareJid} and return it.
|
|
||||||
* Return null on failure.
|
|
||||||
* @param input input string
|
|
||||||
* @return valid username or null
|
|
||||||
*/
|
|
||||||
private EntityBareJid asValidUsernameOrNull(String input) {
|
|
||||||
return JidCreate.entityBareFromOrNull(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPasswordValid(String password) {
|
|
||||||
return !password.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(@NonNull Account account) {
|
|
||||||
this.account.setValue(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MutableLiveData<Account> getAccount() {
|
|
||||||
return account;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login() {
|
public void login() {
|
||||||
Account account = getAccount().getValue();
|
Account account = new IAccount();
|
||||||
if (account != null && account.getAddress() != null
|
account.setAddress(username.asUnescapedString());
|
||||||
&& !TextUtils.isEmpty(account.getAuthentication().getPassword())) {
|
account.setPassword(password);
|
||||||
accountRepository.upsertAccount(account);
|
account.setEnabled(true);
|
||||||
}
|
loginEnabled.setValue(false);
|
||||||
|
messenger.addAccount().enableAccountAndLogin(account)
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.map(result -> {
|
||||||
|
switch (result) {
|
||||||
|
case success:
|
||||||
|
loginSuccessful.setValue(true);
|
||||||
|
break;
|
||||||
|
case credential_error:
|
||||||
|
passwordError.setValue(new Error(getApplication().getResources().getString(R.string.error_incorrect_password)));
|
||||||
|
loginEnabled.setValue(true);
|
||||||
|
break;
|
||||||
|
case server_error:
|
||||||
|
case other_error:
|
||||||
|
Toast.makeText(getApplication(), "A connection error occurred", Toast.LENGTH_LONG);
|
||||||
|
loginEnabled.setValue(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.ignoreElement()
|
||||||
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginDetailsEntered() {
|
public LiveData<Error> getPasswordError() {
|
||||||
boolean loginIntact = true;
|
return passwordError;
|
||||||
if (username.isEmpty()) {
|
}
|
||||||
usernameError.postValue(UsernameError.emptyUsername);
|
|
||||||
loginIntact = false;
|
public LiveData<Error> getUsernameError() {
|
||||||
|
return usernameError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Boolean> isLoginSuccessful() {
|
||||||
|
return loginSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Boolean> isLoginEnabled() {
|
||||||
|
return loginEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Error {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public Error() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityBareJid bareJid = asValidUsernameOrNull(username);
|
public Error(String errorMessage) {
|
||||||
if (bareJid == null) {
|
this.message = errorMessage;
|
||||||
usernameError.postValue(UsernameError.invalidUsername);
|
|
||||||
loginIntact = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPasswordValid(password)) {
|
public boolean isError() {
|
||||||
passwordError.postValue(PasswordError.invalidPassword);
|
return message != null;
|
||||||
loginIntact = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginIntact) {
|
public String getErrorMessage() {
|
||||||
Account account = new IAccount();
|
return message;
|
||||||
account.setEnabled(true);
|
|
||||||
account.setAddress(bareJid.toString());
|
|
||||||
account.setAuthentication(new PasswordAuthentication(password));
|
|
||||||
Single<Account> insert = accountRepository.upsertAccount(account);
|
|
||||||
insert.subscribe(
|
|
||||||
new DisposableSingleObserver<Account>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Account inserted) {
|
|
||||||
// connectionCenter.createConnection(account);
|
|
||||||
signinSuccessful.setValue(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable e) {
|
|
||||||
Log.e("Mercury", "Could not insert new Account data.", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ sourceSets {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
api project(':entity') // Entities
|
api project(':entity')
|
||||||
|
|
||||||
// Smack
|
// Smack
|
||||||
// Not all of those are needed, but it may be a good idea to define those versions explicitly
|
// Not all of those are needed, but it may be a good idea to define those versions explicitly
|
||||||
|
@ -37,6 +37,7 @@ dependencies {
|
||||||
|
|
||||||
// JUnit for testing
|
// JUnit for testing
|
||||||
testImplementation "junit:junit:$junitVersion"
|
testImplementation "junit:junit:$junitVersion"
|
||||||
|
compile project(path: ':data')
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = "8"
|
sourceCompatibility = "8"
|
||||||
|
|
|
@ -4,8 +4,8 @@ import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.mercury_im.messenger.xmpp.model.EntityCapsModel;
|
import org.mercury_im.messenger.data.model.EntityCapsModel;
|
||||||
import org.mercury_im.messenger.xmpp.repository.EntityCapsRepository;
|
import org.mercury_im.messenger.data.repository.XmppEntityCapsRepository;
|
||||||
|
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
@ -23,13 +23,13 @@ public class EntityCapsStore implements EntityCapsPersistentCache {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(EntityCapsStore.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(EntityCapsStore.class.getName());
|
||||||
|
|
||||||
private final EntityCapsRepository entityCapsRepository;
|
private final XmppEntityCapsRepository entityCapsRepository;
|
||||||
private final Map<String, DiscoverInfo> discoverInfoMap = new HashMap<>();
|
private final Map<String, DiscoverInfo> discoverInfoMap = new HashMap<>();
|
||||||
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public EntityCapsStore(EntityCapsRepository entityCapsRepository) {
|
public EntityCapsStore(XmppEntityCapsRepository entityCapsRepository) {
|
||||||
this.entityCapsRepository = entityCapsRepository;
|
this.entityCapsRepository = entityCapsRepository;
|
||||||
populateFromDatabase();
|
populateFromDatabase();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.mercury_im.messenger.data.repository.GroupChatRepository;
|
||||||
import org.mercury_im.messenger.data.repository.MessageRepository;
|
import org.mercury_im.messenger.data.repository.MessageRepository;
|
||||||
import org.mercury_im.messenger.data.repository.PeerRepository;
|
import org.mercury_im.messenger.data.repository.PeerRepository;
|
||||||
import org.mercury_im.messenger.data.repository.DirectChatRepository;
|
import org.mercury_im.messenger.data.repository.DirectChatRepository;
|
||||||
import org.mercury_im.messenger.data.repository.EntityCapsRepository;
|
import org.mercury_im.messenger.data.repository.XmppEntityCapsRepository;
|
||||||
import org.mercury_im.messenger.data.repository.Repositories;
|
import org.mercury_im.messenger.data.repository.Repositories;
|
||||||
import org.mercury_im.messenger.data.repository.XmppAccountRepository;
|
import org.mercury_im.messenger.data.repository.XmppAccountRepository;
|
||||||
import org.mercury_im.messenger.data.repository.XmppDirectChatRepository;
|
import org.mercury_im.messenger.data.repository.XmppDirectChatRepository;
|
||||||
|
@ -89,11 +89,11 @@ public class RepositoryModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static EntityCapsRepository provideCapsRepository(
|
static XmppEntityCapsRepository provideCapsRepository(
|
||||||
ReactiveEntityStore<Persistable> data,
|
ReactiveEntityStore<Persistable> data,
|
||||||
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler ioScheduler,
|
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler ioScheduler,
|
||||||
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler uiScheduler) {
|
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler uiScheduler) {
|
||||||
return new EntityCapsRepository(data, ioScheduler, uiScheduler);
|
return new XmppEntityCapsRepository(data, ioScheduler, uiScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.mercury_im.messenger.data.mapping;
|
||||||
import org.mercury_im.messenger.data.model.AccountModel;
|
import org.mercury_im.messenger.data.model.AccountModel;
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.entity.IAccount;
|
import org.mercury_im.messenger.entity.IAccount;
|
||||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ public class AccountMapping extends AbstractMapping<Account, AccountModel> {
|
||||||
@Override
|
@Override
|
||||||
public AccountModel mapToModel(Account entity, AccountModel model) {
|
public AccountModel mapToModel(Account entity, AccountModel model) {
|
||||||
model.setAddress(entity.getAddress());
|
model.setAddress(entity.getAddress());
|
||||||
model.setPassword(entity.getAuthentication().getPassword());
|
model.setPassword(entity.getPassword());
|
||||||
model.setEnabled(entity.isEnabled());
|
model.setEnabled(entity.isEnabled());
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
@ -37,7 +36,7 @@ public class AccountMapping extends AbstractMapping<Account, AccountModel> {
|
||||||
public Account mapToEntity(AccountModel model, Account entity) {
|
public Account mapToEntity(AccountModel model, Account entity) {
|
||||||
entity.setId(model.getId());
|
entity.setId(model.getId());
|
||||||
entity.setAddress(model.getAddress());
|
entity.setAddress(model.getAddress());
|
||||||
entity.setAuthentication(new PasswordAuthentication(model.getPassword()));
|
entity.setPassword(model.getPassword());
|
||||||
entity.setEnabled(model.isEnabled());
|
entity.setEnabled(model.isEnabled());
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
|
|
|
@ -9,10 +9,10 @@ import io.reactivex.Scheduler;
|
||||||
import io.requery.Persistable;
|
import io.requery.Persistable;
|
||||||
import io.requery.reactivex.ReactiveEntityStore;
|
import io.requery.reactivex.ReactiveEntityStore;
|
||||||
|
|
||||||
public class EntityCapsRepository extends RequeryRepository {
|
public class XmppEntityCapsRepository extends RequeryRepository {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public EntityCapsRepository(
|
public XmppEntityCapsRepository(
|
||||||
ReactiveEntityStore<Persistable> data,
|
ReactiveEntityStore<Persistable> data,
|
||||||
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler subscriberScheduler,
|
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler subscriberScheduler,
|
||||||
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler observerScheduler) {
|
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler observerScheduler) {
|
|
@ -74,8 +74,8 @@ public class XmppGroupChatRepository
|
||||||
@Override
|
@Override
|
||||||
public Single<GroupChat> getOrCreateGroupChat(Account account, String roomAddress) {
|
public Single<GroupChat> getOrCreateGroupChat(Account account, String roomAddress) {
|
||||||
return getGroupChatByRoomAddress(account, roomAddress)
|
return getGroupChatByRoomAddress(account, roomAddress)
|
||||||
.switchIfEmpty(Single
|
.switchIfEmpty(
|
||||||
.just((GroupChat) new IGroupChat() {
|
Single.just((GroupChat) new IGroupChat() {
|
||||||
{
|
{
|
||||||
setAccount(account);
|
setAccount(account);
|
||||||
setRoomAddress(roomAddress);
|
setRoomAddress(roomAddress);
|
||||||
|
|
|
@ -6,21 +6,6 @@ import org.mercury_im.messenger.data.di.InMemoryDatabaseComponent;
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.entity.IAccount;
|
import org.mercury_im.messenger.entity.IAccount;
|
||||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
||||||
import org.mercury_im.messenger.entity.chat.DirectChat;
|
|
||||||
import org.mercury_im.messenger.entity.chat.IDirectChat;
|
|
||||||
import org.mercury_im.messenger.entity.contact.IPeer;
|
|
||||||
import org.mercury_im.messenger.entity.contact.Peer;
|
|
||||||
import org.mercury_im.messenger.entity.message.IMessage;
|
|
||||||
import org.mercury_im.messenger.entity.message.IPayloadContainer;
|
|
||||||
import org.mercury_im.messenger.entity.message.Message;
|
|
||||||
import org.mercury_im.messenger.entity.message.PayloadContainer;
|
|
||||||
import org.mercury_im.messenger.entity.message.content.TextPayload;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -28,8 +13,6 @@ import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.requery.Persistable;
|
import io.requery.Persistable;
|
||||||
import io.requery.reactivex.ReactiveEntityStore;
|
import io.requery.reactivex.ReactiveEntityStore;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
|
|
||||||
public class AccountRepositoryTest {
|
public class AccountRepositoryTest {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
|
1
domain/.gitignore
vendored
1
domain/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/build
|
/build
|
||||||
|
testcredentials.properties
|
||||||
|
|
|
@ -4,6 +4,16 @@ dependencies {
|
||||||
|
|
||||||
implementation project(':entity')
|
implementation project(':entity')
|
||||||
|
|
||||||
|
// Smack
|
||||||
|
// Not all of those are needed, but it may be a good idea to define those versions explicitly
|
||||||
|
api "org.igniterealtime.smack:smack-core:$smackCoreVersion"
|
||||||
|
api "org.igniterealtime.smack:smack-experimental:$smackExperimentalVersion"
|
||||||
|
api "org.igniterealtime.smack:smack-extensions:$smackExtensionsVersion"
|
||||||
|
api "org.igniterealtime.smack:smack-im:$smackImVersion"
|
||||||
|
api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion"
|
||||||
|
|
||||||
|
testImplementation "org.igniterealtime.smack:smack-java7:$smackJava7Version"
|
||||||
|
|
||||||
// RxJava2
|
// RxJava2
|
||||||
implementation "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
implementation "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.mercury_im.messenger;
|
package org.mercury_im.messenger;
|
||||||
|
|
||||||
import org.mercury_im.messenger.transport.listener.IncomingDirectMessageListener;
|
import org.mercury_im.messenger.listener.IncomingDirectMessageListener;
|
||||||
import org.mercury_im.messenger.entity.chat.Chat;
|
import org.mercury_im.messenger.entity.chat.Chat;
|
||||||
import org.mercury_im.messenger.entity.message.Message;
|
import org.mercury_im.messenger.entity.message.Message;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ package org.mercury_im.messenger;
|
||||||
|
|
||||||
import org.mercury_im.messenger.data.repository.Repositories;
|
import org.mercury_im.messenger.data.repository.Repositories;
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.transport.connection.ConnectionMethod;
|
import org.mercury_im.messenger.usecase.AddAccount;
|
||||||
|
import org.mercury_im.messenger.xmpp.MercuryConnection;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -13,28 +14,23 @@ public class Messenger {
|
||||||
|
|
||||||
public static final String TAG = "MercuryIM";
|
public static final String TAG = "MercuryIM";
|
||||||
|
|
||||||
private final Map<Long, ConnectionMethod> connections = new HashMap<>();
|
private final Map<Long, MercuryConnection> connections = new HashMap<>();
|
||||||
|
private Repositories repositories;
|
||||||
private final Repositories repositories;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Messenger(Repositories repositories) {
|
public Messenger(Repositories repositories) {
|
||||||
this.repositories = repositories;
|
this.repositories = repositories;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConnection(ConnectionMethod connection) {
|
public void addConnection(MercuryConnection connection) {
|
||||||
connections.put(connection.getAccount().getId(), connection);
|
connections.put(connection.getAccount().getId(), connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionMethod getConnection(Account account) {
|
public MercuryConnection getConnection(Account account) {
|
||||||
return connections.get(account.getId());
|
return connections.get(account.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void appInUse() {
|
public AddAccount addAccount() {
|
||||||
|
return new AddAccount(repositories.getAccountRepository(), this);
|
||||||
}
|
|
||||||
|
|
||||||
public void appInBackground() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
package org.mercury_im.messenger.di.module;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.data.repository.Repositories;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class MercuryModule {
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
static Messenger provideMessenger(Repositories repositories) {
|
|
||||||
return new Messenger(repositories);
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.mercury_im.messenger.exception;
|
||||||
|
|
||||||
|
public class IllegalUsernameException extends RuntimeException {
|
||||||
|
|
||||||
|
public IllegalUsernameException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.mercury_im.messenger.transport.listener;
|
package org.mercury_im.messenger.listener;
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.entity.chat.DirectChat;
|
import org.mercury_im.messenger.entity.chat.DirectChat;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.mercury_im.messenger.transport.listener;
|
package org.mercury_im.messenger.listener;
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
import org.mercury_im.messenger.entity.chat.GroupChat;
|
import org.mercury_im.messenger.entity.chat.GroupChat;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.mercury_im.messenger.transport.listener;
|
package org.mercury_im.messenger.listener;
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.chat.Chat;
|
import org.mercury_im.messenger.entity.chat.Chat;
|
||||||
import org.mercury_im.messenger.entity.event.TypingEvent;
|
import org.mercury_im.messenger.entity.event.TypingEvent;
|
|
@ -1,27 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
public class AccountChangedEvent {
|
|
||||||
|
|
||||||
private final Account account;
|
|
||||||
private final EventType eventType;
|
|
||||||
|
|
||||||
public AccountChangedEvent(Account account, EventType eventType) {
|
|
||||||
this.account = account;
|
|
||||||
this.eventType = eventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account getAccount() {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EventType getEventType() {
|
|
||||||
return eventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EventType {
|
|
||||||
enabled,
|
|
||||||
disabled
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.data.repository.AccountRepository;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
import org.mercury_im.messenger.util.DiffUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
public class AccountChangedHandler {
|
|
||||||
|
|
||||||
private final Messenger messenger;
|
|
||||||
private final AccountRepository accountRepository;
|
|
||||||
private final Map<Long, Account> accounts = new HashMap<>();
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AccountChangedHandler(Messenger messenger, AccountRepository accountRepository) {
|
|
||||||
this.messenger = messenger;
|
|
||||||
this.accountRepository = accountRepository;
|
|
||||||
//disposable.add(accountRepository.observeAllAccounts()
|
|
||||||
// .scan(new ArrayList<>(), this::calculateChangeEvents)
|
|
||||||
// .subscribe(this::onAccountsChanged));
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<Long, Account> calculateChangeEvents(List<Account> accounts, List<Account> newAccounts) {
|
|
||||||
DiffUtil.Diff<Account> diff = DiffUtil.diff(accounts, newAccounts);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountChangedEvent calculateEvent(Account previous, Account next) {
|
|
||||||
if (previous == null && next == null) {
|
|
||||||
throw new IllegalArgumentException("Not both accounts can be null at the same time!");
|
|
||||||
}
|
|
||||||
if (previous == null) {
|
|
||||||
if (next.isEnabled()) {
|
|
||||||
return new AccountChangedEvent(next, AccountChangedEvent.EventType.enabled);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (next == null) {
|
|
||||||
if (previous.isEnabled()) {
|
|
||||||
return new AccountChangedEvent(previous, AccountChangedEvent.EventType.disabled);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (previous.isEnabled()) {
|
|
||||||
if (next.isEnabled()) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return new AccountChangedEvent(next, AccountChangedEvent.EventType.disabled);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (next.isEnabled()) {
|
|
||||||
return new AccountChangedEvent(next, AccountChangedEvent.EventType.enabled);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onAccountsChanged(List<Account> accounts, List<Account> newAccounts) {
|
|
||||||
DiffUtil.Diff<Account> diff = DiffUtil.diff(accounts, newAccounts);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport;
|
|
||||||
|
|
||||||
public enum ConnectionType {
|
|
||||||
// Smack Connection Types from module transport_xmpp.
|
|
||||||
/**
|
|
||||||
* Underlying connection is a Smack XMPPTCPConnection.
|
|
||||||
*/
|
|
||||||
SMACK_TCP,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Underlying connection is a Smack XMPPBOSHConnection.
|
|
||||||
* @deprecated Not yet implemented.
|
|
||||||
*/
|
|
||||||
SMACK_BOSH,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Underlying connection is a Smack XMPPWebsocketConnection.
|
|
||||||
* @deprecated Not yet implemented.
|
|
||||||
*/
|
|
||||||
SMACK_WEBSOCKETS,
|
|
||||||
;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.connection;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
public abstract class AbstractConnectionMethod
|
|
||||||
implements ConnectionMethod {
|
|
||||||
|
|
||||||
protected final Account account;
|
|
||||||
protected final Messenger messenger;
|
|
||||||
|
|
||||||
public AbstractConnectionMethod(Account account, Messenger messenger) {
|
|
||||||
this.account = account;
|
|
||||||
this.messenger = messenger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Account getAccount() {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Messenger getMessenger() {
|
|
||||||
return messenger;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.connection;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
public interface ConnectionFactory<
|
|
||||||
CM extends ConnectionMethod> {
|
|
||||||
|
|
||||||
Messenger getMessenger();
|
|
||||||
|
|
||||||
CM provideConnection(Account account);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.connection;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.transport.ConnectionType;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
|
|
||||||
public interface ConnectionMethod {
|
|
||||||
|
|
||||||
Account getAccount();
|
|
||||||
|
|
||||||
Messenger getMessenger();
|
|
||||||
|
|
||||||
Completable connect();
|
|
||||||
|
|
||||||
ConnectionType getConnectionType();
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.connection.exception;
|
|
||||||
|
|
||||||
public class ConnectionFailedException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
}
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
package org.mercury_im.messenger.usecase;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||||
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
import org.mercury_im.messenger.Messenger;
|
||||||
|
import org.mercury_im.messenger.data.repository.AccountRepository;
|
||||||
|
import org.mercury_im.messenger.entity.Account;
|
||||||
|
import org.mercury_im.messenger.entity.IAccount;
|
||||||
|
import org.mercury_im.messenger.exception.IllegalUsernameException;
|
||||||
|
import org.mercury_im.messenger.xmpp.MercuryConnection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public class AddAccount {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AddAccount.class.getName());
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String server;
|
||||||
|
private BareJid address;
|
||||||
|
private String password;
|
||||||
|
private Account account;
|
||||||
|
|
||||||
|
private final AccountRepository accountRepository;
|
||||||
|
private final Messenger messenger;
|
||||||
|
|
||||||
|
public AddAccount(AccountRepository accountRepository, Messenger messenger) {
|
||||||
|
this.accountRepository = accountRepository;
|
||||||
|
this.messenger = messenger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
try {
|
||||||
|
this.address = JidCreate.entityBareFrom(address);
|
||||||
|
} catch (XmppStringprepException e) {
|
||||||
|
this.address = null;
|
||||||
|
throw new IllegalUsernameException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddressSet() {
|
||||||
|
return address != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPasswordSet() {
|
||||||
|
return password != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Single<Account> insertAccountIntoDatabase() {
|
||||||
|
Account account = new IAccount();
|
||||||
|
account.setAddress(address.asUnescapedString());
|
||||||
|
account.setPassword(password);
|
||||||
|
return accountRepository.insertAccount(account).doAfterSuccess(this::setAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAccount(Account account) {
|
||||||
|
this.account = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Single<ConnectionResult> enableAccountAndLogin(Account account) {
|
||||||
|
return enableAccount(account).andThen(login(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Completable enableAccount(Account account) {
|
||||||
|
account.setEnabled(true);
|
||||||
|
return accountRepository.upsertAccount(account)
|
||||||
|
.doAfterSuccess(this::setAccount)
|
||||||
|
.ignoreElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Single<ConnectionResult> login(Account account) {
|
||||||
|
return Single.fromCallable(() -> {
|
||||||
|
MercuryConnection connection = getOrCreateConnection(account);
|
||||||
|
return authenticateIfNecessary(connection);
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MercuryConnection getOrCreateConnection(Account account) {
|
||||||
|
MercuryConnection connection = messenger.getConnection(account);
|
||||||
|
if (connection == null) {
|
||||||
|
connection = new MercuryConnection(account);
|
||||||
|
messenger.addConnection(connection);
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConnectionResult authenticateIfNecessary(MercuryConnection connection) {
|
||||||
|
try {
|
||||||
|
doAuthenticateIfNecessary(connection);
|
||||||
|
return ConnectionResult.success;
|
||||||
|
} catch (SASLErrorException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "SASL Error while connecting to account " + account.getAddress(), e);
|
||||||
|
return ConnectionResult.credential_error;
|
||||||
|
} catch (SmackException.ConnectionException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Connectivity error while connecting to account " + account.getAddress(), e);
|
||||||
|
return ConnectionResult.server_error;
|
||||||
|
}
|
||||||
|
catch (IOException | XMPPException | SmackException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error connecting to account " + account.getAddress(), e);
|
||||||
|
return ConnectionResult.other_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAuthenticateIfNecessary(MercuryConnection connection)
|
||||||
|
throws InterruptedException, XMPPException, SmackException, IOException {
|
||||||
|
if (!connection.getConnection().isAuthenticated()) {
|
||||||
|
((AbstractXMPPConnection) connection.getConnection()).connect().login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ConnectionResult {
|
||||||
|
success,
|
||||||
|
credential_error,
|
||||||
|
server_error,
|
||||||
|
other_error
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.mercury_im.messenger.xmpp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
import org.mercury_im.messenger.entity.Account;
|
||||||
|
|
||||||
|
public class MercuryConnection {
|
||||||
|
|
||||||
|
private final Account account;
|
||||||
|
|
||||||
|
private XMPPConnection connection;
|
||||||
|
|
||||||
|
public MercuryConnection(Account account) {
|
||||||
|
this.account = account;
|
||||||
|
try {
|
||||||
|
this.connection = new XMPPTCPConnection(account.getAddress(), account.getPassword());
|
||||||
|
} catch (XmppStringprepException e) {
|
||||||
|
throw new AssertionError("Account has invalid address: " + account.getAddress(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XMPPConnection getConnection() {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureAuthenticated() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
package org.mercury_im.messenger.domain.xmpp;
|
package org.mercury_im.messenger.xmpp;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.chat2.Chat;
|
import org.jivesoftware.smack.chat2.Chat;
|
||||||
import org.jivesoftware.smack.chat2.ChatManager;
|
import org.jivesoftware.smack.chat2.ChatManager;
|
||||||
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
||||||
import org.jivesoftware.smackx.avatar.element.MetadataExtension;
|
|
||||||
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
@ -20,9 +19,7 @@ import org.mercury_im.messenger.entity.chat.DirectChat;
|
||||||
import org.mercury_im.messenger.entity.message.IMessage;
|
import org.mercury_im.messenger.entity.message.IMessage;
|
||||||
import org.mercury_im.messenger.entity.message.IMessageMetadata;
|
import org.mercury_im.messenger.entity.message.IMessageMetadata;
|
||||||
import org.mercury_im.messenger.entity.message.Message;
|
import org.mercury_im.messenger.entity.message.Message;
|
||||||
import org.mercury_im.messenger.entity.message.MessageMetadata;
|
import org.mercury_im.messenger.listener.IncomingDirectMessageListener;
|
||||||
import org.mercury_im.messenger.transport.listener.IncomingDirectMessageListener;
|
|
||||||
import org.mercury_im.messenger.transport.xmpp.XmppTcpConnectionMethod;
|
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -56,8 +53,7 @@ public class XmppDirectMessageCenter
|
||||||
this.directChatRepository = repositories.getDirectChatRepository();
|
this.directChatRepository = repositories.getDirectChatRepository();
|
||||||
this.messageRepository = repositories.getMessageRepository();
|
this.messageRepository = repositories.getMessageRepository();
|
||||||
|
|
||||||
XMPPConnection connection = ((XmppTcpConnectionMethod) getMessenger()
|
XMPPConnection connection = getMessenger().getConnection(account).getConnection();
|
||||||
.getConnection(account)).getConnection();
|
|
||||||
|
|
||||||
ChatManager.getInstanceFor(connection).addIncomingListener(this);
|
ChatManager.getInstanceFor(connection).addIncomingListener(this);
|
||||||
}
|
}
|
||||||
|
@ -102,8 +98,8 @@ public class XmppDirectMessageCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ChatManager getChatManager(DirectChat chat) {
|
protected ChatManager getChatManager(DirectChat chat) {
|
||||||
XmppTcpConnectionMethod connectionMethod = (XmppTcpConnectionMethod) getMessenger().getConnection(chat.getAccount());
|
MercuryConnection mercuryConnection = getMessenger().getConnection(chat.getAccount());
|
||||||
return ChatManager.getInstanceFor(connectionMethod.getConnection());
|
return ChatManager.getInstanceFor(mercuryConnection.getConnection());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.mercury_im.messenger.learning_tests.smack;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||||
|
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||||
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
|
import org.jivesoftware.smack.util.stringencoder.java7.Java7Base64Encoder;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Learning Test to study Smacks behavior during connection process.
|
||||||
|
*/
|
||||||
|
public class XmppConnectionLoginBehaviorTest {
|
||||||
|
|
||||||
|
public static final String CREDENTIALS_PROPERTIES = "testcredentials.properties";
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(XmppConnectionLoginBehaviorTest.class.getName());
|
||||||
|
private static Properties testCredentials = null;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void readProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
File propertiesFile = new File(CREDENTIALS_PROPERTIES);
|
||||||
|
if (!propertiesFile.exists() || !propertiesFile.isFile()) {
|
||||||
|
LOGGER.log(Level.WARNING, "Cannot find file domain/testcredentials.properties. Some tests will be skipped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try(FileReader reader = new FileReader(propertiesFile)) {
|
||||||
|
properties.load(reader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error reading properties file testcredentials.properties.", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
testCredentials = properties;
|
||||||
|
|
||||||
|
Base64.setEncoder(Java7Base64Encoder.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connecting to an invalid host causes {@link org.jivesoftware.smack.SmackException.ConnectionException}
|
||||||
|
* to be thrown.
|
||||||
|
*/
|
||||||
|
@Test(expected = SmackException.ConnectionException.class)
|
||||||
|
public void invalidHostConnectionTest() throws IOException, InterruptedException, XMPPException, SmackException {
|
||||||
|
ignoreIfNoCredentials();
|
||||||
|
new XMPPTCPConnection(
|
||||||
|
testCredentials.getProperty("invalidHostUsername"),
|
||||||
|
testCredentials.getProperty("invalidHostPassword"))
|
||||||
|
.connect().login();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connecting with invalid user causes {@link SASLErrorException} to be thrown.
|
||||||
|
*/
|
||||||
|
@Test(expected = SASLErrorException.class)
|
||||||
|
public void invalidUserConnectionTest() throws IOException, InterruptedException, XMPPException, SmackException {
|
||||||
|
ignoreIfNoCredentials();
|
||||||
|
new XMPPTCPConnection(
|
||||||
|
testCredentials.getProperty("invalidUserUsername"),
|
||||||
|
testCredentials.getProperty("invalidUserPassword"))
|
||||||
|
.connect().login();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connecting with invalid password causes {@link SASLErrorException} to be thrown.
|
||||||
|
*/
|
||||||
|
@Test(expected = SASLErrorException.class)
|
||||||
|
public void invalidPasswordConnectionTest() throws IOException, InterruptedException, XMPPException, SmackException {
|
||||||
|
ignoreIfNoCredentials();
|
||||||
|
new XMPPTCPConnection(
|
||||||
|
testCredentials.getProperty("invalidPasswordUsername"),
|
||||||
|
testCredentials.getProperty("invalidPasswordPassword"))
|
||||||
|
.connect().login();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ignoreIfNoCredentials() {
|
||||||
|
assumeTrue("Test ignored as domain/testcredentials.properties file not found.", testCredentials != null);
|
||||||
|
}
|
||||||
|
}
|
11
domain/testcredentials.properties.example
Normal file
11
domain/testcredentials.properties.example
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#Server must not exist
|
||||||
|
invalidHostUsername=invalid@example.com
|
||||||
|
invalidHostPassword=invalid123
|
||||||
|
|
||||||
|
#User must not exist on a server that is reachable
|
||||||
|
invalidUserUsername=
|
||||||
|
invalidPassword=
|
||||||
|
|
||||||
|
#User must exists, but password must be invalid
|
||||||
|
invalidPasswordUsername=
|
||||||
|
invalidPasswordPassword=
|
|
@ -15,8 +15,14 @@ public interface Account {
|
||||||
|
|
||||||
String getAddress();
|
String getAddress();
|
||||||
|
|
||||||
|
void setPassword(String password);
|
||||||
|
|
||||||
|
String getPassword();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
void setAuthentication(AuthMethod authentication);
|
void setAuthentication(AuthMethod authentication);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
AuthMethod getAuthentication();
|
AuthMethod getAuthentication();
|
||||||
|
|
||||||
void setEnabled(boolean enabled);
|
void setEnabled(boolean enabled);
|
||||||
|
|
|
@ -4,6 +4,7 @@ public class IAccount implements Account {
|
||||||
|
|
||||||
protected long id;
|
protected long id;
|
||||||
protected String address;
|
protected String address;
|
||||||
|
protected String password;
|
||||||
protected AuthMethod authentication;
|
protected AuthMethod authentication;
|
||||||
protected boolean enabled;
|
protected boolean enabled;
|
||||||
|
|
||||||
|
@ -28,6 +29,16 @@ public class IAccount implements Account {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthentication(AuthMethod authentication) {
|
public void setAuthentication(AuthMethod authentication) {
|
||||||
this.authentication = authentication;
|
this.authentication = authentication;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
include ':entity',
|
include ':entity',
|
||||||
':data',
|
':data',
|
||||||
':domain',
|
':domain',
|
||||||
':xmpp',
|
|
||||||
':app',
|
':app',
|
||||||
':core-old',
|
':core-old'
|
||||||
':view_entity'
|
|
||||||
|
|
||||||
includeBuild 'libs/Smack'
|
includeBuild 'libs/Smack'
|
||||||
|
|
1
view_entity/.gitignore
vendored
1
view_entity/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,8 +0,0 @@
|
||||||
apply plugin: 'java-library'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':entity')
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = "8"
|
|
||||||
targetCompatibility = "8"
|
|
|
@ -1,134 +0,0 @@
|
||||||
package org.mercury_im.messenger.view.entity;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.contact.SubscriptionMode;
|
|
||||||
import org.mercury_im.messenger.view.entity.definition.InterlocutorViewEntity;
|
|
||||||
|
|
||||||
public class ViewInterlocutor<A> implements InterlocutorViewEntity {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final String address;
|
|
||||||
private final String accountAddress;
|
|
||||||
private final SubscriptionMode subscriptionMode;
|
|
||||||
private final String lastActivity;
|
|
||||||
private final boolean isTyping;
|
|
||||||
private final String status;
|
|
||||||
private final A avatar;
|
|
||||||
|
|
||||||
private ViewInterlocutor(String name,
|
|
||||||
String address,
|
|
||||||
String accountAddress,
|
|
||||||
SubscriptionMode subscriptionMode,
|
|
||||||
String lastActivity,
|
|
||||||
boolean isTyping,
|
|
||||||
String status,
|
|
||||||
A avatar) {
|
|
||||||
this.name = name;
|
|
||||||
this.address = address;
|
|
||||||
this.accountAddress = accountAddress;
|
|
||||||
this.subscriptionMode = subscriptionMode;
|
|
||||||
this.lastActivity = lastActivity;
|
|
||||||
this.isTyping = isTyping;
|
|
||||||
this.status = status;
|
|
||||||
this.avatar = avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountAddress() {
|
|
||||||
return accountAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubscriptionMode getSubscriptionMode() {
|
|
||||||
return subscriptionMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLastActivity() {
|
|
||||||
return lastActivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTyping() {
|
|
||||||
return isTyping;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public A getAvatar() {
|
|
||||||
return avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <A> Builder<A> builder() {
|
|
||||||
return new Builder<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Builder<A> {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String address;
|
|
||||||
private String accountAddress;
|
|
||||||
private SubscriptionMode subscriptionMode;
|
|
||||||
private String lastActivity;
|
|
||||||
private boolean isTyping;
|
|
||||||
private String status;
|
|
||||||
private A avatar;
|
|
||||||
|
|
||||||
public Builder setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setAddress(String address) {
|
|
||||||
this.address = address;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setAccountAddress(String accountAddress) {
|
|
||||||
this.accountAddress = accountAddress;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setSubscriptionMode(SubscriptionMode subscriptionMode) {
|
|
||||||
this.subscriptionMode = subscriptionMode;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setLastActivity(String lastActivity) {
|
|
||||||
this.lastActivity = lastActivity;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setIsTyping(boolean isTyping) {
|
|
||||||
this.isTyping = isTyping;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setStatus(String status) {
|
|
||||||
this.status = status;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setAvatar(A avatar) {
|
|
||||||
this.avatar = avatar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ViewInterlocutor<A> build() {
|
|
||||||
return new ViewInterlocutor<>(name, address, accountAddress, subscriptionMode,
|
|
||||||
lastActivity, isTyping, status, avatar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package org.mercury_im.messenger.view.entity.definition;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.contact.SubscriptionMode;
|
|
||||||
|
|
||||||
public interface InterlocutorViewEntity {
|
|
||||||
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
String getAddress();
|
|
||||||
|
|
||||||
String getAccountAddress();
|
|
||||||
|
|
||||||
SubscriptionMode getSubscriptionMode();
|
|
||||||
|
|
||||||
String getLastActivity();
|
|
||||||
|
|
||||||
boolean isTyping();
|
|
||||||
|
|
||||||
String getStatus();
|
|
||||||
}
|
|
1
xmpp/.gitignore
vendored
1
xmpp/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,25 +0,0 @@
|
||||||
apply plugin: 'java-library'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(":entity")
|
|
||||||
implementation project(':domain')
|
|
||||||
|
|
||||||
// RxJava2
|
|
||||||
implementation "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
|
||||||
|
|
||||||
// Dagger 2 for dependency injection
|
|
||||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
|
||||||
|
|
||||||
// Smack
|
|
||||||
// Not all of those are needed, but it may be a good idea to define those versions explicitly
|
|
||||||
api "org.igniterealtime.smack:smack-core:$smackCoreVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-experimental:$smackExperimentalVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-extensions:$smackExtensionsVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-im:$smackImVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion"
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = "8"
|
|
||||||
targetCompatibility = "8"
|
|
|
@ -1,30 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.xmpp;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.transport.connection.ConnectionFactory;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
public abstract class XmppConnectionFactory<CF extends ConnectionConfiguration>
|
|
||||||
implements ConnectionFactory<XmppTcpConnectionMethod> {
|
|
||||||
|
|
||||||
protected final Messenger messenger;
|
|
||||||
|
|
||||||
public XmppConnectionFactory(Messenger messenger) {
|
|
||||||
this.messenger = messenger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Messenger getMessenger() {
|
|
||||||
return messenger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XmppTcpConnectionMethod provideConnection(Account account) {
|
|
||||||
return new XmppTcpConnectionMethod(account, getMessenger(), createXmppConnection(getConfiguration(account)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract CF getConfiguration(Account account);
|
|
||||||
|
|
||||||
protected abstract XMPPConnection createXmppConnection(CF connectionConfiguration);
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.xmpp;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
|
||||||
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
|
||||||
|
|
||||||
public class XmppTcpConnectionFactory extends XmppConnectionFactory<XMPPTCPConnectionConfiguration> {
|
|
||||||
|
|
||||||
public XmppTcpConnectionFactory(Messenger messenger) {
|
|
||||||
super(messenger);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected XMPPTCPConnectionConfiguration getConfiguration(Account account) {
|
|
||||||
XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
|
|
||||||
configBuilder.setConnectTimeout(20 * 1000);
|
|
||||||
|
|
||||||
if (account.getAuthentication() instanceof PasswordAuthentication) {
|
|
||||||
PasswordAuthentication authPassword = (PasswordAuthentication) account.getAuthentication();
|
|
||||||
EntityBareJid accountAddress = JidCreate.entityBareFromOrThrowUnchecked(account.getAddress());
|
|
||||||
configBuilder.setXmppAddressAndPassword(accountAddress, authPassword.getPassword());
|
|
||||||
}
|
|
||||||
|
|
||||||
return configBuilder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected XMPPConnection createXmppConnection(XMPPTCPConnectionConfiguration configuration) {
|
|
||||||
return new XMPPTCPConnection(configuration);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package org.mercury_im.messenger.transport.xmpp;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.mercury_im.messenger.Messenger;
|
|
||||||
import org.mercury_im.messenger.transport.ConnectionType;
|
|
||||||
import org.mercury_im.messenger.transport.connection.AbstractConnectionMethod;
|
|
||||||
import org.mercury_im.messenger.entity.Account;
|
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
|
|
||||||
public class XmppTcpConnectionMethod extends AbstractConnectionMethod {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(XmppTcpConnectionMethod.class.getName());
|
|
||||||
|
|
||||||
private XMPPConnection connection;
|
|
||||||
|
|
||||||
public XmppTcpConnectionMethod(Account account, Messenger messenger, XMPPConnection connection) {
|
|
||||||
super(account, messenger);
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Completable connect() {
|
|
||||||
if (connection.isConnected()) {
|
|
||||||
return Completable.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Completable.fromAction(
|
|
||||||
() -> {
|
|
||||||
AbstractXMPPConnection con = (AbstractXMPPConnection) connection;
|
|
||||||
try {
|
|
||||||
con.connect();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Exception while connecting to XMPP account " + account.getId(), e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
con.login();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Exception while logging into XMPP account " + account.getId(), e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public XMPPConnection getConnection() {
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConnectionType getConnectionType() {
|
|
||||||
return ConnectionType.SMACK_TCP;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue