package org.mercury_im.messenger.core.viewmodel.accounts; import org.jivesoftware.smack.util.StringUtils; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.account.error.PasswordError; import org.mercury_im.messenger.core.account.error.UsernameError; import org.mercury_im.messenger.core.data.repository.AccountRepository; import org.mercury_im.messenger.core.util.Optional; import org.mercury_im.messenger.core.viewmodel.MercuryViewModel; import org.mercury_im.messenger.core.xmpp.MercuryConnection; import org.mercury_im.messenger.core.xmpp.MercuryConnectionManager; import org.mercury_im.messenger.core.xmpp.exception.InvalidCredentialsException; import org.mercury_im.messenger.core.xmpp.exception.ServerUnreachableException; import org.mercury_im.messenger.entity.Account; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; import io.reactivex.Observable; import io.reactivex.subjects.BehaviorSubject; public class LoginViewModel implements MercuryViewModel { private static final Logger LOGGER = Logger.getLogger(LoginViewModel.class.getName()); private final MercuryConnectionManager connectionManager; private final AccountRepository accountRepository; private final SchedulersFacade schedulers; private BehaviorSubject> loginUsernameError; private BehaviorSubject> loginPasswordError; private BehaviorSubject isLoginPossible; private BehaviorSubject isLoginSuccessful; private EntityBareJid loginUsernameValue; private String loginPasswordValue; @Inject public LoginViewModel(MercuryConnectionManager connectionManager, AccountRepository accountRepository, SchedulersFacade schedulers) { this.connectionManager = connectionManager; this.accountRepository = accountRepository; this.schedulers = schedulers; reset(); } public void reset() { loginUsernameError = BehaviorSubject.createDefault(new Optional<>()); loginPasswordError = BehaviorSubject.createDefault(new Optional<>()); isLoginPossible = BehaviorSubject.createDefault(false); isLoginSuccessful = BehaviorSubject.createDefault(false); loginUsernameValue = null; loginPasswordValue = null; } public Observable> getLoginUsernameError() { return loginUsernameError; } public Observable> getLoginPasswordError() { return loginPasswordError; } public Observable isLoginPossible() { return isLoginPossible; } public Observable isLoginSuccessful() { return isLoginSuccessful; } public synchronized void onLoginUsernameChanged(String username) { if (StringUtils.isNullOrEmpty(username)) { loginUsernameValue = null; loginUsernameError.onNext(new Optional<>(UsernameError.emptyUsername)); } else { try { loginUsernameValue = JidCreate.entityBareFrom(username); loginUsernameError.onNext(new Optional<>()); } catch (XmppStringprepException e) { loginUsernameValue = null; loginUsernameError.onNext(new Optional<>(UsernameError.invalidUsername)); } } updateLoginPossible(); } public synchronized void onLoginPasswordChanged(String password) { if (StringUtils.isNullOrEmpty(password)) { loginPasswordValue = null; loginPasswordError.onNext(new Optional<>(PasswordError.emptyPassword)); } else { loginPasswordValue = password; loginPasswordError.onNext(new Optional<>()); } updateLoginPossible(); } private synchronized void updateLoginPossible() { isLoginPossible.onNext(loginUsernameValue != null && loginPasswordValue != null); } public synchronized void login() { if (!isLoginPossible.getValue()) { // Prevent race condition where account would be logged in twice return; } isLoginPossible.onNext(false); Account account = createAccountEntity(); MercuryConnection connection = connectionManager.createConnection(account); addDisposable(connection.connect() .andThen(connection.login()) .andThen(connectionManager.registerConnection(connection)) .andThen(accountRepository.insertAccount(account)) .subscribeOn(schedulers.getNewThread()) .observeOn(schedulers.getUiScheduler()) .subscribe( this::onLoginSuccessful, this::onLoginFailed )); } private Account createAccountEntity() { Account account = new Account(); account.setAddress(loginUsernameValue.asEntityBareJidString()); account.setPassword(loginPasswordValue); account.setEnabled(true); return account; } private void onLoginSuccessful(Account account) { LOGGER.log(Level.FINER, "Successfully added new account " + account); isLoginSuccessful.onNext(true); } private void onLoginFailed(Throwable error) { isLoginPossible.onNext(true); if (error instanceof InvalidCredentialsException) { loginPasswordError.onNext(new Optional<>(PasswordError.incorrectPassword)); } else if (error instanceof ServerUnreachableException) { loginUsernameError.onNext(new Optional<>(UsernameError.unreachableServer)); } } }