mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2025-04-21 23:54:47 +02:00
Too lazy to comment. Simplifications and rewrites of login
This commit is contained in:
parent
ccddad2e31
commit
3569462a78
43 changed files with 451 additions and 708 deletions
|
@ -4,7 +4,6 @@ import org.mercury_im.messenger.MercuryImApplication;
|
|||
import org.mercury_im.messenger.data.di.RepositoryModule;
|
||||
import org.mercury_im.messenger.di.module.AndroidPersistenceModule;
|
||||
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.ui.MainActivity;
|
||||
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
||||
|
@ -30,8 +29,7 @@ import dagger.Component;
|
|||
modules = {
|
||||
AppModule.class,
|
||||
AndroidPersistenceModule.class,
|
||||
RepositoryModule.class,
|
||||
MercuryModule.class
|
||||
RepositoryModule.class
|
||||
})
|
||||
public interface AppComponent {
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.mercury_im.messenger.ui.login;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
|
@ -14,7 +13,6 @@ import androidx.lifecycle.ViewModelProvider;
|
|||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.account.error.PasswordError;
|
||||
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);
|
||||
observeViewModel();
|
||||
setupTextInputFields();
|
||||
mSignInView.setOnClickListener(view -> viewModel.loginDetailsEntered());
|
||||
mSignInView.setOnClickListener(view -> viewModel.login());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
observeUsernameError();
|
||||
observePasswordError();
|
||||
finishOnceLoginWasSuccessful();
|
||||
displayCredentials(viewModel.getAccount());
|
||||
enableLoginButtonOncePossible();
|
||||
}
|
||||
|
||||
private void observeUsernameError() {
|
||||
viewModel.getUsernameError().observe(this, usernameError -> {
|
||||
Optional<String> errorMessage = getUsernameError(usernameError);
|
||||
if (errorMessage.isPresent()) {
|
||||
mUsernameView.setError(errorMessage.getItem());
|
||||
}
|
||||
mUsernameView.setError(usernameError.isError() ? usernameError.getErrorMessage() : null);
|
||||
});
|
||||
}
|
||||
|
||||
private void observePasswordError() {
|
||||
viewModel.getPasswordError().observe(this, passwordError -> {
|
||||
Optional<String> errorMessage = getPasswordError(passwordError);
|
||||
if (errorMessage.isPresent()) {
|
||||
mPasswordView.setError(errorMessage.getItem());
|
||||
}
|
||||
mPasswordView.setError(passwordError.isError() ? passwordError.getErrorMessage() : null);
|
||||
});
|
||||
}
|
||||
|
||||
private void finishOnceLoginWasSuccessful() {
|
||||
viewModel.getSigninSuccessful().observe(this, successful -> {
|
||||
viewModel.isLoginSuccessful().observe(this, successful -> {
|
||||
if (Boolean.TRUE.equals(successful)) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableLoginButtonOncePossible() {
|
||||
viewModel.isLoginEnabled().observe(this, isEnabled -> {
|
||||
mSignInView.setEnabled(isEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<String> getUsernameError(UsernameError usernameError) {
|
||||
switch (usernameError) {
|
||||
case none:
|
||||
|
@ -130,8 +128,8 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
|||
mUsernameView.setText(accountEvent.getAddress());
|
||||
}
|
||||
|
||||
if (accountEvent.getAuthentication() != null) {
|
||||
mPasswordView.setText(accountEvent.getAuthentication().getPassword());
|
||||
if (accountEvent.getPassword() != null) {
|
||||
mPasswordView.setText(accountEvent.getPassword());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -143,16 +141,14 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
|||
mUsernameView.addTextChangedListener(new TextChangedListener() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
viewModel.onUsernameInputChanged(charSequence.toString());
|
||||
Log.d(Messenger.TAG, "onTextChanged");
|
||||
viewModel.setUsername(charSequence.toString());
|
||||
}
|
||||
});
|
||||
|
||||
mPasswordView.addTextChangedListener(new TextChangedListener() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
viewModel.onPasswordInputChanged(charSequence.toString());
|
||||
Log.d(Messenger.TAG, "onTextChanged");
|
||||
viewModel.setPassword(charSequence.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -169,7 +165,7 @@ public class LoginActivity extends AppCompatActivity implements TextView.OnEdito
|
|||
|
||||
case R.id.password:
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
|
||||
viewModel.loginDetailsEntered();
|
||||
viewModel.login();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,140 +1,138 @@
|
|||
package org.mercury_im.messenger.ui.login;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.app.Application;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
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.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.account.error.PasswordError;
|
||||
import org.mercury_im.messenger.account.error.UsernameError;
|
||||
import org.mercury_im.messenger.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
import org.mercury_im.messenger.entity.IAccount;
|
||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class LoginViewModel extends ViewModel {
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Inject
|
||||
AccountRepository accountRepository;
|
||||
public class LoginViewModel extends AndroidViewModel {
|
||||
|
||||
// @Inject
|
||||
// ConnectionCenter connectionCenter;
|
||||
private MutableLiveData<Error> usernameError = new MutableLiveData<>(new Error());
|
||||
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 MutableLiveData<UsernameError> usernameError = new MutableLiveData<>(UsernameError.none);
|
||||
private MutableLiveData<PasswordError> passwordError = new MutableLiveData<>(PasswordError.none);
|
||||
@Inject
|
||||
Messenger messenger;
|
||||
|
||||
private MutableLiveData<Account> account = new MutableLiveData<>();
|
||||
|
||||
private MutableLiveData<Boolean> signinSuccessful = new MutableLiveData<>();
|
||||
|
||||
public LoginViewModel() {
|
||||
super();
|
||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
init(new IAccount());
|
||||
public LoginViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
((MercuryImApplication) application).getAppComponent().inject(this);
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getSigninSuccessful() {
|
||||
return signinSuccessful;
|
||||
public void setUsername(String username) {
|
||||
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) {
|
||||
this.username = input;
|
||||
public void setPassword(String password) {
|
||||
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) {
|
||||
this.password = input;
|
||||
}
|
||||
|
||||
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;
|
||||
private void updateLoginEnabled() {
|
||||
loginEnabled.setValue(username != null && !(password == null || password.isEmpty()));
|
||||
}
|
||||
|
||||
public void login() {
|
||||
Account account = getAccount().getValue();
|
||||
if (account != null && account.getAddress() != null
|
||||
&& !TextUtils.isEmpty(account.getAuthentication().getPassword())) {
|
||||
accountRepository.upsertAccount(account);
|
||||
}
|
||||
Account account = new IAccount();
|
||||
account.setAddress(username.asUnescapedString());
|
||||
account.setPassword(password);
|
||||
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() {
|
||||
boolean loginIntact = true;
|
||||
if (username.isEmpty()) {
|
||||
usernameError.postValue(UsernameError.emptyUsername);
|
||||
loginIntact = false;
|
||||
public LiveData<Error> getPasswordError() {
|
||||
return passwordError;
|
||||
}
|
||||
|
||||
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);
|
||||
if (bareJid == null) {
|
||||
usernameError.postValue(UsernameError.invalidUsername);
|
||||
loginIntact = false;
|
||||
public Error(String errorMessage) {
|
||||
this.message = errorMessage;
|
||||
}
|
||||
|
||||
if (!isPasswordValid(password)) {
|
||||
passwordError.postValue(PasswordError.invalidPassword);
|
||||
loginIntact = false;
|
||||
public boolean isError() {
|
||||
return message != null;
|
||||
}
|
||||
|
||||
if (loginIntact) {
|
||||
Account account = new IAccount();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
public String getErrorMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ sourceSets {
|
|||
|
||||
dependencies {
|
||||
|
||||
api project(':entity') // Entities
|
||||
api project(':entity')
|
||||
|
||||
// Smack
|
||||
// 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
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
compile project(path: ':data')
|
||||
}
|
||||
|
||||
sourceCompatibility = "8"
|
||||
|
|
|
@ -4,8 +4,8 @@ import org.jivesoftware.smack.util.PacketParserUtils;
|
|||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||
import org.mercury_im.messenger.xmpp.model.EntityCapsModel;
|
||||
import org.mercury_im.messenger.xmpp.repository.EntityCapsRepository;
|
||||
import org.mercury_im.messenger.data.model.EntityCapsModel;
|
||||
import org.mercury_im.messenger.data.repository.XmppEntityCapsRepository;
|
||||
|
||||
|
||||
import java.io.StringReader;
|
||||
|
@ -23,13 +23,13 @@ public class EntityCapsStore implements EntityCapsPersistentCache {
|
|||
|
||||
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 CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
@Inject
|
||||
public EntityCapsStore(EntityCapsRepository entityCapsRepository) {
|
||||
public EntityCapsStore(XmppEntityCapsRepository entityCapsRepository) {
|
||||
this.entityCapsRepository = entityCapsRepository;
|
||||
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.PeerRepository;
|
||||
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.XmppAccountRepository;
|
||||
import org.mercury_im.messenger.data.repository.XmppDirectChatRepository;
|
||||
|
@ -89,11 +89,11 @@ public class RepositoryModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
static EntityCapsRepository provideCapsRepository(
|
||||
static XmppEntityCapsRepository provideCapsRepository(
|
||||
ReactiveEntityStore<Persistable> data,
|
||||
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler ioScheduler,
|
||||
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler uiScheduler) {
|
||||
return new EntityCapsRepository(data, ioScheduler, uiScheduler);
|
||||
return new XmppEntityCapsRepository(data, ioScheduler, uiScheduler);
|
||||
}
|
||||
|
||||
@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.entity.Account;
|
||||
import org.mercury_im.messenger.entity.IAccount;
|
||||
import org.mercury_im.messenger.entity.PasswordAuthentication;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -27,7 +26,7 @@ public class AccountMapping extends AbstractMapping<Account, AccountModel> {
|
|||
@Override
|
||||
public AccountModel mapToModel(Account entity, AccountModel model) {
|
||||
model.setAddress(entity.getAddress());
|
||||
model.setPassword(entity.getAuthentication().getPassword());
|
||||
model.setPassword(entity.getPassword());
|
||||
model.setEnabled(entity.isEnabled());
|
||||
|
||||
return model;
|
||||
|
@ -37,7 +36,7 @@ public class AccountMapping extends AbstractMapping<Account, AccountModel> {
|
|||
public Account mapToEntity(AccountModel model, Account entity) {
|
||||
entity.setId(model.getId());
|
||||
entity.setAddress(model.getAddress());
|
||||
entity.setAuthentication(new PasswordAuthentication(model.getPassword()));
|
||||
entity.setPassword(model.getPassword());
|
||||
entity.setEnabled(model.isEnabled());
|
||||
|
||||
return entity;
|
||||
|
|
|
@ -9,10 +9,10 @@ import io.reactivex.Scheduler;
|
|||
import io.requery.Persistable;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
public class EntityCapsRepository extends RequeryRepository {
|
||||
public class XmppEntityCapsRepository extends RequeryRepository {
|
||||
|
||||
@Inject
|
||||
public EntityCapsRepository(
|
||||
public XmppEntityCapsRepository(
|
||||
ReactiveEntityStore<Persistable> data,
|
||||
@Named(value = ThreadUtils.SCHEDULER_IO) Scheduler subscriberScheduler,
|
||||
@Named(value = ThreadUtils.SCHEDULER_UI) Scheduler observerScheduler) {
|
|
@ -74,8 +74,8 @@ public class XmppGroupChatRepository
|
|||
@Override
|
||||
public Single<GroupChat> getOrCreateGroupChat(Account account, String roomAddress) {
|
||||
return getGroupChatByRoomAddress(account, roomAddress)
|
||||
.switchIfEmpty(Single
|
||||
.just((GroupChat) new IGroupChat() {
|
||||
.switchIfEmpty(
|
||||
Single.just((GroupChat) new IGroupChat() {
|
||||
{
|
||||
setAccount(account);
|
||||
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.IAccount;
|
||||
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;
|
||||
|
||||
|
@ -28,8 +13,6 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||
import io.requery.Persistable;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
|
||||
public class AccountRepositoryTest {
|
||||
|
||||
@Inject
|
||||
|
|
1
domain/.gitignore
vendored
1
domain/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/build
|
||||
testcredentials.properties
|
||||
|
|
|
@ -4,6 +4,16 @@ dependencies {
|
|||
|
||||
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
|
||||
implementation "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.message.Message;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ package org.mercury_im.messenger;
|
|||
|
||||
import org.mercury_im.messenger.data.repository.Repositories;
|
||||
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.Map;
|
||||
|
@ -13,28 +14,23 @@ public class Messenger {
|
|||
|
||||
public static final String TAG = "MercuryIM";
|
||||
|
||||
private final Map<Long, ConnectionMethod> connections = new HashMap<>();
|
||||
|
||||
private final Repositories repositories;
|
||||
private final Map<Long, MercuryConnection> connections = new HashMap<>();
|
||||
private Repositories repositories;
|
||||
|
||||
@Inject
|
||||
public Messenger(Repositories repositories) {
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
public void addConnection(ConnectionMethod connection) {
|
||||
public void addConnection(MercuryConnection connection) {
|
||||
connections.put(connection.getAccount().getId(), connection);
|
||||
}
|
||||
|
||||
public ConnectionMethod getConnection(Account account) {
|
||||
public MercuryConnection getConnection(Account account) {
|
||||
return connections.get(account.getId());
|
||||
}
|
||||
|
||||
public void appInUse() {
|
||||
|
||||
}
|
||||
|
||||
public void appInBackground() {
|
||||
|
||||
public AddAccount addAccount() {
|
||||
return new AddAccount(repositories.getAccountRepository(), this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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.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.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.chat2.Chat;
|
||||
import org.jivesoftware.smack.chat2.ChatManager;
|
||||
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
||||
import org.jivesoftware.smackx.avatar.element.MetadataExtension;
|
||||
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
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.IMessageMetadata;
|
||||
import org.mercury_im.messenger.entity.message.Message;
|
||||
import org.mercury_im.messenger.entity.message.MessageMetadata;
|
||||
import org.mercury_im.messenger.transport.listener.IncomingDirectMessageListener;
|
||||
import org.mercury_im.messenger.transport.xmpp.XmppTcpConnectionMethod;
|
||||
import org.mercury_im.messenger.listener.IncomingDirectMessageListener;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
@ -56,8 +53,7 @@ public class XmppDirectMessageCenter
|
|||
this.directChatRepository = repositories.getDirectChatRepository();
|
||||
this.messageRepository = repositories.getMessageRepository();
|
||||
|
||||
XMPPConnection connection = ((XmppTcpConnectionMethod) getMessenger()
|
||||
.getConnection(account)).getConnection();
|
||||
XMPPConnection connection = getMessenger().getConnection(account).getConnection();
|
||||
|
||||
ChatManager.getInstanceFor(connection).addIncomingListener(this);
|
||||
}
|
||||
|
@ -102,8 +98,8 @@ public class XmppDirectMessageCenter
|
|||
}
|
||||
|
||||
protected ChatManager getChatManager(DirectChat chat) {
|
||||
XmppTcpConnectionMethod connectionMethod = (XmppTcpConnectionMethod) getMessenger().getConnection(chat.getAccount());
|
||||
return ChatManager.getInstanceFor(connectionMethod.getConnection());
|
||||
MercuryConnection mercuryConnection = getMessenger().getConnection(chat.getAccount());
|
||||
return ChatManager.getInstanceFor(mercuryConnection.getConnection());
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
void setPassword(String password);
|
||||
|
||||
String getPassword();
|
||||
|
||||
@Deprecated
|
||||
void setAuthentication(AuthMethod authentication);
|
||||
|
||||
@Deprecated
|
||||
AuthMethod getAuthentication();
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
|
|
@ -4,6 +4,7 @@ public class IAccount implements Account {
|
|||
|
||||
protected long id;
|
||||
protected String address;
|
||||
protected String password;
|
||||
protected AuthMethod authentication;
|
||||
protected boolean enabled;
|
||||
|
||||
|
@ -28,6 +29,16 @@ public class IAccount implements Account {
|
|||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthentication(AuthMethod authentication) {
|
||||
this.authentication = authentication;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
include ':entity',
|
||||
':data',
|
||||
':domain',
|
||||
':xmpp',
|
||||
':app',
|
||||
':core-old',
|
||||
':view_entity'
|
||||
':core-old'
|
||||
|
||||
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…
Add table
Reference in a new issue