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}