ConferenceDetailsActivity.java

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