ConferenceDetailsActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.PendingIntent;
  4import android.content.Context;
  5import android.content.Intent;
  6import android.content.IntentSender.SendIntentException;
  7import android.content.res.Resources;
  8import android.databinding.DataBindingUtil;
  9import android.graphics.Bitmap;
 10import android.graphics.drawable.BitmapDrawable;
 11import android.graphics.drawable.Drawable;
 12import android.os.AsyncTask;
 13import android.os.Bundle;
 14import android.support.v7.app.AlertDialog;
 15import android.support.v7.widget.Toolbar;
 16import android.text.SpannableStringBuilder;
 17import android.util.Log;
 18import android.view.ContextMenu;
 19import android.view.LayoutInflater;
 20import android.view.Menu;
 21import android.view.MenuItem;
 22import android.view.View;
 23import android.view.View.OnClickListener;
 24import android.widget.ImageView;
 25import android.widget.Toast;
 26
 27import org.openintents.openpgp.util.OpenPgpUtils;
 28
 29import java.lang.ref.WeakReference;
 30import java.util.ArrayList;
 31import java.util.Collections;
 32import java.util.concurrent.RejectedExecutionException;
 33import java.util.concurrent.atomic.AtomicInteger;
 34
 35import eu.siacs.conversations.Config;
 36import eu.siacs.conversations.R;
 37import eu.siacs.conversations.crypto.PgpEngine;
 38import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
 39import eu.siacs.conversations.databinding.ContactBinding;
 40import eu.siacs.conversations.entities.Account;
 41import eu.siacs.conversations.entities.Bookmark;
 42import eu.siacs.conversations.entities.Contact;
 43import eu.siacs.conversations.entities.Conversation;
 44import eu.siacs.conversations.entities.MucOptions;
 45import eu.siacs.conversations.entities.MucOptions.User;
 46import eu.siacs.conversations.services.XmppConnectionService;
 47import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
 48import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
 49import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
 50import eu.siacs.conversations.ui.util.MyLinkify;
 51import eu.siacs.conversations.utils.UIHelper;
 52import eu.siacs.conversations.utils.XmppUri;
 53import rocks.xmpp.addr.Jid;
 54
 55import static eu.siacs.conversations.entities.Bookmark.printableValue;
 56
 57public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed {
 58    public static final String ACTION_VIEW_MUC = "view_muc";
 59
 60    private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
 61
 62    private Conversation mConversation;
 63    private OnClickListener inviteListener = new OnClickListener() {
 64
 65        @Override
 66        public void onClick(View v) {
 67            inviteToConversation(mConversation);
 68        }
 69    };
 70    private ActivityMucDetailsBinding binding;
 71    private String uuid = null;
 72    private User mSelectedUser = null;
 73
 74    private boolean mAdvancedMode = false;
 75
 76    private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
 77        @Override
 78        public void success(Conversation object) {
 79            runOnUiThread(() -> {
 80                Toast.makeText(ConferenceDetailsActivity.this, getString(R.string.your_nick_has_been_changed), Toast.LENGTH_SHORT).show();
 81                updateView();
 82            });
 83
 84        }
 85
 86        @Override
 87        public void error(final int errorCode, Conversation object) {
 88            runOnUiThread(() -> Toast.makeText(ConferenceDetailsActivity.this, getString(errorCode), Toast.LENGTH_SHORT).show());
 89        }
 90
 91        @Override
 92        public void userInputRequried(PendingIntent pi, Conversation object) {
 93
 94        }
 95    };
 96
 97    private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
 98        @Override
 99        public void onClick(View v) {
100            AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
101            builder.setTitle(R.string.pref_notification_settings);
102            String[] choices = {
103                    getString(R.string.notify_on_all_messages),
104                    getString(R.string.notify_only_when_highlighted),
105                    getString(R.string.notify_never)
106            };
107            final AtomicInteger choice;
108            if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0) == Long.MAX_VALUE) {
109                choice = new AtomicInteger(2);
110            } else {
111                choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
112            }
113            builder.setSingleChoiceItems(choices, choice.get(), (dialog, which) -> choice.set(which));
114            builder.setNegativeButton(R.string.cancel, null);
115            builder.setPositiveButton(R.string.ok, (dialog, which) -> {
116                if (choice.get() == 2) {
117                    mConversation.setMutedTill(Long.MAX_VALUE);
118                } else {
119                    mConversation.setMutedTill(0);
120                    mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY, String.valueOf(choice.get() == 0));
121                }
122                xmppConnectionService.updateConversation(mConversation);
123                updateView();
124            });
125            builder.create().show();
126        }
127    };
128
129    private OnClickListener mChangeConferenceSettings = new OnClickListener() {
130        @Override
131        public void onClick(View v) {
132            final MucOptions mucOptions = mConversation.getMucOptions();
133            AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
134            builder.setTitle(R.string.conference_options);
135            final String[] options;
136            final boolean[] values;
137            if (mAdvancedMode) {
138                options = new String[]{
139                        getString(R.string.members_only),
140                        getString(R.string.moderated),
141                        getString(R.string.non_anonymous)
142                };
143                values = new boolean[]{
144                        mucOptions.membersOnly(),
145                        mucOptions.moderated(),
146                        mucOptions.nonanonymous()
147                };
148            } else {
149                options = new String[]{
150                        getString(R.string.members_only),
151                        getString(R.string.non_anonymous)
152                };
153                values = new boolean[]{
154                        mucOptions.membersOnly(),
155                        mucOptions.nonanonymous()
156                };
157            }
158            builder.setMultiChoiceItems(options, values, (dialog, which, isChecked) -> values[which] = isChecked);
159            builder.setNegativeButton(R.string.cancel, null);
160            builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
161                if (!mucOptions.membersOnly() && values[0]) {
162                    xmppConnectionService.changeAffiliationsInConference(mConversation,
163                            MucOptions.Affiliation.NONE,
164                            MucOptions.Affiliation.MEMBER);
165                }
166                Bundle options1 = new Bundle();
167                options1.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
168                if (values.length == 2) {
169                    options1.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
170                } else if (values.length == 3) {
171                    options1.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
172                    options1.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
173                }
174                options1.putString("muc#roomconfig_persistentroom", "1");
175                final boolean whois = values.length == 2 ? values[1] : values[2];
176                if (values[0] == whois) {
177                    options1.putString("muc#roomconfig_publicroom", whois ? "0" : "1");
178                }
179                xmppConnectionService.pushConferenceConfiguration(mConversation,
180                        options1,
181                        ConferenceDetailsActivity.this);
182            });
183            builder.create().show();
184        }
185    };
186
187    public static boolean cancelPotentialWork(User user, ImageView imageView) {
188        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
189
190        if (bitmapWorkerTask != null) {
191            final User old = bitmapWorkerTask.o;
192            if (old == null || user != old) {
193                bitmapWorkerTask.cancel(true);
194            } else {
195                return false;
196            }
197        }
198        return true;
199    }
200
201    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
202        if (imageView != null) {
203            final Drawable drawable = imageView.getDrawable();
204            if (drawable instanceof AsyncDrawable) {
205                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
206                return asyncDrawable.getBitmapWorkerTask();
207            }
208        }
209        return null;
210    }
211
212    @Override
213    public void onConversationUpdate() {
214        refreshUi();
215    }
216
217    @Override
218    public void onMucRosterUpdate() {
219        refreshUi();
220    }
221
222    @Override
223    protected void refreshUiReal() {
224        updateView();
225    }
226
227    @Override
228    protected void onCreate(Bundle savedInstanceState) {
229        super.onCreate(savedInstanceState);
230        this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details);
231        this.binding.mucMoreDetails.setVisibility(View.GONE);
232        this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings);
233        this.binding.invite.setOnClickListener(inviteListener);
234        setSupportActionBar((Toolbar) binding.toolbar);
235        configureActionBar(getSupportActionBar());
236        this.binding.editNickButton.setOnClickListener(v -> quickEdit(mConversation.getMucOptions().getActualNick(),
237                R.string.nickname,
238                value -> {
239                    if (xmppConnectionService.renameInMuc(mConversation, value, renameCallback)) {
240                        return null;
241                    } else {
242                        return getString(R.string.invalid_muc_nick);
243                    }
244                }));
245        this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
246        this.binding.mucInfoMore.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
247        this.binding.notificationStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
248        this.binding.yourPhoto.setOnClickListener(v -> {
249            final MucOptions mucOptions = mConversation.getMucOptions();
250            if (!mucOptions.hasVCards()) {
251                Toast.makeText(this, R.string.host_does_not_support_group_chat_avatars, Toast.LENGTH_SHORT).show();
252                return;
253            }
254            if (!mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
255                Toast.makeText(this, R.string.only_the_owner_can_change_group_chat_avatar, Toast.LENGTH_SHORT).show();
256                return;
257            }
258            final Intent intent = new Intent(this, PublishGroupChatProfilePictureActivity.class);
259            intent.putExtra("uuid", mConversation.getUuid());
260            startActivity(intent);
261        });
262        this.binding.editMucNameButton.setOnClickListener(this::onMucEditButtonClicked);
263    }
264
265    @Override
266    protected void onStart() {
267        super.onStart();
268        final int theme = findTheme();
269        if (this.mTheme != theme) {
270            recreate();
271        }
272    }
273
274    @Override
275    public boolean onOptionsItemSelected(MenuItem menuItem) {
276        if (MenuDoubleTabUtil.shouldIgnoreTap()) {
277            return false;
278        }
279        switch (menuItem.getItemId()) {
280            case android.R.id.home:
281                finish();
282                break;
283            case R.id.action_share_http:
284                shareLink(true);
285                break;
286            case R.id.action_share_uri:
287                shareLink(false);
288                break;
289            case R.id.action_save_as_bookmark:
290                saveAsBookmark();
291                break;
292            case R.id.action_delete_bookmark:
293                deleteBookmark();
294                break;
295            case R.id.action_advanced_mode:
296                this.mAdvancedMode = !menuItem.isChecked();
297                menuItem.setChecked(this.mAdvancedMode);
298                getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).apply();
299                final boolean online = mConversation != null && mConversation.getMucOptions().online();
300                this.binding.mucInfoMore.setVisibility(this.mAdvancedMode && online ? View.VISIBLE : View.GONE);
301                invalidateOptionsMenu();
302                updateView();
303                break;
304        }
305        return super.onOptionsItemSelected(menuItem);
306    }
307
308    public void onMucEditButtonClicked(View v) {
309        if (this.binding.mucEditor.getVisibility() == View.GONE) {
310            final MucOptions mucOptions = mConversation.getMucOptions();
311            this.binding.mucEditor.setVisibility(View.VISIBLE);
312            this.binding.mucDisplay.setVisibility(View.GONE);
313            this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_save, R.drawable.ic_save_black_24dp));
314            final String name = mucOptions.getName();
315            this.binding.mucEditTitle.setText("");
316            final boolean owner = mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER);
317            if (owner || printableValue(name)) {
318                this.binding.mucEditTitle.setVisibility(View.VISIBLE);
319                if (name != null) {
320                    this.binding.mucEditTitle.append(name);
321                }
322            } else {
323                this.binding.mucEditTitle.setVisibility(View.GONE);
324            }
325            this.binding.mucEditTitle.setEnabled(owner);
326            final String subject = mucOptions.getSubject();
327            this.binding.mucEditSubject.setText("");
328            if (subject != null) {
329                this.binding.mucEditSubject.append(subject);
330            }
331            this.binding.mucEditSubject.setEnabled(mucOptions.canChangeSubject());
332            if (!owner) {
333                this.binding.mucEditSubject.requestFocus();
334            }
335            this.binding.yourPhoto.setVisibility(View.GONE);
336        } else {
337            String subject = this.binding.mucEditSubject.isEnabled() ? this.binding.mucEditSubject.getEditableText().toString().trim() : null;
338            String name = this.binding.mucEditTitle.isEnabled() ? this.binding.mucEditTitle.getEditableText().toString().trim() : null;
339            onMucInfoUpdated(subject, name);
340            hideEditor();
341        }
342    }
343
344    private void hideEditor() {
345        this.binding.mucEditor.setVisibility(View.GONE);
346        this.binding.mucDisplay.setVisibility(View.VISIBLE);
347        this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_edit_body, R.drawable.ic_edit_black_24dp));
348        this.binding.yourPhoto.setVisibility(View.VISIBLE);
349    }
350
351    private void onMucInfoUpdated(String subject, String name) {
352        final MucOptions mucOptions = mConversation.getMucOptions();
353        if (mucOptions.canChangeSubject() && !blankOnNull(mucOptions.getSubject()).equals(subject)) {
354            Log.d(Config.LOGTAG,"subject changed");
355            xmppConnectionService.pushSubjectToConference(mConversation, subject);
356        }
357        if (mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER) && !blankOnNull(mucOptions.getName()).equals(name)) {
358            Log.d(Config.LOGTAG,"name changed");
359            Bundle options = new Bundle();
360            options.putString("muc#roomconfig_persistentroom", "1");
361            options.putString("muc#roomconfig_roomname", name);
362            xmppConnectionService.pushConferenceConfiguration(mConversation, options, null);
363        }
364    }
365
366    private static String blankOnNull(String input) {
367        return input == null ? "" : input;
368    }
369
370    @Override
371    protected String getShareableUri(boolean http) {
372        if (mConversation != null) {
373            if (http) {
374                return "https://conversations.im/j/" + XmppUri.lameUrlEncode(mConversation.getJid().asBareJid().toEscapedString());
375            } else {
376                return "xmpp:" + mConversation.getJid().asBareJid() + "?join";
377            }
378        } else {
379            return null;
380        }
381    }
382
383    @Override
384    public boolean onPrepareOptionsMenu(Menu menu) {
385        MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
386        MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
387        MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
388        menuItemAdvancedMode.setChecked(mAdvancedMode);
389        if (mConversation == null) {
390            return true;
391        }
392        if (mConversation.getBookmark() != null) {
393            menuItemSaveBookmark.setVisible(false);
394            menuItemDeleteBookmark.setVisible(true);
395        } else {
396            menuItemDeleteBookmark.setVisible(false);
397            menuItemSaveBookmark.setVisible(true);
398        }
399        return true;
400    }
401
402    @Override
403    public boolean onCreateOptionsMenu(Menu menu) {
404        getMenuInflater().inflate(R.menu.muc_details, menu);
405        return super.onCreateOptionsMenu(menu);
406    }
407
408    @Override
409    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
410        Object tag = v.getTag();
411        if (tag instanceof User) {
412            getMenuInflater().inflate(R.menu.muc_details_context, menu);
413            final User user = (User) tag;
414            final User self = mConversation.getMucOptions().getSelf();
415            this.mSelectedUser = user;
416            String name;
417            final Contact contact = user.getContact();
418            if (contact != null && contact.showInRoster()) {
419                name = contact.getDisplayName();
420            } else if (user.getRealJid() != null) {
421                name = user.getRealJid().asBareJid().toString();
422            } else {
423                name = user.getName();
424            }
425            menu.setHeaderTitle(name);
426            if (user.getRealJid() != null) {
427                MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
428                MenuItem startConversation = menu.findItem(R.id.start_conversation);
429                MenuItem giveMembership = menu.findItem(R.id.give_membership);
430                MenuItem removeMembership = menu.findItem(R.id.remove_membership);
431                MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
432                MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
433                MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
434                MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
435                MenuItem invite = menu.findItem(R.id.invite);
436                startConversation.setVisible(true);
437                if (contact != null && contact.showInRoster()) {
438                    showContactDetails.setVisible(!contact.isSelf());
439                }
440                if (user.getRole() == MucOptions.Role.NONE) {
441                    invite.setVisible(true);
442                }
443                if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
444                        self.getAffiliation().outranks(user.getAffiliation())) {
445                    if (mAdvancedMode) {
446                        if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
447                            giveMembership.setVisible(true);
448                        } else {
449                            removeMembership.setVisible(true);
450                        }
451                        if (!Config.DISABLE_BAN) {
452                            banFromConference.setVisible(true);
453                        }
454                    } else {
455                        if (!Config.DISABLE_BAN || mConversation.getMucOptions().membersOnly()) {
456                            removeFromRoom.setVisible(true);
457                        }
458                    }
459                    if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
460                        giveAdminPrivileges.setVisible(true);
461                    } else {
462                        removeAdminPrivileges.setVisible(true);
463                    }
464                }
465            } else {
466                MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
467                sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.VISITOR));
468            }
469
470        }
471        super.onCreateContextMenu(menu, v, menuInfo);
472    }
473
474    @Override
475    public boolean onContextItemSelected(MenuItem item) {
476        Jid jid = mSelectedUser.getRealJid();
477        switch (item.getItemId()) {
478            case R.id.action_contact_details:
479                Contact contact = mSelectedUser.getContact();
480                if (contact != null) {
481                    switchToContactDetails(contact);
482                }
483                return true;
484            case R.id.start_conversation:
485                startConversation(mSelectedUser);
486                return true;
487            case R.id.give_admin_privileges:
488                xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN, this);
489                return true;
490            case R.id.give_membership:
491                xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
492                return true;
493            case R.id.remove_membership:
494                xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE, this);
495                return true;
496            case R.id.remove_admin_privileges:
497                xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
498                return true;
499            case R.id.remove_from_room:
500                removeFromRoom(mSelectedUser);
501                return true;
502            case R.id.ban_from_conference:
503                xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.OUTCAST, this);
504                if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
505                    xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
506                }
507                return true;
508            case R.id.send_private_message:
509                if (mConversation.getMucOptions().allowPm()) {
510                    privateMsgInMuc(mConversation, mSelectedUser.getName());
511                } else {
512                    Toast.makeText(this, R.string.private_messages_are_disabled, Toast.LENGTH_SHORT).show();
513                }
514                return true;
515            case R.id.invite:
516                xmppConnectionService.directInvite(mConversation, jid);
517                return true;
518            default:
519                return super.onContextItemSelected(item);
520        }
521    }
522
523    private void removeFromRoom(final User user) {
524        if (mConversation.getMucOptions().membersOnly()) {
525            xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.NONE, this);
526            if (user.getRole() != MucOptions.Role.NONE) {
527                xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
528            }
529        } else {
530            AlertDialog.Builder builder = new AlertDialog.Builder(this);
531            builder.setTitle(R.string.ban_from_conference);
532            builder.setMessage(getString(R.string.removing_from_public_conference, user.getName()));
533            builder.setNegativeButton(R.string.cancel, null);
534            builder.setPositiveButton(R.string.ban_now, (dialog, which) -> {
535                xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.OUTCAST, ConferenceDetailsActivity.this);
536                if (user.getRole() != MucOptions.Role.NONE) {
537                    xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
538                }
539            });
540            builder.create().show();
541        }
542    }
543
544    protected void startConversation(User user) {
545        if (user.getRealJid() != null) {
546            Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(), user.getRealJid().asBareJid(), false, true);
547            switchToConversation(conversation);
548        }
549    }
550
551    protected void saveAsBookmark() {
552        xmppConnectionService.saveConversationAsBookmark(mConversation,
553                mConversation.getMucOptions().getSubject());
554    }
555
556    protected void deleteBookmark() {
557        Account account = mConversation.getAccount();
558        Bookmark bookmark = mConversation.getBookmark();
559        account.getBookmarks().remove(bookmark);
560        bookmark.setConversation(null);
561        xmppConnectionService.pushBookmarks(account);
562        updateView();
563    }
564
565    @Override
566    void onBackendConnected() {
567        if (mPendingConferenceInvite != null) {
568            mPendingConferenceInvite.execute(this);
569            mPendingConferenceInvite = null;
570        }
571        if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
572            this.uuid = getIntent().getExtras().getString("uuid");
573        }
574        if (uuid != null) {
575            this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
576            if (this.mConversation != null) {
577                updateView();
578            }
579        }
580    }
581
582    @Override
583    public void onBackPressed() {
584        if (this.binding.mucEditor.getVisibility() == View.VISIBLE) {
585            hideEditor();
586        } else {
587            super.onBackPressed();
588        }
589    }
590
591    private void updateView() {
592        invalidateOptionsMenu();
593        final MucOptions mucOptions = mConversation.getMucOptions();
594        final User self = mucOptions.getSelf();
595        String account;
596        if (Config.DOMAIN_LOCK != null) {
597            account = mConversation.getAccount().getJid().getLocal();
598        } else {
599            account = mConversation.getAccount().getJid().asBareJid().toString();
600        }
601        this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE);
602        this.binding.detailsAccount.setText(getString(R.string.using_account, account));
603        this.binding.jid.setText(mConversation.getJid().asBareJid().toEscapedString());
604        this.binding.yourPhoto.setImageBitmap(avatarService().get(mConversation, getPixel(72)));
605        String roomName = mucOptions.getName();
606        String subject = mucOptions.getSubject();
607        if (printableValue(roomName)) {
608            this.binding.mucTitle.setText(roomName);
609            this.binding.mucTitle.setVisibility(View.VISIBLE);
610        } else if (!printableValue(subject)) {
611            this.binding.mucTitle.setText(mConversation.getName());
612            this.binding.mucTitle.setVisibility(View.VISIBLE);
613        } else {
614            this.binding.mucTitle.setVisibility(View.GONE);
615        }
616        if (printableValue(subject)) {
617            SpannableStringBuilder spannable = new SpannableStringBuilder(subject);
618            MyLinkify.addLinks(spannable, false);
619            this.binding.mucSubject.setText(spannable);
620            this.binding.mucSubject.setAutoLinkMask(0);
621            this.binding.mucSubject.setVisibility(View.VISIBLE);
622        } else {
623            this.binding.mucSubject.setVisibility(View.GONE);
624        }
625        this.binding.mucYourNick.setText(mucOptions.getActualNick());
626        if (mucOptions.online()) {
627            this.binding.mucMoreDetails.setVisibility(View.VISIBLE);
628            this.binding.mucSettings.setVisibility(View.VISIBLE);
629            this.binding.mucInfoMore.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
630            this.binding.mucRole.setVisibility(View.VISIBLE);
631            this.binding.mucRole.setText(getStatus(self));
632            if (mucOptions.membersOnly()) {
633                this.binding.mucConferenceType.setText(R.string.private_conference);
634            } else {
635                this.binding.mucConferenceType.setText(R.string.public_conference);
636            }
637            if (mucOptions.mamSupport()) {
638                this.binding.mucInfoMam.setText(R.string.server_info_available);
639            } else {
640                this.binding.mucInfoMam.setText(R.string.server_info_unavailable);
641            }
642            if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
643                this.binding.changeConferenceButton.setVisibility(View.VISIBLE);
644            } else {
645                this.binding.changeConferenceButton.setVisibility(View.INVISIBLE);
646            }
647        } else {
648            this.binding.mucMoreDetails.setVisibility(View.GONE);
649            this.binding.mucInfoMore.setVisibility(View.GONE);
650            this.binding.mucSettings.setVisibility(View.GONE);
651        }
652
653        int ic_notifications = getThemeResource(R.attr.icon_notifications, R.drawable.ic_notifications_black_24dp);
654        int ic_notifications_off = getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
655        int ic_notifications_paused = getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
656        int ic_notifications_none = getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
657
658        long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
659        if (mutedTill == Long.MAX_VALUE) {
660            this.binding.notificationStatusText.setText(R.string.notify_never);
661            this.binding.notificationStatusButton.setImageResource(ic_notifications_off);
662        } else if (System.currentTimeMillis() < mutedTill) {
663            this.binding.notificationStatusText.setText(R.string.notify_paused);
664            this.binding.notificationStatusButton.setImageResource(ic_notifications_paused);
665        } else if (mConversation.alwaysNotify()) {
666            this.binding.notificationStatusText.setText(R.string.notify_on_all_messages);
667            this.binding.notificationStatusButton.setImageResource(ic_notifications);
668        } else {
669            this.binding.notificationStatusText.setText(R.string.notify_only_when_highlighted);
670            this.binding.notificationStatusButton.setImageResource(ic_notifications_none);
671        }
672
673        final LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
674        this.binding.mucMembers.removeAllViews();
675        if (inflater == null) {
676            return;
677        }
678        final ArrayList<User> users = mucOptions.getUsers();
679        Collections.sort(users);
680        for (final User user : users) {
681            ContactBinding binding = DataBindingUtil.inflate(inflater, R.layout.contact, this.binding.mucMembers, false);
682            this.setListItemBackgroundOnView(binding.getRoot());
683            binding.getRoot().setOnClickListener(view1 -> highlightInMuc(mConversation, user.getName()));
684            registerForContextMenu(binding.getRoot());
685            binding.getRoot().setTag(user);
686            if (mAdvancedMode && user.getPgpKeyId() != 0) {
687                binding.key.setVisibility(View.VISIBLE);
688                binding.key.setOnClickListener(v -> viewPgpKey(user));
689                binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
690            }
691            Contact contact = user.getContact();
692            String name = user.getName();
693            if (contact != null) {
694                binding.contactDisplayName.setText(contact.getDisplayName());
695                binding.contactJid.setText((name != null ? name + " \u2022 " : "") + getStatus(user));
696            } else {
697                binding.contactDisplayName.setText(name == null ? "" : name);
698                binding.contactJid.setText(getStatus(user));
699
700            }
701            loadAvatar(user, binding.contactPhoto);
702            if (user.getRole() == MucOptions.Role.NONE) {
703                binding.contactJid.setAlpha(INACTIVE_ALPHA);
704                binding.key.setAlpha(INACTIVE_ALPHA);
705                binding.contactDisplayName.setAlpha(INACTIVE_ALPHA);
706                binding.contactPhoto.setAlpha(INACTIVE_ALPHA);
707            }
708            this.binding.mucMembers.addView(binding.getRoot());
709            if (mConversation.getMucOptions().canInvite()) {
710                this.binding.invite.setVisibility(View.VISIBLE);
711            } else {
712                this.binding.invite.setVisibility(View.GONE);
713            }
714        }
715    }
716
717    private String getStatus(User user) {
718        if (mAdvancedMode) {
719            return getString(user.getAffiliation().getResId()) +
720                    " (" + getString(user.getRole().getResId()) + ')';
721        } else {
722            return getString(user.getAffiliation().getResId());
723        }
724    }
725
726    private void viewPgpKey(User user) {
727        PgpEngine pgp = xmppConnectionService.getPgpEngine();
728        if (pgp != null) {
729            PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
730            if (intent != null) {
731                try {
732                    startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
733                } catch (SendIntentException ignored) {
734
735                }
736            }
737        }
738    }
739
740    @Override
741    public void onAffiliationChangedSuccessful(Jid jid) {
742        refreshUi();
743    }
744
745    @Override
746    public void onAffiliationChangeFailed(Jid jid, int resId) {
747        displayToast(getString(resId, jid.asBareJid().toString()));
748    }
749
750    @Override
751    public void onRoleChangedSuccessful(String nick) {
752
753    }
754
755    @Override
756    public void onRoleChangeFailed(String nick, int resId) {
757        displayToast(getString(resId, nick));
758    }
759
760    @Override
761    public void onPushSucceeded() {
762        displayToast(getString(R.string.modified_conference_options));
763    }
764
765    @Override
766    public void onPushFailed() {
767        displayToast(getString(R.string.could_not_modify_conference_options));
768    }
769
770    private void displayToast(final String msg) {
771        runOnUiThread(() -> Toast.makeText(ConferenceDetailsActivity.this, msg, Toast.LENGTH_SHORT).show());
772    }
773
774    public void loadAvatar(User user, ImageView imageView) {
775        if (cancelPotentialWork(user, imageView)) {
776            final Bitmap bm = avatarService().get(user, getPixel(48), true);
777            if (bm != null) {
778                cancelPotentialWork(user, imageView);
779                imageView.setImageBitmap(bm);
780                imageView.setBackgroundColor(0x00000000);
781            } else {
782                String seed = user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null;
783                imageView.setBackgroundColor(UIHelper.getColorForName(seed == null ? user.getName() : seed));
784                imageView.setImageDrawable(null);
785                final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
786                final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), null, task);
787                imageView.setImageDrawable(asyncDrawable);
788                try {
789                    task.execute(user);
790                } catch (final RejectedExecutionException ignored) {
791                }
792            }
793        }
794    }
795
796    static class AsyncDrawable extends BitmapDrawable {
797        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
798
799        AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
800            super(res, bitmap);
801            bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
802        }
803
804        BitmapWorkerTask getBitmapWorkerTask() {
805            return bitmapWorkerTaskReference.get();
806        }
807    }
808
809    class BitmapWorkerTask extends AsyncTask<User, Void, Bitmap> {
810        private final WeakReference<ImageView> imageViewReference;
811        private User o = null;
812
813        private BitmapWorkerTask(ImageView imageView) {
814            imageViewReference = new WeakReference<>(imageView);
815        }
816
817        @Override
818        protected Bitmap doInBackground(User... params) {
819            this.o = params[0];
820            if (imageViewReference.get() == null) {
821                return null;
822            }
823            return avatarService().get(this.o, getPixel(48), isCancelled());
824        }
825
826        @Override
827        protected void onPostExecute(Bitmap bitmap) {
828            if (bitmap != null && !isCancelled()) {
829                final ImageView imageView = imageViewReference.get();
830                if (imageView != null) {
831                    imageView.setImageBitmap(bitmap);
832                    imageView.setBackgroundColor(0x00000000);
833                }
834            }
835        }
836    }
837
838}