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			if (user.getJid() != null) {
269				final Contact contact = user.getContact();
270				if (contact != null) {
271					name = contact.getDisplayName();
272				} else {
273					name = user.getJid().toBareJid().toString();
274				}
275				menu.setHeaderTitle(name);
276				MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
277				MenuItem startConversation = menu.findItem(R.id.start_conversation);
278				MenuItem giveMembership = menu.findItem(R.id.give_membership);
279				MenuItem removeMembership = menu.findItem(R.id.remove_membership);
280				MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
281				MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
282				MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
283				MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
284				startConversation.setVisible(true);
285				if (contact != null) {
286					showContactDetails.setVisible(true);
287				}
288				if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
289						self.getAffiliation().outranks(user.getAffiliation())) {
290					if (mAdvancedMode) {
291						if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
292							giveMembership.setVisible(true);
293						} else {
294							removeMembership.setVisible(true);
295						}
296						banFromConference.setVisible(true);
297					} else {
298						removeFromRoom.setVisible(true);
299					}
300					if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
301						giveAdminPrivileges.setVisible(true);
302					} else {
303						removeAdminPrivileges.setVisible(true);
304					}
305				}
306			}
307
308		}
309		super.onCreateContextMenu(menu, v, menuInfo);
310	}
311
312	@Override
313	public boolean onContextItemSelected(MenuItem item) {
314		switch (item.getItemId()) {
315			case R.id.action_contact_details:
316				Contact contact = mSelectedUser.getContact();
317				if (contact != null) {
318					switchToContactDetails(contact);
319				}
320				return true;
321			case R.id.start_conversation:
322				startConversation(mSelectedUser);
323				return true;
324			case R.id.give_admin_privileges:
325				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.ADMIN,this);
326				return true;
327			case R.id.give_membership:
328				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
329				return true;
330			case R.id.remove_membership:
331				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.NONE,this);
332				return true;
333			case R.id.remove_admin_privileges:
334				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
335				return true;
336			case R.id.remove_from_room:
337				removeFromRoom(mSelectedUser);
338				return true;
339			case R.id.ban_from_conference:
340				xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this);
341				xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,this);
342				return true;
343			default:
344				return super.onContextItemSelected(item);
345		}
346	}
347
348	private void removeFromRoom(final User user) {
349		if (mConversation.getMucOptions().membersOnly()) {
350			xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this);
351			xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
352		} else {
353			AlertDialog.Builder builder = new AlertDialog.Builder(this);
354			builder.setTitle(R.string.ban_from_conference);
355			builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
356			builder.setNegativeButton(R.string.cancel,null);
357			builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
358				@Override
359				public void onClick(DialogInterface dialog, int which) {
360					xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
361					xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
362				}
363			});
364			builder.create().show();
365		}
366	}
367
368	protected void startConversation(User user) {
369		if (user.getJid() != null) {
370			Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
371			switchToConversation(conversation);
372		}
373	}
374
375	protected void saveAsBookmark() {
376		Account account = mConversation.getAccount();
377		Bookmark bookmark = new Bookmark(account, mConversation.getJid().toBareJid());
378		if (!mConversation.getJid().isBareJid()) {
379			bookmark.setNick(mConversation.getJid().getResourcepart());
380		}
381		bookmark.setAutojoin(true);
382		account.getBookmarks().add(bookmark);
383		xmppConnectionService.pushBookmarks(account);
384		mConversation.setBookmark(bookmark);
385	}
386
387	protected void deleteBookmark() {
388		Account account = mConversation.getAccount();
389		Bookmark bookmark = mConversation.getBookmark();
390		bookmark.unregisterConversation();
391		account.getBookmarks().remove(bookmark);
392		xmppConnectionService.pushBookmarks(account);
393	}
394
395	@Override
396	void onBackendConnected() {
397		if (mPendingConferenceInvite != null) {
398			mPendingConferenceInvite.execute(this);
399			mPendingConferenceInvite = null;
400		}
401		if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
402			this.uuid = getIntent().getExtras().getString("uuid");
403		}
404		if (uuid != null) {
405			this.mConversation = xmppConnectionService
406				.findConversationByUuid(uuid);
407			if (this.mConversation != null) {
408				updateView();
409			}
410		}
411	}
412
413	private void updateView() {
414		final MucOptions mucOptions = mConversation.getMucOptions();
415		final User self = mucOptions.getSelf();
416		mAccountJid.setText(getString(R.string.using_account, mConversation
417					.getAccount().getJid().toBareJid()));
418		mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
419		setTitle(mConversation.getName());
420		mFullJid.setText(mConversation.getJid().toBareJid().toString());
421		mYourNick.setText(mucOptions.getActualNick());
422		mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
423		if (mucOptions.online()) {
424			mMoreDetails.setVisibility(View.VISIBLE);
425			final String status = getStatus(self);
426			if (status != null) {
427				mRoleAffiliaton.setVisibility(View.VISIBLE);
428				mRoleAffiliaton.setText(status);
429			} else {
430				mRoleAffiliaton.setVisibility(View.GONE);
431			}
432			if (mucOptions.membersOnly()) {
433				mConferenceType.setText(R.string.private_conference);
434			} else {
435				mConferenceType.setText(R.string.public_conference);
436			}
437			if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
438				mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
439			} else {
440				mChangeConferenceSettingsButton.setVisibility(View.GONE);
441			}
442		}
443		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
444		membersView.removeAllViews();
445		final ArrayList<User> users = new ArrayList<>();
446		users.addAll(mConversation.getMucOptions().getUsers());
447		Collections.sort(users,new Comparator<User>() {
448			@Override
449			public int compare(User lhs, User rhs) {
450				return lhs.getName().compareToIgnoreCase(rhs.getName());
451			}
452		});
453		for (final User user : users) {
454			View view = inflater.inflate(R.layout.contact, membersView,false);
455			this.setListItemBackgroundOnView(view);
456			view.setOnClickListener(new OnClickListener() {
457				@Override
458				public void onClick(View view) {
459					highlightInMuc(mConversation, user.getName());
460				}
461			});
462			registerForContextMenu(view);
463			view.setTag(user);
464			TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
465			TextView tvKey = (TextView) view.findViewById(R.id.key);
466			TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
467			if (mAdvancedMode && user.getPgpKeyId() != 0) {
468				tvKey.setVisibility(View.VISIBLE);
469				tvKey.setOnClickListener(new OnClickListener() {
470
471					@Override
472					public void onClick(View v) {
473						viewPgpKey(user);
474					}
475				});
476				tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
477			}
478			Bitmap bm;
479			Contact contact = user.getContact();
480			if (contact != null) {
481				bm = avatarService().get(contact, getPixel(48));
482				tvDisplayName.setText(contact.getDisplayName());
483				tvStatus.setText(user.getName() + " \u2022 " + getStatus(user));
484			} else {
485				bm = avatarService().get(user.getName(), getPixel(48));
486				tvDisplayName.setText(user.getName());
487				tvStatus.setText(getStatus(user));
488
489			}
490			ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
491			iv.setImageBitmap(bm);
492			membersView.addView(view);
493			if (mConversation.getMucOptions().canInvite()) {
494				mInviteButton.setVisibility(View.VISIBLE);
495			} else {
496				mInviteButton.setVisibility(View.GONE);
497			}
498		}
499	}
500
501	private String getStatus(User user) {
502		if (mAdvancedMode) {
503			StringBuilder builder = new StringBuilder();
504			builder.append(getString(user.getAffiliation().getResId()));
505			builder.append(" (");
506			builder.append(getString(user.getRole().getResId()));
507			builder.append(')');
508			return builder.toString();
509		} else {
510			return getString(user.getAffiliation().getResId());
511		}
512	}
513
514	@SuppressWarnings("deprecation")
515	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
516	private void setListItemBackgroundOnView(View view) {
517		int sdk = android.os.Build.VERSION.SDK_INT;
518		if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
519			view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
520		} else {
521			view.setBackground(getResources().getDrawable(R.drawable.greybackground));
522		}
523	}
524
525	private void viewPgpKey(User user) {
526		PgpEngine pgp = xmppConnectionService.getPgpEngine();
527		if (pgp != null) {
528			PendingIntent intent = pgp.getIntentForKey(
529					mConversation.getAccount(), user.getPgpKeyId());
530			if (intent != null) {
531				try {
532					startIntentSenderForResult(intent.getIntentSender(), 0,
533							null, 0, 0, 0);
534				} catch (SendIntentException ignored) {
535
536				}
537			}
538		}
539	}
540
541	@Override
542	public void onAffiliationChangedSuccessful(Jid jid) {
543
544	}
545
546	@Override
547	public void onAffiliationChangeFailed(Jid jid, int resId) {
548		displayToast(getString(resId,jid.toBareJid().toString()));
549	}
550
551	@Override
552	public void onRoleChangedSuccessful(String nick) {
553
554	}
555
556	@Override
557	public void onRoleChangeFailed(String nick, int resId) {
558		displayToast(getString(resId,nick));
559	}
560
561	@Override
562	public void onPushSucceeded() {
563		displayToast(getString(R.string.modified_conference_options));
564	}
565
566	@Override
567	public void onPushFailed() {
568		displayToast(getString(R.string.could_not_modify_conference_options));
569	}
570
571	private void displayToast(final String msg) {
572		runOnUiThread(new Runnable() {
573			@Override
574			public void run() {
575				Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show();
576			}
577		});
578	}
579}