1package eu.siacs.conversations.ui.util;
  2
  3import android.app.Activity;
  4import android.preference.PreferenceManager;
  5import android.text.SpannableString;
  6import android.text.Spanned;
  7import android.text.style.TypefaceSpan;
  8import android.view.ContextMenu;
  9import android.view.Menu;
 10import android.view.MenuItem;
 11import android.view.View;
 12import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 13import eu.siacs.conversations.Config;
 14import eu.siacs.conversations.R;
 15import eu.siacs.conversations.entities.Account;
 16import eu.siacs.conversations.entities.Contact;
 17import eu.siacs.conversations.entities.Conversation;
 18import eu.siacs.conversations.entities.MucOptions;
 19import eu.siacs.conversations.entities.MucOptions.User;
 20import eu.siacs.conversations.services.XmppConnectionService;
 21import eu.siacs.conversations.ui.ConferenceDetailsActivity;
 22import eu.siacs.conversations.ui.ConversationFragment;
 23import eu.siacs.conversations.ui.ConversationsActivity;
 24import eu.siacs.conversations.ui.MucUsersActivity;
 25import eu.siacs.conversations.ui.XmppActivity;
 26import eu.siacs.conversations.xmpp.Jid;
 27import im.conversations.android.xmpp.model.muc.Affiliation;
 28import im.conversations.android.xmpp.model.muc.Role;
 29
 30public final class MucDetailsContextMenuHelper {
 31
 32    public static void onCreateContextMenu(ContextMenu menu, View v) {
 33        final XmppActivity activity = XmppActivity.find(v);
 34        final Object tag = v.getTag();
 35        if (tag instanceof User user && activity != null) {
 36            activity.getMenuInflater().inflate(R.menu.muc_details_context, menu);
 37            String name;
 38            final Contact contact = user.getContact();
 39            if (contact != null && contact.showInContactList()) {
 40                name = contact.getDisplayName();
 41            } else if (user.getRealJid() != null) {
 42                name = user.getRealJid().asBareJid().toString();
 43            } else {
 44                name = user.getName();
 45            }
 46            menu.setHeaderTitle(name);
 47            MucDetailsContextMenuHelper.configureMucDetailsContextMenu(
 48                    activity, menu, user.getConversation(), user);
 49        }
 50    }
 51
 52    public static void configureMucDetailsContextMenu(
 53            Activity activity, Menu menu, Conversation conversation, User user) {
 54        final MucOptions mucOptions = conversation.getMucOptions();
 55        final boolean advancedMode =
 56                PreferenceManager.getDefaultSharedPreferences(activity)
 57                        .getBoolean("advanced_muc_mode", false);
 58        final boolean isGroupChat = mucOptions.isPrivateAndNonAnonymous();
 59        MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
 60        if (user != null && user.getRealJid() != null) {
 61            MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
 62            MenuItem startConversation = menu.findItem(R.id.start_conversation);
 63            MenuItem giveMembership = menu.findItem(R.id.give_membership);
 64            MenuItem removeMembership = menu.findItem(R.id.remove_membership);
 65            MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
 66            MenuItem giveOwnerPrivileges = menu.findItem(R.id.give_owner_privileges);
 67            MenuItem removeOwnerPrivileges = menu.findItem(R.id.revoke_owner_privileges);
 68            MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
 69            MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
 70            MenuItem managePermissions = menu.findItem(R.id.manage_permissions);
 71            removeFromRoom.setTitle(
 72                    isGroupChat ? R.string.remove_from_room : R.string.remove_from_channel);
 73            MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
 74            banFromConference.setTitle(
 75                    isGroupChat ? R.string.ban_from_conference : R.string.ban_from_channel);
 76            MenuItem invite = menu.findItem(R.id.invite);
 77            startConversation.setVisible(true);
 78            final Contact contact = user.getContact();
 79            final User self = conversation.getMucOptions().getSelf();
 80            if ((contact != null && contact.showInRoster())
 81                    || mucOptions.isPrivateAndNonAnonymous()) {
 82                showContactDetails.setVisible(contact == null || !contact.isSelf());
 83            }
 84            if ((activity instanceof ConferenceDetailsActivity
 85                            || activity instanceof MucUsersActivity)
 86                    && user.getRole() == Role.NONE) {
 87                invite.setVisible(true);
 88            }
 89            boolean managePermissionsVisible = false;
 90            if ((self.ranks(Affiliation.ADMIN) && self.outranks(user.getAffiliation()))
 91                    || self.getAffiliation() == Affiliation.OWNER) {
 92                if (advancedMode) {
 93                    if (!user.ranks(Affiliation.MEMBER)) {
 94                        managePermissionsVisible = true;
 95                        giveMembership.setVisible(true);
 96                    } else if (user.getAffiliation() == Affiliation.MEMBER) {
 97                        managePermissionsVisible = true;
 98                        removeMembership.setVisible(true);
 99                    }
100                    if (!Config.DISABLE_BAN) {
101                        managePermissionsVisible = true;
102                        banFromConference.setVisible(true);
103                    }
104                } else {
105                    if (!Config.DISABLE_BAN || conversation.getMucOptions().membersOnly()) {
106                        removeFromRoom.setVisible(true);
107                    }
108                }
109            }
110            if (self.ranks(Affiliation.OWNER)) {
111                if (isGroupChat || advancedMode || user.getAffiliation() == Affiliation.OWNER) {
112                    if (!user.ranks(Affiliation.OWNER)) {
113                        managePermissionsVisible = true;
114                        giveOwnerPrivileges.setVisible(true);
115                    } else if (user.getAffiliation() == Affiliation.OWNER) {
116                        managePermissionsVisible = true;
117                        removeOwnerPrivileges.setVisible(true);
118                    }
119                }
120                if (!isGroupChat || advancedMode || user.getAffiliation() == Affiliation.ADMIN) {
121                    if (!user.ranks(Affiliation.ADMIN)) {
122                        managePermissionsVisible = true;
123                        giveAdminPrivileges.setVisible(true);
124                    } else if (user.getAffiliation() == Affiliation.ADMIN) {
125                        managePermissionsVisible = true;
126                        removeAdminPrivileges.setVisible(true);
127                    }
128                }
129            }
130            managePermissions.setVisible(managePermissionsVisible);
131            sendPrivateMessage.setVisible(
132                    !isGroupChat && mucOptions.allowPm() && user.ranks(Role.VISITOR));
133        } else {
134            sendPrivateMessage.setVisible(true);
135            sendPrivateMessage.setEnabled(
136                    user != null && mucOptions.allowPm() && user.ranks(Role.VISITOR));
137        }
138    }
139
140    public static boolean onContextItemSelected(MenuItem item, User user, XmppActivity activity) {
141        return onContextItemSelected(item, user, activity, null);
142    }
143
144    public static boolean onContextItemSelected(
145            MenuItem item, User user, XmppActivity activity, final String fingerprint) {
146        final Conversation conversation = user.getConversation();
147        final XmppConnectionService.OnAffiliationChanged onAffiliationChanged =
148                activity instanceof XmppConnectionService.OnAffiliationChanged
149                        ? (XmppConnectionService.OnAffiliationChanged) activity
150                        : null;
151        Jid jid = user.getRealJid();
152        switch (item.getItemId()) {
153            case R.id.action_contact_details:
154                final Jid realJid = user.getRealJid();
155                final Account account = conversation.getAccount();
156                final Contact contact =
157                        realJid == null ? null : account.getRoster().getContact(realJid);
158                if (contact != null) {
159                    activity.switchToContactDetails(contact, fingerprint);
160                }
161                return true;
162            case R.id.start_conversation:
163                startConversation(user, activity);
164                return true;
165            case R.id.give_admin_privileges:
166                activity.xmppConnectionService.changeAffiliationInConference(
167                        conversation, jid, Affiliation.ADMIN, onAffiliationChanged);
168                return true;
169            case R.id.give_membership:
170            case R.id.remove_admin_privileges:
171            case R.id.revoke_owner_privileges:
172                activity.xmppConnectionService.changeAffiliationInConference(
173                        conversation, jid, Affiliation.MEMBER, onAffiliationChanged);
174                return true;
175            case R.id.give_owner_privileges:
176                activity.xmppConnectionService.changeAffiliationInConference(
177                        conversation, jid, Affiliation.OWNER, onAffiliationChanged);
178                return true;
179            case R.id.remove_membership:
180                activity.xmppConnectionService.changeAffiliationInConference(
181                        conversation, jid, Affiliation.NONE, onAffiliationChanged);
182                return true;
183            case R.id.remove_from_room:
184                removeFromRoom(user, activity, onAffiliationChanged);
185                return true;
186            case R.id.ban_from_conference:
187                activity.xmppConnectionService.changeAffiliationInConference(
188                        conversation, jid, Affiliation.OUTCAST, onAffiliationChanged);
189                if (user.getRole() != Role.NONE) {
190                    activity.xmppConnectionService.changeRoleInConference(
191                            conversation, user.getName(), Role.NONE);
192                }
193                return true;
194            case R.id.send_private_message:
195                if (activity instanceof ConversationsActivity) {
196                    ConversationFragment conversationFragment = ConversationFragment.get(activity);
197                    if (conversationFragment != null) {
198                        conversationFragment.privateMessageWith(user.getFullJid());
199                        return true;
200                    }
201                }
202                activity.privateMsgInMuc(conversation, user.getName());
203                return true;
204            case R.id.invite:
205                // TODO use direct invites for public conferences
206                if (user.ranks(Affiliation.MEMBER)) {
207                    activity.xmppConnectionService.directInvite(conversation, jid.asBareJid());
208                } else {
209                    activity.xmppConnectionService.invite(conversation, jid);
210                }
211                return true;
212            default:
213                return false;
214        }
215    }
216
217    private static void removeFromRoom(
218            final User user,
219            XmppActivity activity,
220            XmppConnectionService.OnAffiliationChanged onAffiliationChanged) {
221        final Conversation conversation = user.getConversation();
222        if (conversation.getMucOptions().membersOnly()) {
223            activity.xmppConnectionService.changeAffiliationInConference(
224                    conversation, user.getRealJid(), Affiliation.NONE, onAffiliationChanged);
225            if (user.getRole() != Role.NONE) {
226                activity.xmppConnectionService.changeRoleInConference(
227                        conversation, user.getName(), Role.NONE);
228            }
229        } else {
230            final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
231            builder.setTitle(R.string.ban_from_conference);
232            String jid = user.getRealJid().asBareJid().toString();
233            SpannableString message =
234                    new SpannableString(
235                            activity.getString(R.string.removing_from_public_conference, jid));
236            int start = message.toString().indexOf(jid);
237            if (start >= 0) {
238                message.setSpan(
239                        new TypefaceSpan("monospace"),
240                        start,
241                        start + jid.length(),
242                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
243            }
244            builder.setMessage(message);
245            builder.setNegativeButton(R.string.cancel, null);
246            builder.setPositiveButton(
247                    R.string.ban_now,
248                    (dialog, which) -> {
249                        activity.xmppConnectionService.changeAffiliationInConference(
250                                conversation,
251                                user.getRealJid(),
252                                Affiliation.OUTCAST,
253                                onAffiliationChanged);
254                        if (user.getRole() != Role.NONE) {
255                            activity.xmppConnectionService.changeRoleInConference(
256                                    conversation, user.getName(), Role.NONE);
257                        }
258                    });
259            builder.create().show();
260        }
261    }
262
263    private static void startConversation(User user, XmppActivity activity) {
264        if (user.getRealJid() != null) {
265            Conversation newConversation =
266                    activity.xmppConnectionService.findOrCreateConversation(
267                            user.getAccount(), user.getRealJid().asBareJid(), false, true);
268            activity.switchToConversation(newConversation);
269        }
270    }
271}