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.List;
 29
 30import eu.siacs.conversations.R;
 31import eu.siacs.conversations.crypto.PgpEngine;
 32import eu.siacs.conversations.entities.Account;
 33import eu.siacs.conversations.entities.Bookmark;
 34import eu.siacs.conversations.entities.Contact;
 35import eu.siacs.conversations.entities.Conversation;
 36import eu.siacs.conversations.entities.MucOptions;
 37import eu.siacs.conversations.entities.MucOptions.User;
 38import eu.siacs.conversations.services.XmppConnectionService;
 39import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
 40import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
 41import eu.siacs.conversations.xmpp.jid.Jid;
 42import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 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 List<User> users = new ArrayList<>();
 67	private User mSelectedUser = null;
 68
 69	private boolean mAdvancedMode = false;
 70
 71	private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
 72		@Override
 73		public void success(Conversation object) {
 74			runOnUiThread(new Runnable() {
 75				@Override
 76				public void run() {
 77					Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show();
 78					updateView();
 79				}
 80			});
 81
 82		}
 83
 84		@Override
 85		public void error(final int errorCode, Conversation object) {
 86			runOnUiThread(new Runnable() {
 87				@Override
 88				public void run() {
 89					Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show();
 90				}
 91			});
 92		}
 93
 94		@Override
 95		public void userInputRequried(PendingIntent pi, Conversation object) {
 96
 97		}
 98	};
 99	private OnClickListener mChangeConferenceSettings = new OnClickListener() {
100		@Override
101		public void onClick(View v) {
102			final MucOptions mucOptions = mConversation.getMucOptions();
103			AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
104			builder.setTitle(R.string.conference_options);
105			String[] options = {getString(R.string.members_only),
106					getString(R.string.non_anonymous)};
107			final boolean[] values = new boolean[options.length];
108			values[0] = mucOptions.membersOnly();
109			values[1] = mucOptions.nonanonymous();
110			builder.setMultiChoiceItems(options,values,new DialogInterface.OnMultiChoiceClickListener() {
111				@Override
112				public void onClick(DialogInterface dialog, int which, boolean isChecked) {
113					values[which] = isChecked;
114				}
115			});
116			builder.setNegativeButton(R.string.cancel, null);
117			builder.setPositiveButton(R.string.confirm,new DialogInterface.OnClickListener() {
118				@Override
119				public void onClick(DialogInterface dialog, int which) {
120					Bundle options = new Bundle();
121					options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
122					options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
123					xmppConnectionService.pushConferenceConfiguration(mConversation,options,ConferenceDetailsActivity.this);
124				}
125			});
126			builder.create().show();
127		}
128	};
129
130	@Override
131	public void onConversationUpdate() {
132		runOnUiThread(new Runnable() {
133
134			@Override
135			public void run() {
136				updateView();
137			}
138		});
139	}
140
141	@Override
142	public void onMucRosterUpdate() {
143		runOnUiThread(new Runnable() {
144
145			@Override
146			public void run() {
147				updateView();
148			}
149		});
150	}
151
152	@Override
153	protected void onCreate(Bundle savedInstanceState) {
154		super.onCreate(savedInstanceState);
155		setContentView(R.layout.activity_muc_details);
156		mYourNick = (TextView) findViewById(R.id.muc_your_nick);
157		mYourPhoto = (ImageView) findViewById(R.id.your_photo);
158		mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
159		mFullJid = (TextView) findViewById(R.id.muc_jabberid);
160		membersView = (LinearLayout) findViewById(R.id.muc_members);
161		mAccountJid = (TextView) findViewById(R.id.details_account);
162		mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
163		mMoreDetails.setVisibility(View.GONE);
164		mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
165		mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
166		mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
167		mInviteButton = (Button) findViewById(R.id.invite);
168		mInviteButton.setOnClickListener(inviteListener);
169		mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
170		if (getActionBar() != null) {
171			getActionBar().setHomeButtonEnabled(true);
172			getActionBar().setDisplayHomeAsUpEnabled(true);
173		}
174		mEditNickButton.setOnClickListener(new OnClickListener() {
175
176			@Override
177			public void onClick(View v) {
178				quickEdit(mConversation.getMucOptions().getActualNick(),
179						new OnValueEdited() {
180
181							@Override
182							public void onValueEdited(String value) {
183								xmppConnectionService.renameInMuc(mConversation,value,renameCallback);
184							}
185						});
186			}
187		});
188	}
189
190	@Override
191	public boolean onOptionsItemSelected(MenuItem menuItem) {
192		switch (menuItem.getItemId()) {
193			case android.R.id.home:
194				finish();
195				break;
196			case R.id.action_edit_subject:
197				if (mConversation != null) {
198					quickEdit(mConversation.getName(), new OnValueEdited() {
199
200						@Override
201						public void onValueEdited(String value) {
202							MessagePacket packet = xmppConnectionService
203								.getMessageGenerator().conferenceSubject(
204										mConversation, value);
205							xmppConnectionService.sendMessagePacket(
206									mConversation.getAccount(), packet);
207						}
208					});
209				}
210				break;
211			case R.id.action_save_as_bookmark:
212				saveAsBookmark();
213				break;
214			case R.id.action_delete_bookmark:
215				deleteBookmark();
216				break;
217			case R.id.action_advanced_mode:
218				this.mAdvancedMode = !menuItem.isChecked();
219				menuItem.setChecked(this.mAdvancedMode);
220				invalidateOptionsMenu();
221				updateView();
222				break;
223		}
224		return super.onOptionsItemSelected(menuItem);
225	}
226
227	@Override
228	protected String getShareableUri() {
229		if (mConversation != null) {
230			return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
231		} else {
232			return "";
233		}
234	}
235
236	@Override
237	public boolean onPrepareOptionsMenu(Menu menu) {
238		MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
239		MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
240		MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
241		menuItemAdvancedMode.setChecked(mAdvancedMode);
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.getName();
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 (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
388			this.uuid = getIntent().getExtras().getString("uuid");
389		}
390		if (uuid != null) {
391			this.mConversation = xmppConnectionService
392				.findConversationByUuid(uuid);
393			if (this.mConversation != null) {
394				updateView();
395			}
396		}
397	}
398
399	private void updateView() {
400		final MucOptions mucOptions = mConversation.getMucOptions();
401		final User self = mucOptions.getSelf();
402		mAccountJid.setText(getString(R.string.using_account, mConversation
403					.getAccount().getJid().toBareJid()));
404		mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
405		setTitle(mConversation.getName());
406		mFullJid.setText(mConversation.getJid().toBareJid().toString());
407		mYourNick.setText(mucOptions.getActualNick());
408		mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
409		if (mucOptions.online()) {
410			mMoreDetails.setVisibility(View.VISIBLE);
411			final String status = getStatus(self);
412			if (status != null) {
413				mRoleAffiliaton.setVisibility(View.VISIBLE);
414				mRoleAffiliaton.setText(status);
415			} else {
416				mRoleAffiliaton.setVisibility(View.GONE);
417			}
418			if (mucOptions.membersOnly()) {
419				mConferenceType.setText(R.string.private_conference);
420			} else {
421				mConferenceType.setText(R.string.public_conference);
422			}
423			if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
424				mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
425			} else {
426				mChangeConferenceSettingsButton.setVisibility(View.GONE);
427			}
428		}
429		this.users.clear();
430		this.users.addAll(mucOptions.getUsers());
431		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
432		membersView.removeAllViews();
433		for (final User user : mConversation.getMucOptions().getUsers()) {
434			View view = inflater.inflate(R.layout.contact, membersView,
435					false);
436			this.setListItemBackgroundOnView(view);
437			view.setOnClickListener(new OnClickListener() {
438				@Override
439				public void onClick(View view) {
440					highlightInMuc(mConversation, user.getName());
441				}
442			});
443			registerForContextMenu(view);
444			view.setTag(user);
445			TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
446			TextView tvKey = (TextView) view.findViewById(R.id.key);
447			TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
448			if (mAdvancedMode && user.getPgpKeyId() != 0) {
449				tvKey.setVisibility(View.VISIBLE);
450				tvKey.setOnClickListener(new OnClickListener() {
451
452					@Override
453					public void onClick(View v) {
454						viewPgpKey(user);
455					}
456				});
457				tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
458			}
459			Bitmap bm;
460			Contact contact = user.getContact();
461			if (contact != null) {
462				bm = avatarService().get(contact, getPixel(48));
463				tvDisplayName.setText(contact.getDisplayName());
464				tvStatus.setText(user.getName() + " \u2022 " + getStatus(user));
465			} else {
466				bm = avatarService().get(user.getName(), getPixel(48));
467				tvDisplayName.setText(user.getName());
468				tvStatus.setText(getStatus(user));
469
470			}
471			ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
472			iv.setImageBitmap(bm);
473			membersView.addView(view);
474			if (mConversation.getMucOptions().canInvite()) {
475				mInviteButton.setVisibility(View.VISIBLE);
476			} else {
477				mInviteButton.setVisibility(View.GONE);
478			}
479		}
480	}
481
482	private String getStatus(User user) {
483		if (mAdvancedMode) {
484			StringBuilder builder = new StringBuilder();
485			builder.append(getString(user.getAffiliation().getResId()));
486			builder.append(" (");
487			builder.append(getString(user.getRole().getResId()));
488			builder.append(')');
489			return builder.toString();
490		} else {
491			return getString(user.getAffiliation().getResId());
492		}
493	}
494
495	@SuppressWarnings("deprecation")
496	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
497	private void setListItemBackgroundOnView(View view) {
498		int sdk = android.os.Build.VERSION.SDK_INT;
499		if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
500			view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
501		} else {
502			view.setBackground(getResources().getDrawable(R.drawable.greybackground));
503		}
504	}
505
506	private void viewPgpKey(User user) {
507		PgpEngine pgp = xmppConnectionService.getPgpEngine();
508		if (pgp != null) {
509			PendingIntent intent = pgp.getIntentForKey(
510					mConversation.getAccount(), user.getPgpKeyId());
511			if (intent != null) {
512				try {
513					startIntentSenderForResult(intent.getIntentSender(), 0,
514							null, 0, 0, 0);
515				} catch (SendIntentException ignored) {
516
517				}
518			}
519		}
520	}
521
522	@Override
523	public void onAffiliationChangedSuccessful(Jid jid) {
524
525	}
526
527	@Override
528	public void onAffiliationChangeFailed(Jid jid, int resId) {
529		displayToast(getString(resId,jid.toBareJid().toString()));
530	}
531
532	@Override
533	public void onRoleChangedSuccessful(String nick) {
534
535	}
536
537	@Override
538	public void onRoleChangeFailed(String nick, int resId) {
539		displayToast(getString(resId,nick));
540	}
541
542	@Override
543	public void onPushSucceeded() {
544		displayToast(getString(R.string.modified_conference_options));
545	}
546
547	@Override
548	public void onPushFailed() {
549		displayToast(getString(R.string.could_not_modify_conference_options));
550	}
551
552	private void displayToast(final String msg) {
553		runOnUiThread(new Runnable() {
554			@Override
555			public void run() {
556				Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show();
557			}
558		});
559	}
560}