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