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