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