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