Implement adding/removing contacts + detail contact view
This commit is contained in:
parent
8fa067acfb
commit
71e16d0fef
|
@ -35,6 +35,8 @@
|
|||
<activity
|
||||
android:name=".ui.account.LoginActivity"
|
||||
android:label="@string/title_activity_login" />
|
||||
<activity android:name=".ui.roster.contacts.detail.ContactDetailActivity" />
|
||||
|
||||
<service android:name=".service.MercuryConnectionService" />
|
||||
</application>
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import org.mercury_im.messenger.ui.account.AccountsViewModel;
|
|||
import org.mercury_im.messenger.ui.account.LoginActivity;
|
||||
import org.mercury_im.messenger.ui.account.LoginViewModel;
|
||||
import org.mercury_im.messenger.ui.roster.contacts.ContactListViewModel;
|
||||
import org.mercury_im.messenger.ui.roster.contacts.detail.ContactDetailActivity;
|
||||
import org.mercury_im.messenger.ui.roster.contacts.detail.ContactDetailViewModel;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
@ -49,7 +51,8 @@ public interface AppComponent {
|
|||
|
||||
void inject(ChatInputFragment chatInputFragment);
|
||||
|
||||
void inject(ChatListViewModel chatListViewModel);
|
||||
void inject(ContactDetailActivity contactDetailActivity);
|
||||
|
||||
|
||||
|
||||
// ViewModels
|
||||
|
@ -64,6 +67,10 @@ public interface AppComponent {
|
|||
|
||||
void inject(AccountsViewModel accountsViewModel);
|
||||
|
||||
void inject(ChatListViewModel chatListViewModel);
|
||||
|
||||
void inject(ContactDetailViewModel contactDetailViewModel);
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -114,13 +115,15 @@ public class ChatActivity extends AppCompatActivity
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_debug:
|
||||
Log.d("CHATACTIVITY", "Fetch MAM messages!");
|
||||
chatViewModel.requestMamMessages()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe();
|
||||
Peer peer = chatViewModel.getContact().getValue();
|
||||
Toast.makeText(this, "subscription: " + peer.getSubscriptionDirection().toString() +
|
||||
" isApproved: " + peer.isSubscriptionApproved() + " isPending: " + peer.isSubscriptionPending(), Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
|
||||
// menu_chat
|
||||
case R.id.action_delete_contact:
|
||||
chatViewModel.deleteContact();
|
||||
break;
|
||||
case R.id.action_call:
|
||||
case R.id.action_clear_history:
|
||||
case R.id.action_notification_settings:
|
||||
|
|
|
@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel;
|
|||
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.data.repository.DirectChatRepository;
|
||||
import org.mercury_im.messenger.data.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.data.repository.PeerRepository;
|
||||
|
@ -24,7 +25,6 @@ import io.reactivex.Completable;
|
|||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class ChatViewModel extends ViewModel {
|
||||
|
@ -41,6 +41,9 @@ public class ChatViewModel extends ViewModel {
|
|||
@Inject
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@Inject
|
||||
Messenger messenger;
|
||||
|
||||
private MutableLiveData<Peer> contact = new MutableLiveData<>();
|
||||
private MutableLiveData<List<Message>> messages = new MutableLiveData<>();
|
||||
private MutableLiveData<String> contactDisplayName = new MutableLiveData<>();
|
||||
|
@ -60,10 +63,15 @@ public class ChatViewModel extends ViewModel {
|
|||
|
||||
public void init(DirectChat chat) {
|
||||
this.chat.setValue(chat);
|
||||
this.contact.setValue(chat.getPeer());
|
||||
|
||||
// Subscribe peer
|
||||
disposable.add(contactRepository.observePeer(chat.getPeer().getId())
|
||||
.subscribe(peer -> contactDisplayName.setValue(peer.getItem().getName()),
|
||||
.subscribe(peer -> {
|
||||
if (peer.isPresent()) {
|
||||
contactDisplayName.setValue(peer.getItem().getName());
|
||||
}
|
||||
},
|
||||
error -> LOGGER.log(Level.SEVERE, "Error subscribing display name to peer", error)));
|
||||
|
||||
// Subscribe messages
|
||||
|
@ -112,4 +120,14 @@ public class ChatViewModel extends ViewModel {
|
|||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
public void deleteContact() {
|
||||
Peer contact = getContact().getValue();
|
||||
disposable.add(messenger.deleteContact(contact)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> LOGGER.log(Level.INFO, "Contact deleted."), e -> {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,12 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.entity.chat.DirectChat;
|
||||
import org.mercury_im.messenger.ui.avatar.AvatarDrawable;
|
||||
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
||||
import org.mercury_im.messenger.ui.roster.contacts.detail.ContactDetailActivity;
|
||||
import org.mercury_im.messenger.ui.util.AbstractRecyclerViewAdapter;
|
||||
import org.mercury_im.messenger.util.ColorUtil;
|
||||
|
||||
|
@ -102,7 +104,7 @@ public class ChatListRecyclerViewAdapter
|
|||
|
||||
@Override
|
||||
public boolean areContentsTheSame(DirectChat oldItem, DirectChat newItem) {
|
||||
return oldItem.getPeer().getName().equals(newItem.getPeer().getName());
|
||||
return Objects.equals(oldItem.getPeer().getName(), newItem.getPeer().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,41 +2,90 @@ package org.mercury_im.messenger.ui.roster.contacts;
|
|||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.DataSetObserver;
|
||||
import android.opengl.Visibility;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDialogFragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
import org.mercury_im.messenger.exception.ConnectionNotFoundException;
|
||||
import org.mercury_im.messenger.exception.ContactAlreadyAddedException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class AddContactDialogFragment extends AppCompatDialogFragment {
|
||||
|
||||
private final LiveData<List<Account>> accounts;
|
||||
private final List<Account> accounts;
|
||||
private final Messenger messenger;
|
||||
|
||||
public AddContactDialogFragment(LiveData<List<Account>> accountList) {
|
||||
@BindView(R.id.account_select_container)
|
||||
LinearLayout accountSelectorContainer;
|
||||
|
||||
@BindView(R.id.spinner)
|
||||
Spinner accountSelector;
|
||||
|
||||
@BindView(R.id.address_layout)
|
||||
TextInputLayout contactAddressLayout;
|
||||
|
||||
@BindView(R.id.address)
|
||||
TextInputEditText contactAddress;
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
public AddContactDialogFragment(List<Account> accountList, Messenger messenger) {
|
||||
this.accounts = accountList;
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
|
||||
View dialogView = inflater.inflate(R.layout.dialog_add_contact, null);
|
||||
Spinner spinner = dialogView.findViewById(R.id.spinner);
|
||||
spinner.setAdapter(
|
||||
new ArrayAdapter<>(requireActivity(), R.layout.support_simple_spinner_dropdown_item, accounts.getValue()));
|
||||
ButterKnife.bind(this, dialogView);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
// Hide Spinner when only one account.
|
||||
if (accounts == null || accounts.size() <= 1) {
|
||||
accountSelectorContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
accountSelector.setAdapter(new AccountAdapter(requireActivity(), accounts));
|
||||
accountSelector.setSelection(0);
|
||||
|
||||
builder.setMessage("Add Contact")
|
||||
.setView(dialogView)
|
||||
|
@ -44,7 +93,7 @@ public class AddContactDialogFragment extends AppCompatDialogFragment {
|
|||
.setPositiveButton("Add", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
// Later overwrite in onResume.
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
|
@ -56,4 +105,69 @@ public class AddContactDialogFragment extends AppCompatDialogFragment {
|
|||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
final AlertDialog d = (AlertDialog)getDialog();
|
||||
if(d != null)
|
||||
{
|
||||
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
Account account = accounts.get(accountSelector.getSelectedItemPosition());
|
||||
String address = contactAddress.getText() != null ? contactAddress.getText().toString() : "";
|
||||
disposable.add(messenger.addContact(account.getId(), address)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()).subscribe(
|
||||
AddContactDialogFragment.this::dismiss,
|
||||
e -> {
|
||||
if (e instanceof SmackException.NotLoggedInException || e instanceof SmackException.NotConnectedException) {
|
||||
contactAddressLayout.setError("Account not connected");
|
||||
} else if (e instanceof ContactAlreadyAddedException) {
|
||||
contactAddressLayout.setError("Contact already added");
|
||||
} else if (e instanceof XmppStringprepException) {
|
||||
contactAddressLayout.setError("Invalid address");
|
||||
} else {
|
||||
contactAddressLayout.setError(e.getClass().getName());
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposable.dispose();
|
||||
}
|
||||
|
||||
private static class AccountAdapter extends ArrayAdapter<Account> {
|
||||
|
||||
public AccountAdapter(@NonNull Context context, @NonNull List<Account> objects) {
|
||||
super(context, R.layout.spinner_item_account, objects);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(R.layout.spinner_item_account, parent, false);
|
||||
}
|
||||
TextView textView = convertView.findViewById(R.id.account_address);
|
||||
textView.setText(getItem(position).getAddress());
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
return getView(position, convertView, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
|
|||
|
||||
import org.mercury_im.messenger.R;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
|
@ -52,7 +54,7 @@ public class ContactListFragment extends Fragment {
|
|||
}
|
||||
|
||||
private void displayAddContactDialog() {
|
||||
AddContactDialogFragment addContactDialogFragment = new AddContactDialogFragment(contactListViewModel.getAccounts());
|
||||
AddContactDialogFragment addContactDialogFragment = new AddContactDialogFragment(contactListViewModel.getAccounts().getValue(), contactListViewModel.getMessenger());
|
||||
addContactDialogFragment.show(this.getParentFragmentManager(), "add");
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.mercury_im.messenger.R;
|
|||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
import org.mercury_im.messenger.ui.avatar.AvatarDrawable;
|
||||
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
||||
import org.mercury_im.messenger.ui.roster.contacts.detail.ContactDetailActivity;
|
||||
import org.mercury_im.messenger.ui.util.AbstractRecyclerViewAdapter;
|
||||
import org.mercury_im.messenger.util.ColorUtil;
|
||||
|
||||
|
@ -73,9 +74,9 @@ public class ContactListRecyclerViewAdapter
|
|||
avatarView.setImageDrawable(new AvatarDrawable(name, address));
|
||||
view.setOnClickListener(view -> {
|
||||
|
||||
Intent intent = new Intent(context, ChatActivity.class);
|
||||
intent.putExtra(ChatActivity.EXTRA_JID, address);
|
||||
intent.putExtra(ChatActivity.EXTRA_ACCOUNT, contact.getAccount().getId().toString());
|
||||
Intent intent = new Intent(context, ContactDetailActivity.class);
|
||||
intent.putExtra(ContactDetailActivity.EXTRA_JID, address);
|
||||
intent.putExtra(ContactDetailActivity.EXTRA_ACCOUNT, contact.getAccount().getId().toString());
|
||||
|
||||
context.startActivity(intent);
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.data.repository.XmppAccountRepository;
|
||||
import org.mercury_im.messenger.data.repository.XmppPeerRepository;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
@ -17,6 +18,7 @@ import java.util.List;
|
|||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
public class ContactListViewModel extends ViewModel {
|
||||
|
@ -27,6 +29,10 @@ public class ContactListViewModel extends ViewModel {
|
|||
@Inject
|
||||
XmppAccountRepository accountRepository;
|
||||
|
||||
@Inject
|
||||
@Getter
|
||||
Messenger messenger;
|
||||
|
||||
private final MutableLiveData<List<Peer>> rosterEntryList = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<Account>> accounts = new MutableLiveData<>();
|
||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package org.mercury_im.messenger.ui.roster.contacts.detail;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.R;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class ContactDetailActivity extends AppCompatActivity {
|
||||
public static final String EXTRA_JID = "JID";
|
||||
public static final String EXTRA_ACCOUNT = "ACCOUNT";
|
||||
|
||||
@BindView(R.id.fragment)
|
||||
FrameLayout container;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
savedInstanceState = getIntent().getExtras();
|
||||
if (savedInstanceState == null) return;
|
||||
}
|
||||
|
||||
String jidString = savedInstanceState.getString(EXTRA_JID);
|
||||
if (jidString != null) {
|
||||
UUID accountId = UUID.fromString(savedInstanceState.getString(EXTRA_ACCOUNT));
|
||||
|
||||
ContactDetailViewModel viewModel = new ViewModelProvider(this).get(ContactDetailViewModel.class);
|
||||
viewModel.bind(accountId, jidString);
|
||||
}
|
||||
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new ContactDetailFragment(), "contact_details").commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package org.mercury_im.messenger.ui.roster.contacts.detail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.ui.chat.ChatActivity;
|
||||
import org.mercury_im.messenger.util.ColorUtil;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class ContactDetailFragment extends Fragment {
|
||||
|
||||
@BindView(R.id.contact_avatar)
|
||||
ImageView contactAvatar;
|
||||
|
||||
@BindView(R.id.contact_status_badge)
|
||||
ImageView contactStatusBadge;
|
||||
|
||||
@BindView(R.id.contact_name)
|
||||
TextView contactName;
|
||||
|
||||
@BindView(R.id.contact_address)
|
||||
TextView contactAddress;
|
||||
|
||||
@BindView(R.id.contact_presence)
|
||||
TextView contactPresence;
|
||||
|
||||
@BindView(R.id.contact_account)
|
||||
TextView contactAccount;
|
||||
|
||||
@BindView(R.id.contact_groups)
|
||||
ChipGroup contactGroups;
|
||||
|
||||
@BindView(R.id.fab)
|
||||
ExtendedFloatingActionButton fab;
|
||||
|
||||
private ContactDetailViewModel viewModel;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_contact_details, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
if (fab != null) {
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(getContext(), ChatActivity.class);
|
||||
intent.putExtra(ChatActivity.EXTRA_JID, viewModel.getContactAddress().getValue());
|
||||
intent.putExtra(ChatActivity.EXTRA_ACCOUNT, viewModel.getAccountId().getValue().toString());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(ContactDetailViewModel.class);
|
||||
|
||||
observeViewModel();
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getContactAvatar().observe(this, drawable -> contactAvatar.setImageDrawable(drawable));
|
||||
viewModel.getContactPresenceMode().observe(this, mode -> {
|
||||
int color = 0;
|
||||
switch (mode) {
|
||||
case chat:
|
||||
case available:
|
||||
color = ColorUtil.rgb(0, 255, 0);
|
||||
break;
|
||||
case away:
|
||||
case xa:
|
||||
color = ColorUtil.rgb(255, 128, 0);
|
||||
break;
|
||||
case dnd:
|
||||
color = ColorUtil.rgb(255, 0, 0);
|
||||
break;
|
||||
}
|
||||
contactStatusBadge.setColorFilter(color);
|
||||
});
|
||||
viewModel.getContactName().observe(this, name -> contactName.setText(name));
|
||||
viewModel.getContactAddress().observe(this, address -> contactAddress.setText(address));
|
||||
viewModel.getContactPresenceStatus().observe(this, presenceText -> contactPresence.setText(presenceText));
|
||||
viewModel.getContactAccountAddress().observe(this, address -> contactAccount.setText(address));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package org.mercury_im.messenger.ui.roster.contacts.detail;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import org.jivesoftware.smack.PresenceListener;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.roster.PresenceEventListener;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.Messenger;
|
||||
import org.mercury_im.messenger.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
import org.mercury_im.messenger.ui.avatar.AvatarDrawable;
|
||||
import org.mercury_im.messenger.util.CombinedPresenceListener;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class ContactDetailViewModel extends ViewModel {
|
||||
|
||||
@Inject
|
||||
PeerRepository peerRepository;
|
||||
|
||||
@Inject
|
||||
Messenger messenger;
|
||||
|
||||
private MutableLiveData<UUID> contactAccountId = new MutableLiveData<>(UUID.randomUUID());
|
||||
private MutableLiveData<String> contactAddress = new MutableLiveData<>("alice@wonderland.lit");
|
||||
private MutableLiveData<Drawable> contactAvatar = new MutableLiveData<>(new AvatarDrawable("Alice Wonderland", "alice@wonderland.lit"));
|
||||
private MutableLiveData<Presence.Mode> contactPresenceMode = new MutableLiveData<>(Presence.Mode.available);
|
||||
private MutableLiveData<String> contactPresenceStatus = new MutableLiveData<>("Going down the rabbit hole.");
|
||||
private MutableLiveData<String> contactName = new MutableLiveData<>("Alice Wonderland");
|
||||
private MutableLiveData<String> contactAccountAddress = new MutableLiveData<>("mad@hatter.lit");
|
||||
|
||||
private Roster roster;
|
||||
|
||||
private CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
public ContactDetailViewModel() {
|
||||
super();
|
||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
}
|
||||
|
||||
public void bind(UUID accountId, String peerAddress) {
|
||||
Log.d("MMMMMM", "Bind!");
|
||||
contactAddress.setValue(peerAddress);
|
||||
contactAccountId.setValue(accountId);
|
||||
disposable.add(peerRepository.observePeerByAddress(accountId, peerAddress)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(peerOptional -> {
|
||||
if (!peerOptional.isPresent()) {
|
||||
return;
|
||||
}
|
||||
Peer peer = peerOptional.getItem();
|
||||
contactAccountAddress.setValue(peer.getAccount().getAddress());
|
||||
contactAvatar.setValue(new AvatarDrawable(peer.getDisplayName(), peer.getAddress()));
|
||||
contactName.setValue(peer.getDisplayName());
|
||||
}));
|
||||
|
||||
roster = Roster.getInstanceFor(messenger.getConnectionManager().getConnection(accountId).getConnection());
|
||||
roster.addPresenceEventListener(presenceEventListener);
|
||||
|
||||
Presence presence = roster.getPresence(JidCreate.entityBareFromOrThrowUnchecked(peerAddress));
|
||||
if (presence != null) {
|
||||
contactPresenceMode.postValue(presence.getMode());
|
||||
contactPresenceStatus.postValue(presence.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<String> getContactAddress() {
|
||||
return contactAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
disposable.dispose();
|
||||
if (roster != null) {
|
||||
roster.removePresenceEventListener(presenceEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<UUID> getAccountId() {
|
||||
return contactAccountId;
|
||||
}
|
||||
|
||||
public LiveData<Drawable> getContactAvatar() {
|
||||
return contactAvatar;
|
||||
}
|
||||
|
||||
public LiveData<Presence.Mode> getContactPresenceMode() {
|
||||
return contactPresenceMode;
|
||||
}
|
||||
|
||||
public LiveData<String> getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public LiveData<String> getContactPresenceStatus() {
|
||||
return contactPresenceStatus;
|
||||
}
|
||||
|
||||
public LiveData<String> getContactAccountAddress() {
|
||||
return contactAccountAddress;
|
||||
}
|
||||
|
||||
private final PresenceEventListener presenceEventListener = new CombinedPresenceListener() {
|
||||
@Override
|
||||
public void presenceReceived(Jid address, Presence presence) {
|
||||
if (presence.getFrom().asBareJid().toString().equals(getContactAddress().getValue())) {
|
||||
contactPresenceMode.postValue(presence.getMode());
|
||||
contactPresenceStatus.postValue(presence.getStatus());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar_layout"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_navigation">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -20,10 +20,12 @@
|
|||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp" />
|
||||
android:layout_height="56dp"
|
||||
tools:listitem="@layout/spinner_item_account"/>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/address_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
android:transitionName="fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_add_account"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
app:icon="@drawable/ic_add_white_24dp"/>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fab"
|
||||
android:transitionName="fab"
|
||||
android:text="@string/action_add_bookmark"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
app:icon="@drawable/ic_group_add_black_24dp"/>
|
||||
|
|
|
@ -1,6 +1,162 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:background="@color/lightThemeBackground">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
card_view:cardElevation="4dp"
|
||||
card_view:cardCornerRadius="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/contact_avatar"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_person_black_24dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/contact_status_badge"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:src="@drawable/circle"
|
||||
android:tint="@android:color/holo_green_dark"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/contact_avatar"
|
||||
app:layout_constraintEnd_toEndOf="@+id/contact_avatar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contact_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@tools:sample/full_names"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintTop_toTopOf="@+id/contact_avatar"
|
||||
app:layout_constraintStart_toEndOf="@+id/contact_avatar"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/contact_address" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contact_address"
|
||||
android:layout_width="285dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/contact_avatar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.050"
|
||||
app:layout_constraintStart_toEndOf="@+id/contact_avatar"
|
||||
tools:text="romeo@montague.lit" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contact_presence"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
tools:text="Unavailable due to extended inactivity for more than 15 minutes."/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardCornerRadius="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Used Account"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contact_account"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="vanitasvitae@jabberhead.tk"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Categories"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/contact_groups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipSpacing="4dp">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipStrokeColor="@color/black"
|
||||
android:text="Work"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Dungeons n' Dragons"/>
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fab"
|
||||
android:transitionName="fab"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
app:icon="@drawable/ic_message_black_24dp"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fab"
|
||||
android:transitionName="fab"
|
||||
android:text="@string/action_add_contact"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
app:icon="@drawable/ic_person_add_black_24dp"/>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/account_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:padding="16dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.DropDownItem" />
|
|
@ -11,6 +11,12 @@
|
|||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item android:id="@+id/action_delete_contact"
|
||||
android:orderInCategory="50"
|
||||
android:title="@string/action_remove_contact"
|
||||
android:icon="@drawable/ic_delete_black_24dp"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item android:id="@+id/action_call"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_call"
|
||||
|
|
|
@ -131,4 +131,5 @@
|
|||
<string name="action_add_bookmark">Add Bookmark</string>
|
||||
<string name="action_close_chat">Close Chat</string>
|
||||
<string name="action_copy">Copy</string>
|
||||
<string name="action_remove_contact">Remove Contact</string>
|
||||
</resources>
|
||||
|
|
|
@ -140,7 +140,7 @@ public class XmppPeerRepository
|
|||
public Observable<List<Peer>> observeAllContactsOfAccount(UUID accountId) {
|
||||
return data().select(PeerModel.class)
|
||||
.where(PeerModel.ACCOUNT_ID.eq(accountId))
|
||||
.and(isContact())
|
||||
//.and(isContact())
|
||||
.get().observableResult()
|
||||
.map(ResultDelegate::toList)
|
||||
.map(this::peerModelsToEntities)
|
||||
|
@ -148,12 +148,6 @@ public class XmppPeerRepository
|
|||
.observeOn(observerScheduler());
|
||||
}
|
||||
|
||||
private LogicalCondition<? extends Expression<SubscriptionDirection>, ?> isContact() {
|
||||
return PeerModel.SUBSCRIPTION_DIRECTION.in(Arrays.asList(
|
||||
SubscriptionDirection.both,
|
||||
SubscriptionDirection.to));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Peer> updatePeer(Peer peer) {
|
||||
// In order to update, we fetch the model, update it and write it back.
|
||||
|
|
|
@ -1,16 +1,31 @@
|
|||
package org.mercury_im.messenger;
|
||||
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.mercury_im.messenger.data.repository.Repositories;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
import org.mercury_im.messenger.exception.ConnectionNotFoundException;
|
||||
import org.mercury_im.messenger.exception.ContactAlreadyAddedException;
|
||||
import org.mercury_im.messenger.xmpp.MercuryConnection;
|
||||
import org.mercury_im.messenger.xmpp.MercuryConnectionManager;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
|
@ -54,4 +69,58 @@ public class Messenger {
|
|||
return account;
|
||||
}
|
||||
|
||||
|
||||
public Completable addContact(UUID accountId, String contactAddress) {
|
||||
return Completable.fromAction(() -> doAddContact(accountId, contactAddress));
|
||||
}
|
||||
|
||||
private void doAddContact(UUID accountId, String contactAddress)
|
||||
throws ConnectionNotFoundException, XmppStringprepException, ContactAlreadyAddedException,
|
||||
SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||
MercuryConnection connection = getConnectionManager().getConnection(accountId);
|
||||
if (connection == null) {
|
||||
throw new ConnectionNotFoundException(accountId);
|
||||
}
|
||||
|
||||
EntityBareJid jid = JidCreate.entityBareFrom(contactAddress);
|
||||
Roster roster = Roster.getInstanceFor(connection.getConnection());
|
||||
if (roster.getEntry(jid) != null) {
|
||||
throw new ContactAlreadyAddedException(jid);
|
||||
}
|
||||
|
||||
if (roster.isSubscriptionPreApprovalSupported()) {
|
||||
LOGGER.log(Level.INFO, "Pre-Approval supported.");
|
||||
try {
|
||||
roster.preApproveAndCreateEntry(jid, null, null);
|
||||
} catch (SmackException.FeatureNotSupportedException e) {
|
||||
throw new AssertionError("pre-approval failed even though the feature is announced.");
|
||||
}
|
||||
} else {
|
||||
roster.createItemAndRequestSubscription(jid, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Completable deleteContact(Peer contact) {
|
||||
return Completable.fromAction(() -> doDeleteContact(contact));
|
||||
}
|
||||
|
||||
private void doDeleteContact(Peer contact)
|
||||
throws ConnectionNotFoundException, XmppStringprepException,
|
||||
SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||
MercuryConnection connection = getConnectionManager().getConnection(contact.getAccount().getId());
|
||||
if (connection == null) {
|
||||
throw new ConnectionNotFoundException(contact.getAccount().getId());
|
||||
}
|
||||
|
||||
Roster roster = Roster.getInstanceFor(connection.getConnection());
|
||||
EntityBareJid jid = JidCreate.entityBareFrom(contact.getAddress());
|
||||
RosterEntry entry = roster.getEntry(jid);
|
||||
if (entry != null) {
|
||||
roster.removeEntry(entry);
|
||||
} else {
|
||||
throw new IllegalStateException("Contact " + jid.toString() + " not in roster!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package org.mercury_im.messenger.exception;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class ConnectionNotFoundException extends Exception {
|
||||
|
||||
@Getter
|
||||
private final UUID accountId;
|
||||
|
||||
public ConnectionNotFoundException(UUID accountId) {
|
||||
super("Connection with ID " + accountId.toString() + " not registered.");
|
||||
this.accountId = accountId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.mercury_im.messenger.exception;
|
||||
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class ContactAlreadyAddedException extends Exception {
|
||||
|
||||
@Getter
|
||||
private final Jid jid;
|
||||
|
||||
public ContactAlreadyAddedException(Jid jid) {
|
||||
super("Contact with address " + jid.toString() + " is already a contact.");
|
||||
this.jid = jid;
|
||||
}
|
||||
}
|
|
@ -21,8 +21,6 @@ import java.util.UUID;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
package org.mercury_im.messenger.usecase;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.roster.PresenceEventListener;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
import org.jivesoftware.smack.roster.RosterListener;
|
||||
import org.jivesoftware.smack.roster.RosterLoadedListener;
|
||||
import org.jivesoftware.smack.roster.SubscribeListener;
|
||||
import org.jivesoftware.smack.roster.rosterstore.RosterStore;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.mercury_im.messenger.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
import org.mercury_im.messenger.store.MercuryRosterStore;
|
||||
import org.mercury_im.messenger.xmpp.MercuryConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -16,6 +31,8 @@ public class RosterStoreBinder {
|
|||
private final AccountRepository accountRepository;
|
||||
private final PeerRepository peerRepository;
|
||||
|
||||
private final Logger LOGGER = Logger.getLogger(RosterStoreBinder.class.getName());
|
||||
|
||||
@Inject
|
||||
public RosterStoreBinder(AccountRepository accountRepository, PeerRepository peerRepository) {
|
||||
this.accountRepository = accountRepository;
|
||||
|
@ -27,6 +44,75 @@ public class RosterStoreBinder {
|
|||
createRosterStore(connection.getAccount().getId(), accountRepository, peerRepository);
|
||||
Roster roster = Roster.getInstanceFor(connection.getConnection());
|
||||
roster.setRosterStore(store);
|
||||
roster.addSubscribeListener(new SubscribeListener() {
|
||||
@Override
|
||||
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
|
||||
RosterEntry entry = roster.getEntry(from.asBareJid());
|
||||
if (entry != null) {
|
||||
return SubscribeAnswer.ApproveAndAlsoRequestIfRequired;
|
||||
}
|
||||
LOGGER.log(Level.INFO, "processSubscribe " + from);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
roster.addPresenceEventListener(new PresenceEventListener() {
|
||||
@Override
|
||||
public void presenceAvailable(FullJid address, Presence availablePresence) {
|
||||
LOGGER.log(Level.INFO, "presenceAvailable " + address.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnavailable(FullJid address, Presence presence) {
|
||||
LOGGER.log(Level.INFO, "presenceUnavailable " + address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceError(Jid address, Presence errorPresence) {
|
||||
LOGGER.log(Level.INFO, "presenceError " + address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
|
||||
LOGGER.log(Level.INFO, "presenceSubscribed " + address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) {
|
||||
LOGGER.log(Level.INFO, "presenceUnsubscribed " + address);
|
||||
}
|
||||
});
|
||||
roster.addRosterListener(new RosterListener() {
|
||||
@Override
|
||||
public void entriesAdded(Collection<Jid> addresses) {
|
||||
LOGGER.log(Level.INFO, "entriesAdded " + Arrays.toString(addresses.toArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entriesUpdated(Collection<Jid> addresses) {
|
||||
LOGGER.log(Level.INFO, "entriesUpdated " + Arrays.toString(addresses.toArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entriesDeleted(Collection<Jid> addresses) {
|
||||
LOGGER.log(Level.INFO, "entriesDeleted " + Arrays.toString(addresses.toArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceChanged(Presence presence) {
|
||||
LOGGER.log(Level.INFO, "presenceChanged " + presence.toString());
|
||||
}
|
||||
});
|
||||
roster.addRosterLoadedListener(new RosterLoadedListener() {
|
||||
@Override
|
||||
public void onRosterLoaded(Roster roster) {
|
||||
LOGGER.log(Level.INFO, "onRosterLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRosterLoadingFailed(Exception exception) {
|
||||
LOGGER.log(Level.INFO, "onRosterLoadingFailed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private MercuryRosterStore createRosterStore(UUID accountId, AccountRepository accountRepository, PeerRepository peerRepository) {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package org.mercury_im.messenger.util;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.roster.PresenceEventListener;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
public abstract class CombinedPresenceListener implements PresenceEventListener {
|
||||
@Override
|
||||
public void presenceAvailable(FullJid address, Presence availablePresence) {
|
||||
presenceReceived(address, availablePresence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnavailable(FullJid address, Presence presence) {
|
||||
presenceReceived(address, presence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceError(Jid address, Presence errorPresence) {
|
||||
presenceReceived(address, errorPresence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
|
||||
presenceReceived(address, subscribedPresence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) {
|
||||
presenceReceived(address, unsubscribedPresence);
|
||||
}
|
||||
|
||||
public abstract void presenceReceived(Jid address, Presence presence);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger.xmpp;
|
||||
|
||||
import org.jivesoftware.smack.ReconnectionManager;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smackx.carbons.CarbonManager;
|
||||
import org.jivesoftware.smackx.iqversion.VersionManager;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
|
@ -21,5 +22,7 @@ public class SmackConfig {
|
|||
StableUniqueStanzaIdManager.setEnabledByDefault(true);
|
||||
|
||||
CarbonManager.setEnabledByDefault(true);
|
||||
|
||||
Roster.setRosterLoadedAtLoginDefault(true);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue