diff --git a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java index 5508b4c..9189b90 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java +++ b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java @@ -4,11 +4,12 @@ import org.mercury_im.messenger.android.MercuryImApplication; import org.mercury_im.messenger.android.di.module.AndroidDatabaseModule; import org.mercury_im.messenger.android.di.module.AndroidSchedulersModule; import org.mercury_im.messenger.android.ui.account.AndroidAccountDetailsViewModel; -import org.mercury_im.messenger.android.ui.ox.AndroidOxSecretKeyBackupRestoreViewModel; +import org.mercury_im.messenger.android.ui.roster.contacts.AndroidContactListViewModel; import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule; import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule; import org.mercury_im.messenger.core.di.module.XmppTcpConnectionFactoryModule; import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; +import org.mercury_im.messenger.core.viewmodel.chat.ChatViewModel; import org.mercury_im.messenger.data.di.RepositoryModule; import org.mercury_im.messenger.android.di.module.AppModule; import org.mercury_im.messenger.core.di.module.ViewModelModule; @@ -22,7 +23,6 @@ import org.mercury_im.messenger.android.ui.chat.ChatInputFragment; import org.mercury_im.messenger.android.ui.chat.ChatInputViewModel; import org.mercury_im.messenger.android.ui.chat.AndroidChatViewModel; import org.mercury_im.messenger.android.ui.chatlist.ChatListViewModel; -import org.mercury_im.messenger.android.ui.roster.contacts.ContactListViewModel; import org.mercury_im.messenger.android.ui.roster.contacts.detail.ContactDetailActivity; import org.mercury_im.messenger.android.ui.roster.contacts.detail.ContactDetailViewModel; import org.mercury_im.messenger.core.viewmodel.accounts.AccountsViewModel; @@ -69,10 +69,12 @@ public interface AppComponent { // ViewModels - void inject(ContactListViewModel contactListViewModel); + void inject(AndroidContactListViewModel contactListViewModel); void inject(AndroidChatViewModel androidChatViewModel); + void inject(ChatViewModel chatViewModel); + void inject(ChatInputViewModel chatInputViewModel); void inject(AndroidLoginViewModel androidLoginViewModel); diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java b/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java index 70c6733..148cb2d 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java @@ -7,6 +7,7 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentTransaction; @@ -40,6 +41,8 @@ public class MainActivity extends AppCompatActivity @BindView(R.id.bottom_navigation) BottomNavigationView bottomNavigationView; + private SearchView searchView; + private ChatListFragment chatListFragment = new ChatListFragment(); private RosterFragment rosterFragment = new RosterFragment(); private AccountsFragment accountsFragment = new AccountsFragment(); @@ -63,6 +66,10 @@ public class MainActivity extends AppCompatActivity public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); + + final MenuItem searchItem = menu.findItem(R.id.action_search); + searchView = (SearchView) searchItem.getActionView(); + return true; } @@ -91,6 +98,7 @@ public class MainActivity extends AppCompatActivity case R.id.entry_contacts: transaction.replace(R.id.fragment, rosterFragment).commit(); + searchView.setOnQueryTextListener(rosterFragment); return true; case R.id.entry_accounts: @@ -102,7 +110,8 @@ public class MainActivity extends AppCompatActivity @Override public void onAccountListItemClick(Account item) { - getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new AccountDetailsFragment(item.getId())).commit(); + getSupportFragmentManager().beginTransaction().addToBackStack("Test") + .replace(R.id.fragment, new AccountDetailsFragment(item.getId())).commit(); } @Override diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java index 227132c..2cb6d0e 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java @@ -16,9 +16,11 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.card.MaterialCardView; + import org.mercury_im.messenger.R; import org.mercury_im.messenger.android.MercuryImApplication; -import org.mercury_im.messenger.android.util.OpenPgpFingerprintColorizer; +import org.mercury_im.messenger.android.util.OpenPgpV4FingerprintFormatter; import org.mercury_im.messenger.core.viewmodel.accounts.AccountFingerprintViewItem; import org.pgpainless.key.OpenPgpV4Fingerprint; @@ -47,7 +49,10 @@ public class AccountDetailsFragment extends Fragment { TextView localFingerprint; @BindView(R.id.fingerprint_list) - RecyclerView externalFingerprintList; + RecyclerView externalFingerprintRecyclerView; + + @BindView(R.id.other_fingerprints_card) + MaterialCardView otherFingerprintsLayout; private final UUID accountId; private ToggleableFingerprintsAdapter adapter; @@ -70,33 +75,30 @@ public class AccountDetailsFragment extends Fragment { .get(AndroidAccountDetailsViewModel.class); this.adapter = new ToggleableFingerprintsAdapter(viewModel); - observe(); } private void observe() { - viewModel.getLocalFingerprint().observe(this, - f -> localFingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(f))); + viewModel.getLocalFingerprint().observe(getViewLifecycleOwner(), + f -> localFingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(f))); - viewModel.getRemoteFingerprints().observe(this, adapter::setItems); - - //viewModel.getJid().observe(this, jid::setText); - - /* - localFingerprintShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, viewModel.getLocalFingerprint().getValue()); - sendIntent.setType("text/plain"); - - Intent shareIntent = Intent.createChooser(sendIntent, null); - startActivity(shareIntent); - - } + viewModel.getRemoteFingerprints().observe(getViewLifecycleOwner(), items -> { + otherFingerprintsLayout.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE); + adapter.setItems(items); }); - */ + viewModel.getJid().observe(getViewLifecycleOwner(), accountJid -> jid.setText(accountJid.toString())); + + localFingerprintShareButton.setOnClickListener(v -> { + OpenPgpV4Fingerprint fingerprint = viewModel.getLocalFingerprint().getValue(); + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, "openpgp4fpr:" + fingerprint); + sendIntent.setType("text/plain"); + + Intent shareIntent = Intent.createChooser(sendIntent, "Share OpenPGP Fingerprint"); + startActivity(shareIntent); + + }); } @Nullable @@ -105,7 +107,9 @@ public class AccountDetailsFragment extends Fragment { View view = inflater.inflate(R.layout.fragment_account_details, container, false); ButterKnife.bind(this, view); - externalFingerprintList.setAdapter(adapter); + externalFingerprintRecyclerView.setAdapter(adapter); + + observe(); return view; } @@ -142,12 +146,13 @@ public class AccountDetailsFragment extends Fragment { final OpenPgpV4Fingerprint fingerprint = f.getFingerprint(); - holder.fingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(fingerprint)); + holder.fingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(fingerprint)); holder.fingerprintTimestamp.setText(dateFormat.format(f.getAnnouncementDate())); holder.trustSwitch.setChecked(f.isTrusted()); holder.trustSwitch.setOnCheckedChangeListener( (buttonView, isChecked) -> viewModel.markFingerprintTrusted(fingerprint, isChecked)); + holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE); } } @@ -161,12 +166,14 @@ public class AccountDetailsFragment extends Fragment { private final Switch trustSwitch; private final TextView fingerprintTimestamp; private final TextView fingerprint; + private final View divider; public ViewHolder(@NonNull View itemView) { super(itemView); this.fingerprint = itemView.findViewById(R.id.fingerprint); this.trustSwitch = itemView.findViewById(R.id.fingerprint_toggle); this.fingerprintTimestamp = itemView.findViewById(R.id.fingerprint_timestamp); + this.divider = itemView.findViewById(R.id.divider); } } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsRecyclerViewAdapter.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsRecyclerViewAdapter.java index 7c0aced..90d8053 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsRecyclerViewAdapter.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsRecyclerViewAdapter.java @@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView; import org.jetbrains.annotations.NotNull; import org.mercury_im.messenger.R; -import org.mercury_im.messenger.android.util.OpenPgpFingerprintColorizer; import org.mercury_im.messenger.core.viewmodel.accounts.AccountViewItem; import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.android.ui.avatar.AvatarDrawable; diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java index 0a5787a..e773815 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java @@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; import org.mercury_im.messenger.android.MercuryImApplication; import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel; import org.mercury_im.messenger.core.SchedulersFacade; @@ -19,13 +20,18 @@ import org.mercury_im.messenger.core.viewmodel.accounts.AccountFingerprintViewIt import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.inject.Inject; public class AndroidAccountDetailsViewModel extends AndroidViewModel implements MercuryAndroidViewModel { + private static final Logger LOGGER = Logger.getLogger(AndroidAccountDetailsViewModel.class.getName()); + @Inject SchedulersFacade schedulers; @@ -33,27 +39,33 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements AccountDetailsViewModel commonViewModel; private final UUID accountId; - private MutableLiveData localFingerprint = new MutableLiveData<>(); + private MutableLiveData localFingerprint = new MutableLiveData<>(new OpenPgpV4Fingerprint("09858F60046289311743B90F3152226EB43287C5")); private MutableLiveData> remoteFingerprints = new MutableLiveData<>(new ArrayList<>()); - private MutableLiveData jid; + private MutableLiveData jid = new MutableLiveData<>(JidCreate.entityBareFromOrThrowUnchecked("placeholder@place.holder")); public AndroidAccountDetailsViewModel(@NonNull Application application, UUID accountId) { super(application); this.accountId = accountId; + LOGGER.log(Level.INFO, "Creating AndroidAccountDetailsViewModel"); ((MercuryImApplication) application).getAppComponent().inject(this); addDisposable(getCommonViewModel().observeLocalFingerprint(accountId) .compose(schedulers.executeUiSafeObservable()) .filter(Optional::isPresent) .map(Optional::getItem) - .subscribe(localFingerprint::setValue)); + .subscribe(localFingerprint::postValue)); addDisposable(getCommonViewModel().observeRemoteFingerprints(accountId) .compose(schedulers.executeUiSafeObservable()) - .subscribe(list -> remoteFingerprints.setValue(list))); + .subscribe(list -> { + LOGGER.log(Level.INFO, "Set remote fingerprints to list: " + Arrays.toString(list.toArray())); + remoteFingerprints.postValue(list); + }, + e -> LOGGER.log(Level.SEVERE, "Error observing remote fingerprints.", e), + () -> LOGGER.log(Level.INFO, "observing remote fingerprint onComplete."))); - //addDisposable(getCommonViewModel().getJid(accountId).subscribe(jid::setValue)); + addDisposable(getCommonViewModel().getJid(accountId).subscribe(jid::postValue)); } @Override diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/chat/AndroidChatViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/chat/AndroidChatViewModel.java index 8df6167..0ad314b 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/chat/AndroidChatViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/chat/AndroidChatViewModel.java @@ -12,6 +12,7 @@ import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.DirectChatRepository; import org.mercury_im.messenger.core.data.repository.MessageRepository; import org.mercury_im.messenger.core.data.repository.PeerRepository; +import org.mercury_im.messenger.core.util.Optional; import org.mercury_im.messenger.core.viewmodel.chat.ChatViewModel; import org.mercury_im.messenger.data.repository.RxMessageRepository; import org.mercury_im.messenger.entity.chat.DirectChat; @@ -29,6 +30,7 @@ import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; public class AndroidChatViewModel extends ViewModel implements MercuryAndroidViewModel { @@ -36,7 +38,7 @@ public class AndroidChatViewModel extends ViewModel implements MercuryAndroidVie private final CompositeDisposable disposable = new CompositeDisposable(); private static final Logger LOGGER = Logger.getLogger(AndroidChatViewModel.class.getName()); - //@Inject + @Inject ChatViewModel commonViewModel; @Inject @@ -74,30 +76,22 @@ public class AndroidChatViewModel extends ViewModel implements MercuryAndroidVie public void init(DirectChat chat) { this.chat.setValue(chat); this.contact.setValue(chat.getPeer()); + this.commonViewModel.init(chat); // Subscribe peer - disposable.add(contactRepository.observePeer(chat.getPeer().getId()) - .subscribe(peer -> { - if (peer.isPresent()) { - contactDisplayName.setValue(peer.getItem().getName()); - } - }, + disposable.add(commonViewModel.getPeer() + .subscribe(peer -> contactDisplayName.setValue(peer.getName()), error -> LOGGER.log(Level.SEVERE, "Error subscribing display name to peer", error))); // Subscribe messages - disposable.add(messageRepository.observeMessages(chat) - .doOnNext(m -> LOGGER.log(Level.INFO, "NEW MESSAGES.")) - .subscribe(messageList -> { - - AndroidChatViewModel.this.messages.postValue(messageList); - }, + addDisposable(commonViewModel.getMessages() + .subscribe(messageList -> AndroidChatViewModel.this.messages.postValue(messageList), error -> LOGGER.log(Level.SEVERE, "Error subscribing to messages", error))); } @Override protected void onCleared() { super.onCleared(); - LOGGER.log(Level.INFO, "CLEAR"); disposable.clear(); } @@ -114,12 +108,7 @@ public class AndroidChatViewModel extends ViewModel implements MercuryAndroidVie } public void queryTextChanged(String query) { - Observable> observable = query.isEmpty() ? - messageRepository.observeMessages(chat.getValue()) : - messageRepository.findMessagesWithBody(chat.getValue(), query); - - disposable.add(observable.subscribe(messages -> - AndroidChatViewModel.this.messages.setValue(messages))); + commonViewModel.onQueryTextChanged(query); } public void deleteContact() { @@ -127,9 +116,9 @@ public class AndroidChatViewModel extends ViewModel implements MercuryAndroidVie disposable.add(messenger.deleteContact(contact) .subscribeOn(schedulers.getIoScheduler()) .observeOn(schedulers.getUiScheduler()) - .subscribe(() -> LOGGER.log(Level.INFO, "Contact deleted."), e -> { - LOGGER.log(Level.SEVERE, e.getMessage(), e); - })); + .subscribe( + () -> LOGGER.log(Level.INFO, "Contact deleted."), + e -> LOGGER.log(Level.SEVERE, e.getMessage(), e))); } public void sendMessage(String body) { diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/RosterFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/RosterFragment.java index 5ba97e1..5cbbba9 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/RosterFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/RosterFragment.java @@ -7,6 +7,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; @@ -21,7 +22,7 @@ import org.mercury_im.messenger.android.ui.roster.contacts.ContactListFragment; import butterknife.BindView; import butterknife.ButterKnife; -public class RosterFragment extends Fragment { +public class RosterFragment extends Fragment implements SearchView.OnQueryTextListener { @BindView(R.id.tab_layout) TabLayout tabLayout; @@ -29,6 +30,9 @@ public class RosterFragment extends Fragment { @BindView(R.id.viewpager) ViewPager viewPager; + private ContactListFragment contactListFragment = new ContactListFragment(); + private BookmarkListFragment bookmarkListFragment = new BookmarkListFragment(); + @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -44,6 +48,17 @@ public class RosterFragment extends Fragment { return view; } + @Override + public boolean onQueryTextSubmit(String query) { + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + contactListFragment.onQueryTextChange(newText); + return false; + } + private class RosterFragmentPagerAdapter extends FragmentPagerAdapter { final int PAGE_COUNT = 2; @@ -52,8 +67,8 @@ public class RosterFragment extends Fragment { getString(R.string.tab_bookmarks) }; final Fragment[] PAGES = new Fragment[] { - new ContactListFragment(), - new BookmarkListFragment() + contactListFragment, + bookmarkListFragment }; public RosterFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) { diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/AndroidContactListViewModel.java similarity index 57% rename from app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListViewModel.java rename to app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/AndroidContactListViewModel.java index c29ab5a..f70da74 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/AndroidContactListViewModel.java @@ -1,15 +1,16 @@ package org.mercury_im.messenger.android.ui.roster.contacts; -import android.util.Log; +import android.app.Application; +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; import org.mercury_im.messenger.android.MercuryImApplication; +import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel; import org.mercury_im.messenger.core.Messenger; -import org.mercury_im.messenger.data.repository.RxAccountRepository; -import org.mercury_im.messenger.data.repository.RxPeerRepository; +import org.mercury_im.messenger.core.viewmodel.roster.ContactListViewModel; import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.contact.Peer; @@ -21,13 +22,11 @@ import io.reactivex.disposables.CompositeDisposable; import lombok.Getter; -public class ContactListViewModel extends ViewModel { +public class AndroidContactListViewModel extends AndroidViewModel + implements MercuryAndroidViewModel { @Inject - RxPeerRepository xmppContactRepository; - - @Inject - RxAccountRepository accountRepository; + ContactListViewModel commonViewModel; @Inject @Getter @@ -37,14 +36,13 @@ public class ContactListViewModel extends ViewModel { private final MutableLiveData> accounts = new MutableLiveData<>(); private final CompositeDisposable compositeDisposable = new CompositeDisposable(); - public ContactListViewModel() { - super(); - MercuryImApplication.getApplication().getAppComponent().inject(this); - Log.d("ContactListViewModel", "Start observing database"); + public AndroidContactListViewModel(@NonNull Application application) { + super(application); + ((MercuryImApplication) application).getAppComponent().inject(this); // Subscribe to changes to the contacts table and update the LiveData object for the UI. - compositeDisposable.add(xmppContactRepository.observeAllPeers() + compositeDisposable.add(getCommonViewModel().getContacts() .subscribe(rosterEntryList::postValue)); - compositeDisposable.add(accountRepository.observeAllAccounts() + compositeDisposable.add(getCommonViewModel().getAccounts() .subscribe(accounts::postValue)); } @@ -61,4 +59,13 @@ public class ContactListViewModel extends ViewModel { public LiveData> getAccounts() { return accounts; } + + public void onContactSearchQueryTextChanged(String query) { + getCommonViewModel().onContactSearchQueryChanged(query); + } + + @Override + public ContactListViewModel getCommonViewModel() { + return commonViewModel; + } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListFragment.java index cdb964c..0afccc3 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/ContactListFragment.java @@ -7,6 +7,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; @@ -23,9 +24,9 @@ import static androidx.constraintlayout.widget.Constraints.TAG; /** * A placeholder fragment containing a simple view. */ -public class ContactListFragment extends Fragment { +public class ContactListFragment extends Fragment implements SearchView.OnQueryTextListener { - private ContactListViewModel contactListViewModel; + private AndroidContactListViewModel viewModel; @BindView(R.id.roster_entry_list__recycler_view) RecyclerView recyclerView; @@ -51,18 +52,18 @@ public class ContactListFragment extends Fragment { } private void displayAddContactDialog() { - AddContactDialogFragment addContactDialogFragment = new AddContactDialogFragment(contactListViewModel.getAccounts().getValue(), contactListViewModel.getMessenger()); + AddContactDialogFragment addContactDialogFragment = new AddContactDialogFragment(viewModel.getAccounts().getValue(), viewModel.getMessenger()); addContactDialogFragment.show(this.getParentFragmentManager(), "add"); } @Override public void onAttach(Context context) { super.onAttach(context); - contactListViewModel = new ViewModelProvider(this).get(ContactListViewModel.class); - observeViewModel(contactListViewModel); + viewModel = new ViewModelProvider(this).get(AndroidContactListViewModel.class); + observeViewModel(viewModel); } - private void observeViewModel(ContactListViewModel viewModel) { + private void observeViewModel(AndroidContactListViewModel viewModel) { viewModel.getRosterEntryList().observe(this, rosterEntries -> { if (rosterEntries == null) { Log.d(TAG, "Displaying null roster entries"); @@ -72,4 +73,15 @@ public class ContactListFragment extends Fragment { Log.d(TAG, "Displaying " + rosterEntries.size() + " roster entries"); }); } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + viewModel.onContactSearchQueryTextChanged(newText); + return true; + } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java index 0a1a890..b3aaff4 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java @@ -23,8 +23,7 @@ import com.google.android.material.chip.Chip; import com.google.android.material.chip.ChipGroup; import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; -import org.jivesoftware.smackx.colors.ConsistentColor; -import org.mercury_im.messenger.android.util.OpenPgpFingerprintColorizer; +import org.mercury_im.messenger.android.util.OpenPgpV4FingerprintFormatter; import org.mercury_im.messenger.core.Messenger; import org.mercury_im.messenger.R; import org.mercury_im.messenger.android.ui.chat.ChatActivity; @@ -176,6 +175,6 @@ public class ContactDetailFragment extends Fragment { if (fingerprints.isEmpty()) { return; } - fingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(fingerprints.get(0))); + fingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(fingerprints.get(0))); } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpFingerprintColorizer.java b/app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpV4FingerprintFormatter.java similarity index 98% rename from app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpFingerprintColorizer.java rename to app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpV4FingerprintFormatter.java index cf4990f..9f913bd 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpFingerprintColorizer.java +++ b/app/src/main/java/org/mercury_im/messenger/android/util/OpenPgpV4FingerprintFormatter.java @@ -7,7 +7,7 @@ import android.text.style.ForegroundColorSpan; import org.jivesoftware.smackx.colors.ConsistentColor; import org.pgpainless.key.OpenPgpV4Fingerprint; -public class OpenPgpFingerprintColorizer { +public class OpenPgpV4FingerprintFormatter { /** * Split an OpenPGP fingerprint into 10 blocks of length 4. diff --git a/app/src/main/res/layout/fragment_account_details.xml b/app/src/main/res/layout/fragment_account_details.xml index 7f9e7ba..f17a2b7 100644 --- a/app/src/main/res/layout/fragment_account_details.xml +++ b/app/src/main/res/layout/fragment_account_details.xml @@ -1,16 +1,20 @@ - + + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_fingerprint_toggleable.xml b/app/src/main/res/layout/view_fingerprint_toggleable.xml index 3949f22..f8d13a6 100644 --- a/app/src/main/res/layout/view_fingerprint_toggleable.xml +++ b/app/src/main/res/layout/view_fingerprint_toggleable.xml @@ -40,7 +40,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" - app:layout_constraintTop_toBottomOf="@id/fingerprint" /> + app:layout_constraintTop_toBottomOf="@id/fingerprint_timestamp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_fingerprints_card_toggleable.xml b/app/src/main/res/layout/view_fingerprints_card_toggleable.xml index 51313e4..ab3e424 100644 --- a/app/src/main/res/layout/view_fingerprints_card_toggleable.xml +++ b/app/src/main/res/layout/view_fingerprints_card_toggleable.xml @@ -14,7 +14,8 @@ android:orientation="vertical" android:paddingStart="12dp" android:paddingEnd="12dp" - android:paddingTop="12dp"> + android:paddingTop="12dp" + android:paddingBottom="12dp"> { model.setStanzaId(entity.getStanzaId()); model.setOriginId(entity.getOriginId()); model.setLegacyId(entity.getLegacyStanzaId()); + model.setXml(entity.getXml()); return model; } @@ -49,6 +50,7 @@ public class MessageMapping extends AbstractMapping { entity.setStanzaId(model.getStanzaId()); entity.setOriginId(model.getOriginId()); entity.setLegacyStanzaId(model.getLegacyId()); + entity.setXml(model.getXml()); return entity; } } diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java index 480e3a0..79fd0af 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java @@ -42,6 +42,9 @@ public abstract class AbstractMessageModel implements Persistable { @Column(length = 65536) String body; + @Column(length = 65536) + String xml; + @Column String legacyId; diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/RxMessageRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/RxMessageRepository.java index c1717fc..5ea40da 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/RxMessageRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/RxMessageRepository.java @@ -110,7 +110,7 @@ public class RxMessageRepository public Observable> findMessagesWithBody(String body) { return data().select(MessageModel.class) .from(MessageModel.class) - .where(MessageModel.BODY.eq(body)) + .where(MessageModel.BODY.like("%" + body + "%")) .get().observableResult() .map(ResultDelegate::toList) .map(this::messageModelsToEntities); @@ -121,7 +121,7 @@ public class RxMessageRepository return data().select(MessageModel.class) .from(MessageModel.class) - .where(MessageModel.BODY.eq(body) + .where(MessageModel.BODY.like("%" + body + "%") .and(MessageModel.CHAT_ID.eq(chat.getId()))) .get().observableResult() .map(ResultDelegate::toList) @@ -133,7 +133,7 @@ public class RxMessageRepository return data().select(MessageModel.class) .from(MessageModel.class) - .where(MessageModel.BODY.eq(body) + .where(MessageModel.BODY.like("%" + body + "%") .and(MessageModel.CHAT_ID.eq(chat.getId()))) .get().observableResult() .map(ResultDelegate::toList) diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java index 6ef5e4c..436b4f6 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java @@ -170,10 +170,11 @@ public class RxOpenPgpRepository implements OpenPgpRepository { .where(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId) .and(AnnouncedOpenPgpContactKey.OWNER.eq(owner)) .and(AnnouncedOpenPgpContactKey.FINGERPRINT.eq(fingerprint))) - .get().observableResult() - .map(ResultDelegate::first) - .map(AnnouncedOpenPgpContactKey::getModificationDate) - .singleOrError(); + .limit(1) + .get() + .maybe() + .toSingle() + .map(AnnouncedOpenPgpContactKey::getModificationDate); } @Override @@ -256,27 +257,18 @@ public class RxOpenPgpRepository implements OpenPgpRepository { @Override public Observable> observeRemoteFingerprintsOfAccount(UUID accountId) { - return observeLocalFingerprintOf(accountId) - .flatMap(localFingerprint -> data.select(OpenPgpPublicKeyRing.class) - .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) + return accountRepository.getAccount(accountId).toSingle() + .flatMapObservable(account -> data.select(AnnouncedOpenPgpContactKey.class) + .where(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId) + .and(AnnouncedOpenPgpContactKey.OWNER.eq(account.getJid()))) .get().observableResult() - .map(result -> { - OpenPgpPublicKeyRing ring = new ResultDelegate<>(result).firstOrNull(); - if (ring == null) { - return Collections.emptyList(); - } else { - Iterator iterator = PGPainless.readKeyRing().publicKeyRingCollection(ring.getBytes()).iterator(); - List fingerprints = new ArrayList<>(); - while (iterator.hasNext()) { - PGPPublicKeyRing r = iterator.next(); - OpenPgpV4Fingerprint f = new OpenPgpV4Fingerprint(r); - fingerprints.add(f); - } - if (localFingerprint.isPresent()) { - fingerprints.remove(localFingerprint.getItem()); - } - return fingerprints; + .map(ResultDelegate::toList) + .map(list -> { + ArrayList fingerprints = new ArrayList<>(); + for (AnnouncedOpenPgpContactKey key : list) { + fingerprints.add(key.getFingerprint()); } + return fingerprints; })); } diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/RxPeerRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/RxPeerRepository.java index b303485..8604146 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/RxPeerRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/RxPeerRepository.java @@ -9,6 +9,7 @@ import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.contact.Peer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -174,4 +175,25 @@ public class RxPeerRepository .where(PeerModel.ACCOUNT_ID.eq(accountId)) .get().single().ignoreElement(); } + + @Override + public Observable> findPeers(String query) { + return data().select(PeerModel.class) + .where(PeerModel.ADDRESS.like("%" + query + "%") + .or(PeerModel.NAME.like("%" + query + "%"))) + .get().observableResult() + .map(ResultDelegate::toList) + .map(this::peerModelsToEntities); + } + + @Override + public Observable> findPeers(UUID accountId, String query) { + return data().select(PeerModel.class) + .where(PeerModel.ACCOUNT_ID.eq(accountId) + .and(PeerModel.ADDRESS.like("%" + query + "%") + .or(PeerModel.NAME.like("%" + query + "%")))) + .get().observableResult() + .map(ResultDelegate::toList) + .map(this::peerModelsToEntities); + } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java index ece826d..e9781b6 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java @@ -5,7 +5,6 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.core.util.Optional; -import org.mercury_im.messenger.entity.Account; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.Date; diff --git a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/PeerRepository.java b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/PeerRepository.java index 49f5c9a..3612590 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/PeerRepository.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/PeerRepository.java @@ -61,4 +61,8 @@ public interface PeerRepository { } Completable deleteAllPeers(UUID accountId); + + Observable> findPeers(String query); + + Observable> findPeers(UUID accountId, String query); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java b/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java deleted file mode 100644 index a32e041..0000000 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.mercury_im.messenger.core.di.component; - -import org.mercury_im.messenger.core.di.scope.AccountScope; - -import java.util.UUID; - -import dagger.BindsInstance; -import dagger.Component; - -@Component -public interface ConnectionComponent { - - ConnectionComponent getComponent(); - - @Component.Builder - interface Builder { - - @BindsInstance Builder withAccount(@AccountScope UUID accountId); - - ConnectionComponent build(); - } - -} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/component/CoreComponent.java b/domain/src/main/java/org/mercury_im/messenger/core/di/component/CoreComponent.java new file mode 100644 index 0000000..82a0155 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/component/CoreComponent.java @@ -0,0 +1,23 @@ +package org.mercury_im.messenger.core.di.component; + +import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; +import org.mercury_im.messenger.core.viewmodel.accounts.AccountsViewModel; +import org.mercury_im.messenger.core.viewmodel.accounts.LoginViewModel; +import org.mercury_im.messenger.core.viewmodel.chat.ChatViewModel; +import org.mercury_im.messenger.core.viewmodel.ox.OxSecretKeyBackupRestoreViewModel; + +import dagger.Component; + +@Component +public interface CoreComponent { + + void inject(LoginViewModel viewModel); + + void inject(AccountsViewModel viewModel); + + void inject(AccountDetailsViewModel viewModel); + + void inject(ChatViewModel viewModel); + + void inject(OxSecretKeyBackupRestoreViewModel viewModel); +} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java index 12a36e1..4f23935 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java @@ -1,11 +1,14 @@ package org.mercury_im.messenger.core.di.module; +import org.mercury_im.messenger.core.Messenger; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.AccountRepository; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; +import org.mercury_im.messenger.core.data.repository.Repositories; import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; import org.mercury_im.messenger.core.viewmodel.accounts.AccountsViewModel; import org.mercury_im.messenger.core.viewmodel.accounts.LoginViewModel; +import org.mercury_im.messenger.core.viewmodel.chat.ChatViewModel; import org.mercury_im.messenger.core.xmpp.MercuryConnectionManager; import javax.inject.Singleton; @@ -41,6 +44,11 @@ public class ViewModelModule { return new AccountDetailsViewModel(openPgpRepository, accountRepository, schedulers); } + @Provides + @Singleton + static ChatViewModel provideChatViewModel(Messenger messenger, Repositories repositories, SchedulersFacade schedulers) { + return new ChatViewModel(messenger, repositories, schedulers); + } /* @Provides @Singleton diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/scope/AccountScope.java b/domain/src/main/java/org/mercury_im/messenger/core/di/scope/AccountScope.java deleted file mode 100644 index f6d9729..0000000 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/scope/AccountScope.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mercury_im.messenger.core.di.scope; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Documented -@Retention(value = RetentionPolicy.RUNTIME) -public @interface AccountScope { -} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java b/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java index e417f71..c700022 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java @@ -68,6 +68,7 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date()); message.setSender(from.asEntityBareJidString()); message.setRecipient(smackMessage.getTo().asBareJid().toString()); + message.setXml(smackMessage.toXML().toString()); if (smackMessage.getBody() != null) { message.setBody(smackMessage.getBody()); } @@ -86,6 +87,7 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin message.setTimestamp(new Date()); message.setSender(account.getAddress()); message.setRecipient(to.asBareJid().toString()); + message.setXml(smackMessage.build().toXML().toString()); if (smackMessage.getBody() != null) { message.setBody(smackMessage.getBody()); } @@ -111,6 +113,7 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date()); message.setSender(contact.getJid().toString()); message.setRecipient(smackMessage.getTo().asBareJid().toString()); + message.setXml(smackMessage.toXML().toString()); org.jivesoftware.smack.packet.Message.Body body = decryptedPayload.getExtension(org.jivesoftware.smack.packet.Message.Body.ELEMENT, org.jivesoftware.smack.packet.Message.Body.NAMESPACE); if (body != null) { diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java index 9936465..eddf3fb 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java @@ -6,9 +6,11 @@ import java.util.Date; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.ToString; @Data @AllArgsConstructor +@ToString public class AccountFingerprintViewItem { OpenPgpV4Fingerprint fingerprint; Date announcementDate; diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/chat/ChatViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/chat/ChatViewModel.java index eb90ea1..d47aeb7 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/chat/ChatViewModel.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/chat/ChatViewModel.java @@ -3,6 +3,7 @@ package org.mercury_im.messenger.core.viewmodel.chat; import org.mercury_im.messenger.core.Messenger; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.Repositories; +import org.mercury_im.messenger.core.util.Optional; import org.mercury_im.messenger.core.viewmodel.MercuryViewModel; import org.mercury_im.messenger.entity.chat.DirectChat; import org.mercury_im.messenger.entity.contact.Peer; @@ -11,6 +12,7 @@ import org.mercury_im.messenger.entity.message.Message; import java.util.List; import io.reactivex.Observable; +import io.reactivex.subjects.BehaviorSubject; import lombok.Getter; public class ChatViewModel implements MercuryViewModel { @@ -18,6 +20,7 @@ public class ChatViewModel implements MercuryViewModel { private final Messenger messenger; private final Repositories repositories; private final SchedulersFacade schedulers; + private DirectChat chat; @Getter private Observable peer; @@ -28,23 +31,35 @@ public class ChatViewModel implements MercuryViewModel { @Getter private Observable contactDisplayName; + private final BehaviorSubject>> messageQueryObservable = BehaviorSubject.create(); + public ChatViewModel(Messenger messenger, Repositories repositories, - SchedulersFacade schedulers, - DirectChat chat) { + SchedulersFacade schedulers) { this.messenger = messenger; this.repositories = repositories; this.schedulers = schedulers; + } + public void init(DirectChat directChat) { + this.chat = directChat; //peer = repositories.getPeerRepository().observePeer(chat.getPeer()); - messages = repositories.getMessageRepository().observeMessages(chat); + messageQueryObservable.onNext(repositories.getMessageRepository().observeMessages(chat)); + messages = Observable.switchOnNext(messageQueryObservable); + + peer = repositories.getPeerRepository().observePeer(chat.getPeer().getId()) + .filter(Optional::isPresent).map(Optional::getItem); contactDisplayName = repositories.getPeerRepository().observePeer(chat.getPeer()) .map(optional -> optional.isPresent() ? optional.getItem().getDisplayName() : "DELETED"); } - public void sendMessage(String body) { - //addDisposable(messenger.sendMessage()); + public void onQueryTextChanged(String query) { + if (query.trim().isEmpty()) { + messageQueryObservable.onNext(repositories.getMessageRepository().observeMessages(chat)); + } else { + messageQueryObservable.onNext(repositories.getMessageRepository().findMessagesWithBody(chat, query)); + } } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/roster/ContactListViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/roster/ContactListViewModel.java new file mode 100644 index 0000000..fd7029f --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/roster/ContactListViewModel.java @@ -0,0 +1,47 @@ +package org.mercury_im.messenger.core.viewmodel.roster; + +import org.mercury_im.messenger.core.data.repository.AccountRepository; +import org.mercury_im.messenger.core.data.repository.PeerRepository; +import org.mercury_im.messenger.core.viewmodel.MercuryViewModel; +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.entity.contact.Peer; + +import java.util.List; + +import javax.inject.Inject; + +import io.reactivex.Observable; +import io.reactivex.subjects.BehaviorSubject; +import lombok.Getter; + +public class ContactListViewModel implements MercuryViewModel { + + private final PeerRepository peerRepository; + private final AccountRepository accountRepository; + + @Getter + private Observable> contacts; + + @Getter + private Observable> accounts; + + private BehaviorSubject>> queryResultObservables = BehaviorSubject.create(); + + @Inject + public ContactListViewModel(PeerRepository peerRepository, AccountRepository accountRepository) { + this.peerRepository = peerRepository; + this.accountRepository = accountRepository; + + queryResultObservables.onNext(peerRepository.observeAllPeers()); + this.contacts = Observable.switchOnNext(queryResultObservables); + this.accounts = accountRepository.observeAllAccounts(); + } + + public void onContactSearchQueryChanged(String query) { + if (query.trim().isEmpty()) { + queryResultObservables.onNext(peerRepository.observeAllPeers()); + } else { + queryResultObservables.onNext(peerRepository.findPeers(query)); + } + } +} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnection.java b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnection.java index f2529e3..ad190ca 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnection.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnection.java @@ -16,6 +16,7 @@ import org.mercury_im.messenger.core.xmpp.state.ConnectionState; import java.io.IOException; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -27,6 +28,7 @@ import lombok.Getter; public class MercuryConnection { private static final Logger LOGGER = Logger.getLogger(MercuryConnection.class.getName()); + private AtomicBoolean connecting = new AtomicBoolean(false); @Getter private XMPPConnection connection; @@ -59,20 +61,25 @@ public class MercuryConnection { } private synchronized void doConnect() throws ServerUnreachableException { - AbstractXMPPConnection connection = (AbstractXMPPConnection) getConnection(); - if (connection.isConnected()) { - return; + if (connecting.compareAndSet(false, true)) { + AbstractXMPPConnection connection = (AbstractXMPPConnection) getConnection(); + if (connection.isConnected()) { + return; + } + try { + connection.connect(); + connecting.set(false); + } catch (SmackException.EndpointConnectionException e) { + connection.disconnect(); + throw new ServerUnreachableException("Cannot connect to server " + connection.getXMPPServiceDomain().asUnescapedString(), e); + } catch (IOException | InterruptedException | XMPPException | SmackException e) { + throw new AssertionError("Unexpected exception.", e); + } + stateObservable.onNext(stateObservable.getValue().withConnectivity(ConnectivityState.connecting)); + LOGGER.log(Level.INFO, "Connected!"); + } else { + LOGGER.log(Level.INFO, "Already connecting."); } - try { - connection.connect(); - } catch (SmackException.EndpointConnectionException e) { - connection.disconnect(); - throw new ServerUnreachableException("Cannot connect to server " + connection.getXMPPServiceDomain().asUnescapedString(), e); - } catch (IOException | InterruptedException | XMPPException | SmackException e) { - throw new AssertionError("Unexpected exception.", e); - } - stateObservable.onNext(stateObservable.getValue().withConnectivity(ConnectivityState.connecting)); - LOGGER.log(Level.INFO, "Connected!"); } public Completable login() { @@ -109,6 +116,7 @@ public class MercuryConnection { private final ConnectionListener connectionListener = new ConnectionListener() { @Override public void connected(XMPPConnection connection) { + connecting.set(false); stateObservable.onNext(stateObservable.getValue() .withConnectivity(ConnectivityState.connected) .withAuthenticated(false)); @@ -127,6 +135,7 @@ public class MercuryConnection { @Override public void connectionClosed() { + connecting.set(false); stateObservable.onNext(stateObservable.getValue() .withConnectivity(ConnectivityState.disconnected) .withAuthenticated(false)); @@ -134,6 +143,7 @@ public class MercuryConnection { @Override public void connectionClosedOnError(Exception e) { + connecting.set(false); stateObservable.onNext(stateObservable.getValue() .withConnectivity(ConnectivityState.disconnected) .withAuthenticated(false)); diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java b/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java index ac8645b..29ec04e 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java @@ -18,6 +18,7 @@ public class Message { String legacyStanzaId; String originId; String stanzaId; + String xml; boolean encrypted; boolean received; boolean read;