ConferenceDetailsActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.annotation.TargetApi;
  4import android.app.AlertDialog;
  5import android.app.PendingIntent;
  6import android.content.Context;
  7import android.content.DialogInterface;
  8import android.content.IntentSender.SendIntentException;
  9import android.graphics.Bitmap;
 10import android.os.Build;
 11import android.os.Bundle;
 12import android.view.ContextMenu;
 13import android.view.LayoutInflater;
 14import android.view.Menu;
 15import android.view.MenuItem;
 16import android.view.View;
 17import android.view.View.OnClickListener;
 18import android.widget.Button;
 19import android.widget.ImageButton;
 20import android.widget.ImageView;
 21import android.widget.LinearLayout;
 22import android.widget.TextView;
 23import android.widget.Toast;
 24
 25import org.openintents.openpgp.util.OpenPgpUtils;
 26
 27import java.util.ArrayList;
 28import java.util.Collections;
 29import java.util.Comparator;
 30
 31import eu.siacs.conversations.R;
 32import eu.siacs.conversations.crypto.PgpEngine;
 33import eu.siacs.conversations.entities.Account;
 34import eu.siacs.conversations.entities.Bookmark;
 35import eu.siacs.conversations.entities.Contact;
 36import eu.siacs.conversations.entities.Conversation;
 37import eu.siacs.conversations.entities.MucOptions;
 38import eu.siacs.conversations.entities.MucOptions.User;
 39import eu.siacs.conversations.services.XmppConnectionService;
 40import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
 41import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
 42import eu.siacs.conversations.xmpp.jid.Jid;
 43
 44public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConferenceOptionsPushed {
 45	public static final String ACTION_VIEW_MUC = "view_muc";
 46	private Conversation mConversation;
 47	private OnClickListener inviteListener = new OnClickListener() {
 48
 49		@Override
 50		public void onClick(View v) {
 51			inviteToConversation(mConversation);
 52		}
 53	};
 54	private TextView mYourNick;
 55	private ImageView mYourPhoto;
 56	private ImageButton mEditNickButton;
 57	private TextView mRoleAffiliaton;
 58	private TextView mFullJid;
 59	private TextView mAccountJid;
 60	private LinearLayout membersView;
 61	private LinearLayout mMoreDetails;
 62	private TextView mConferenceType;
 63	private ImageButton mChangeConferenceSettingsButton;
 64	private Button mInviteButton;
 65	private String uuid = null;
 66	private User mSelectedUser = null;
 67
 68	private boolean mAdvancedMode = false;
 69
 70	private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
 71		@Override
 72		public void success(Conversation object) {
 73			runOnUiThread(new Runnable() {
 74				@Override
 75				public void run() {
 76					Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show();
 77					updateView();
 78				}
 79			});
 80
 81		}
 82
 83		@Override
 84		public void error(final int errorCode, Conversation object) {
 85			runOnUiThread(new Runnable() {
 86				@Override
 87				public void run() {
 88					Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show();
 89				}
 90			});
 91		}
 92
 93		@Override
 94		public void userInputRequried(PendingIntent pi, Conversation object) {
 95
 96		}
 97	};
 98	private OnClickListener mChangeConferenceSettings = new OnClickListener() {
 99		@Override
100		public void onClick(View v) {
101			final MucOptions mucOptions = mConversation.getMucOptions();
102			AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
103			builder.setTitle(R.string.conference_options);
104			String[] options = {getString(R.string.members_only),
105					getString(R.string.non_anonymous)};
106			final boolean[] values = new boolean[options.length];
107			values[0] = mucOptions.membersOnly();
108			values[1] = mucOptions.nonanonymous();
109			builder.setMultiChoiceItems(options,values,new DialogInterface.OnMultiChoiceClickListener() {
110				@Override
111				public void onClick(DialogInterface dialog, int which, boolean isChecked) {
112					values[which] = isChecked;
113				}
114			});
115			builder.setNegativeButton(R.string.cancel, null);
116			builder.setPositiveButton(R.string.confirm,new DialogInterface.OnClickListener() {
117				@Override
118				public void onClick(DialogInterface dialog, int which) {
119					if (!mucOptions.membersOnly() && values[0]) {
120						xmppConnectionService.changeAffiliationsInConference(mConversation,
121								MucOptions.Affiliation.NONE,
122								MucOptions.Affiliation.MEMBER);
123					}
124					Bundle options = new Bundle();
125					options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
126					options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
127					options.putString("muc#roomconfig_persistentroom", "1");
128					xmppConnectionService.pushConferenceConfiguration(mConversation,
129							options,
130							ConferenceDetailsActivity.this);
131				}
132			});
133			builder.create().show();
134		}
135	};
136	private OnValueEdited onSubjectEdited = new OnValueEdited() {
137
138		@Override
139		public void onValueEdited(String value) {
140			xmppConnectionService.pushSubjectToConference(mConversation,value);
141		}
142	};
143
144	@Override
145	public void onConversationUpdate() {
146		refreshUi();
147	}
148
149	@Override
150	public void onMucRosterUpdate() {
151		refreshUi();
152	}
153
154	@Override
155	protected void refreshUiReal() {
156		updateView();
157	}
158
159	@Override
160	protected void onCreate(Bundle savedInstanceState) {
161		super.onCreate(savedInstanceState);
162		setContentView(R.layout.activity_muc_details);
163		mYourNick = (TextView) findViewById(R.id.muc_your_nick);
164		mYourPhoto = (ImageView) findViewById(R.id.your_photo);
165		mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
166		mFullJid = (TextView) findViewById(R.id.muc_jabberid);
167		membersView = (LinearLayout) findViewById(R.id.muc_members);
168		mAccountJid = (TextView) findViewById(R.id.details_account);
169		mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
170		mMoreDetails.setVisibility(View.GONE);
171		mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
172		mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
173		mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
174		mInviteButton = (Button) findViewById(R.id.invite);
175		mInviteButton.setOnClickListener(inviteListener);
176		mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
177		if (getActionBar() != null) {
178			getActionBar().setHomeButtonEnabled(true);
179			getActionBar().setDisplayHomeAsUpEnabled(true);
180		}
181		mEditNickButton.setOnClickListener(new OnClickListener() {
182
183			@Override
184			public void onClick(View v) {
185				quickEdit(mConversation.getMucOptions().getActualNick(),
186						new OnValueEdited() {
187
188							@Override
189							public void onValueEdited(String value) {
190								xmppConnectionService.renameInMuc(mConversation,value,renameCallback);
191							}
192						});
193			}
194		});
195	}
196
197	@Override
198	public boolean onOptionsItemSelected(MenuItem menuItem) {
199		switch (menuItem.getItemId()) {
200			case android.R.id.home:
201				finish();
202				break;
203			case R.id.action_edit_subject:
204				if (mConversation != null) {
205					quickEdit(mConversation.getName(),this.onSubjectEdited);
206				}
207				break;
208			case R.id.action_save_as_bookmark:
209				saveAsBookmark();
210				break;
211			case R.id.action_delete_bookmark:
212				deleteBookmark();
213				break;
214			case R.id.action_advanced_mode:
215				this.mAdvancedMode = !menuItem.isChecked();
216				menuItem.setChecked(this.mAdvancedMode);
217				invalidateOptionsMenu();
218				updateView();
219				break;
220		}
221		return super.onOptionsItemSelected(menuItem);
222	}
223
224	@Override
225	protected String getShareableUri() {
226		if (mConversation != null) {
227			return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
228		} else {
229			return "";
230		}
231	}
232
233	@Override
234	public boolean onPrepareOptionsMenu(Menu menu) {
235		MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
236		MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
237		MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
238		menuItemAdvancedMode.setChecked(mAdvancedMode);
239		if (mConversation == null) {
240			return true;
241		}
242		Account account = mConversation.getAccount();
243		if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
244			menuItemSaveBookmark.setVisible(false);
245			menuItemDeleteBookmark.setVisible(true);
246		} else {
247			menuItemDeleteBookmark.setVisible(false);
248			menuItemSaveBookmark.setVisible(true);
249		}
250		return true;
251	}
252
253	@Override
254	public boolean onCreateOptionsMenu(Menu menu) {
255		getMenuInflater().inflate(R.menu.muc_details, menu);
256		return true;
257	}
258
259	@Override
260	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
261		Object tag = v.getTag();
262		if (tag instanceof User) {
263			getMenuInflater().inflate(R.menu.muc_details_context,menu);
264			final User user = (User) tag;
265			final User self = mConversation.getMucOptions().getSelf();
266			this.mSelectedUser = user;
267			String name;
268			final Contact contact = user.getContact();
269			if (contact != null) {
270				name = contact.getDisplayName();
271			} else if (user.getJid() != null){
272				name = user.getJid().toBareJid().toString();
273			} else {
274				name = user.getName();
275			}
276			menu.setHeaderTitle(name);
277			if (user.getJid() != null) {
278				MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
279				MenuItem startConversation = menu.findItem(R.id.start_conversation);
280				MenuItem giveMembership = menu.findItem(R.id.give_membership);
281				MenuItem removeMembership = menu.findItem(R.id.remove_membership);
282				MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
283				MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
284				MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
285				MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
286				startConversation.setVisible(true);
287				if (contact != null) {
288					showContactDetails.setVisible(true);
289				}
290				if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
291						self.getAffiliation().outranks(user.getAffiliation())) {
292					if (mAdvancedMode) {
293						if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
294							giveMembership.setVisible(true);
295						} else {
296							removeMembership.setVisible(true);
297						}
298						banFromConference.setVisible(true);
299					} else {
300						removeFromRoom.setVisible(true);
301					}
302					if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
303						giveAdminPrivileges.setVisible(true);
304					} else {
305						removeAdminPrivileges.setVisible(true);
306					}
307				}
308			} else {
309				MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
310				sendPrivateMessage.setVisible(true);
311			}
312
313		}
314		super.onCreateContextMenu(menu, v, menuInfo);
315	}
316
317	@Override
318	public boolean onContextItemSelected(MenuItem item) {
319		switch (item.getItemId()) {
320			case R.id.action_contact_details:
321				Contact contact = mSelectedUser.getContact();
322				if (contact != null) {
323					switchToContactDetails(contact);
324				}
325				return true;
326			case R.id.start_conversation:
327				startConversation(mSelectedUser);
328				return true;
329			case R.id.give_admin_privileges:
330				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.ADMIN,this);
331				return true;
332			case R.id.give_membership:
333				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
334				return true;
335			case R.id.remove_membership:
336				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.NONE,this);
337				return true;
338			case R.id.remove_admin_privileges:
339				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
340				return true;
341			case R.id.remove_from_room:
342				removeFromRoom(mSelectedUser);
343				return true;
344			case R.id.ban_from_conference:
345				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this);
346				xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,this);
347				return true;
348			case R.id.send_private_message:
349				privateMsgInMuc(mConversation,mSelectedUser.getName());
350				return true;
351			default:
352				return super.onContextItemSelected(item);
353		}
354	}
355
356	private void removeFromRoom(final User user) {
357		if (mConversation.getMucOptions().membersOnly()) {
358			xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this);
359			xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
360		} else {
361			AlertDialog.Builder builder = new AlertDialog.Builder(this);
362			builder.setTitle(R.string.ban_from_conference);
363			builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
364			builder.setNegativeButton(R.string.cancel,null);
365			builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
366				@Override
367				public void onClick(DialogInterface dialog, int which) {
368					xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
369					xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
370				}
371			});
372			builder.create().show();
373		}
374	}
375
376	protected void startConversation(User user) {
377		if (user.getJid() != null) {
378			Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
379			switchToConversation(conversation);
380		}
381	}
382
383	protected void saveAsBookmark() {
384		Account account = mConversation.getAccount();
385		Bookmark bookmark = new Bookmark(account, mConversation.getJid().toBareJid());
386		if (!mConversation.getJid().isBareJid()) {
387			bookmark.setNick(mConversation.getJid().getResourcepart());
388		}
389		bookmark.setAutojoin(true);
390		account.getBookmarks().add(bookmark);
391		xmppConnectionService.pushBookmarks(account);
392		mConversation.setBookmark(bookmark);
393	}
394
395	protected void deleteBookmark() {
396		Account account = mConversation.getAccount();
397		Bookmark bookmark = mConversation.getBookmark();
398		bookmark.unregisterConversation();
399		account.getBookmarks().remove(bookmark);
400		xmppConnectionService.pushBookmarks(account);
401	}
402
403	@Override
404	void onBackendConnected() {
405		if (mPendingConferenceInvite != null) {
406			mPendingConferenceInvite.execute(this);
407			mPendingConferenceInvite = null;
408		}
409		if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
410			this.uuid = getIntent().getExtras().getString("uuid");
411		}
412		if (uuid != null) {
413			this.mConversation = xmppConnectionService
414				.findConversationByUuid(uuid);
415			if (this.mConversation != null) {
416				updateView();
417			}
418		}
419	}
420
421	private void updateView() {
422		final MucOptions mucOptions = mConversation.getMucOptions();
423		final User self = mucOptions.getSelf();
424		mAccountJid.setText(getString(R.string.using_account, mConversation
425					.getAccount().getJid().toBareJid()));
426		mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
427		setTitle(mConversation.getName());
428		mFullJid.setText(mConversation.getJid().toBareJid().toString());
429		mYourNick.setText(mucOptions.getActualNick());
430		mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
431		if (mucOptions.online()) {
432			mMoreDetails.setVisibility(View.VISIBLE);
433			final String status = getStatus(self);
434			if (status != null) {
435				mRoleAffiliaton.setVisibility(View.VISIBLE);
436				mRoleAffiliaton.setText(status);
437			} else {
438				mRoleAffiliaton.setVisibility(View.GONE);
439			}
440			if (mucOptions.membersOnly()) {
441				mConferenceType.setText(R.string.private_conference);
442			} else {
443				mConferenceType.setText(R.string.public_conference);
444			}
445			if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
446				mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
447			} else {
448				mChangeConferenceSettingsButton.setVisibility(View.GONE);
449			}
450		}
451		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
452		membersView.removeAllViews();
453		final ArrayList<User> users = new ArrayList<>();
454		users.addAll(mConversation.getMucOptions().getUsers());
455		Collections.sort(users,new Comparator<User>() {
456			@Override
457			public int compare(User lhs, User rhs) {
458				return lhs.getName().compareToIgnoreCase(rhs.getName());
459			}
460		});
461		for (final User user : users) {
462			View view = inflater.inflate(R.layout.contact, membersView,false);
463			this.setListItemBackgroundOnView(view);
464			view.setOnClickListener(new OnClickListener() {
465				@Override
466				public void onClick(View view) {
467					highlightInMuc(mConversation, user.getName());
468				}
469			});
470			registerForContextMenu(view);
471			view.setTag(user);
472			TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
473			TextView tvKey = (TextView) view.findViewById(R.id.key);
474			TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
475			if (mAdvancedMode && user.getPgpKeyId() != 0) {
476				tvKey.setVisibility(View.VISIBLE);
477				tvKey.setOnClickListener(new OnClickListener() {
478
479					@Override
480					public void onClick(View v) {
481						viewPgpKey(user);
482					}
483				});
484				tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
485			}
486			Bitmap bm;
487			Contact contact = user.getContact();
488			if (contact != null) {
489				bm = avatarService().get(contact, getPixel(48));
490				tvDisplayName.setText(contact.getDisplayName());
491				tvStatus.setText(user.getName() + " \u2022 " + getStatus(user));
492			} else {
493				bm = avatarService().get(user.getName(), getPixel(48));
494				tvDisplayName.setText(user.getName());
495				tvStatus.setText(getStatus(user));
496
497			}
498			ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
499			iv.setImageBitmap(bm);
500			membersView.addView(view);
501			if (mConversation.getMucOptions().canInvite()) {
502				mInviteButton.setVisibility(View.VISIBLE);
503			} else {
504				mInviteButton.setVisibility(View.GONE);
505			}
506		}
507	}
508
509	private String getStatus(User user) {
510		if (mAdvancedMode) {
511			StringBuilder builder = new StringBuilder();
512			builder.append(getString(user.getAffiliation().getResId()));
513			builder.append(" (");
514			builder.append(getString(user.getRole().getResId()));
515			builder.append(')');
516			return builder.toString();
517		} else {
518			return getString(user.getAffiliation().getResId());
519		}
520	}
521
522	@SuppressWarnings("deprecation")
523	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
524	private void setListItemBackgroundOnView(View view) {
525		int sdk = android.os.Build.VERSION.SDK_INT;
526		if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
527			view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
528		} else {
529			view.setBackground(getResources().getDrawable(R.drawable.greybackground));
530		}
531	}
532
533	private void viewPgpKey(User user) {
534		PgpEngine pgp = xmppConnectionService.getPgpEngine();
535		if (pgp != null) {
536			PendingIntent intent = pgp.getIntentForKey(
537					mConversation.getAccount(), user.getPgpKeyId());
538			if (intent != null) {
539				try {
540					startIntentSenderForResult(intent.getIntentSender(), 0,
541							null, 0, 0, 0);
542				} catch (SendIntentException ignored) {
543
544				}
545			}
546		}
547	}
548
549	@Override
550	public void onAffiliationChangedSuccessful(Jid jid) {
551
552	}
553
554	@Override
555	public void onAffiliationChangeFailed(Jid jid, int resId) {
556		displayToast(getString(resId,jid.toBareJid().toString()));
557	}
558
559	@Override
560	public void onRoleChangedSuccessful(String nick) {
561
562	}
563
564	@Override
565	public void onRoleChangeFailed(String nick, int resId) {
566		displayToast(getString(resId,nick));
567	}
568
569	@Override
570	public void onPushSucceeded() {
571		displayToast(getString(R.string.modified_conference_options));
572	}
573
574	@Override
575	public void onPushFailed() {
576		displayToast(getString(R.string.could_not_modify_conference_options));
577	}
578
579	private void displayToast(final String msg) {
580		runOnUiThread(new Runnable() {
581			@Override
582			public void run() {
583				Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show();
584			}
585		});
586	}
587}