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