1package eu.siacs.conversations.ui;
2
3import android.support.v7.app.AlertDialog;
4import android.app.PendingIntent;
5import android.content.Context;
6import android.content.DialogInterface;
7import android.content.IntentSender.SendIntentException;
8import android.content.res.Resources;
9import android.graphics.Bitmap;
10import android.graphics.drawable.BitmapDrawable;
11import android.graphics.drawable.Drawable;
12import android.os.AsyncTask;
13import android.os.Bundle;
14import android.support.v7.widget.CardView;
15import android.view.ContextMenu;
16import android.view.LayoutInflater;
17import android.view.Menu;
18import android.view.MenuItem;
19import android.view.View;
20import android.view.View.OnClickListener;
21import android.widget.Button;
22import android.widget.ImageButton;
23import android.widget.ImageView;
24import android.widget.LinearLayout;
25import android.widget.RelativeLayout;
26import android.widget.TableLayout;
27import android.widget.TextView;
28import android.widget.Toast;
29
30import org.openintents.openpgp.util.OpenPgpUtils;
31
32import java.lang.ref.WeakReference;
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.concurrent.RejectedExecutionException;
36import java.util.concurrent.atomic.AtomicInteger;
37
38import eu.siacs.conversations.Config;
39import eu.siacs.conversations.R;
40import eu.siacs.conversations.crypto.PgpEngine;
41import eu.siacs.conversations.entities.Account;
42import eu.siacs.conversations.entities.Bookmark;
43import eu.siacs.conversations.entities.Contact;
44import eu.siacs.conversations.entities.Conversation;
45import eu.siacs.conversations.entities.MucOptions;
46import eu.siacs.conversations.entities.MucOptions.User;
47import eu.siacs.conversations.services.XmppConnectionService;
48import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
49import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
50import eu.siacs.conversations.utils.UIHelper;
51import eu.siacs.conversations.xmpp.jid.Jid;
52
53public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed {
54 public static final String ACTION_VIEW_MUC = "view_muc";
55
56 private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
57
58 private Conversation mConversation;
59 private OnClickListener inviteListener = new OnClickListener() {
60
61 @Override
62 public void onClick(View v) {
63 inviteToConversation(mConversation);
64 }
65 };
66 private TextView mYourNick;
67 private ImageView mYourPhoto;
68 private TextView mFullJid;
69 private TextView mAccountJid;
70 private LinearLayout membersView;
71 private CardView mMoreDetails;
72 private RelativeLayout mMucSettings;
73 private TextView mConferenceType;
74 private TableLayout mConferenceInfoTable;
75 private TextView mConferenceInfoMam;
76 private TextView mNotifyStatusText;
77 private ImageButton mChangeConferenceSettingsButton;
78 private ImageButton mNotifyStatusButton;
79 private Button mInviteButton;
80 private String uuid = null;
81 private User mSelectedUser = null;
82
83 private boolean mAdvancedMode = false;
84
85 private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
86 @Override
87 public void success(Conversation object) {
88 runOnUiThread(new Runnable() {
89 @Override
90 public void run() {
91 Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show();
92 updateView();
93 }
94 });
95
96 }
97
98 @Override
99 public void error(final int errorCode, Conversation object) {
100 runOnUiThread(new Runnable() {
101 @Override
102 public void run() {
103 Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show();
104 }
105 });
106 }
107
108 @Override
109 public void userInputRequried(PendingIntent pi, Conversation object) {
110
111 }
112 };
113
114 private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
115 @Override
116 public void onClick(View v) {
117 AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
118 builder.setTitle(R.string.pref_notification_settings);
119 String[] choices = {
120 getString(R.string.notify_on_all_messages),
121 getString(R.string.notify_only_when_highlighted),
122 getString(R.string.notify_never)
123 };
124 final AtomicInteger choice;
125 if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0) == Long.MAX_VALUE) {
126 choice = new AtomicInteger(2);
127 } else {
128 choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
129 }
130 builder.setSingleChoiceItems(choices, choice.get(), new DialogInterface.OnClickListener() {
131 @Override
132 public void onClick(DialogInterface dialog, int which) {
133 choice.set(which);
134 }
135 });
136 builder.setNegativeButton(R.string.cancel, null);
137 builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
138 @Override
139 public void onClick(DialogInterface dialog, int which) {
140 if (choice.get() == 2) {
141 mConversation.setMutedTill(Long.MAX_VALUE);
142 } else {
143 mConversation.setMutedTill(0);
144 mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY,String.valueOf(choice.get() == 0));
145 }
146 xmppConnectionService.updateConversation(mConversation);
147 updateView();
148 }
149 });
150 builder.create().show();
151 }
152 };
153
154 private OnClickListener mChangeConferenceSettings = new OnClickListener() {
155 @Override
156 public void onClick(View v) {
157 final MucOptions mucOptions = mConversation.getMucOptions();
158 AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
159 builder.setTitle(R.string.conference_options);
160 final String[] options;
161 final boolean[] values;
162 if (mAdvancedMode) {
163 options = new String[]{
164 getString(R.string.members_only),
165 getString(R.string.moderated),
166 getString(R.string.non_anonymous)
167 };
168 values = new boolean[]{
169 mucOptions.membersOnly(),
170 mucOptions.moderated(),
171 mucOptions.nonanonymous()
172 };
173 } else {
174 options = new String[]{
175 getString(R.string.members_only),
176 getString(R.string.non_anonymous)
177 };
178 values = new boolean[]{
179 mucOptions.membersOnly(),
180 mucOptions.nonanonymous()
181 };
182 }
183 builder.setMultiChoiceItems(options,values,new DialogInterface.OnMultiChoiceClickListener() {
184 @Override
185 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
186 values[which] = isChecked;
187 }
188 });
189 builder.setNegativeButton(R.string.cancel, null);
190 builder.setPositiveButton(R.string.confirm,new DialogInterface.OnClickListener() {
191 @Override
192 public void onClick(DialogInterface dialog, int which) {
193 if (!mucOptions.membersOnly() && values[0]) {
194 xmppConnectionService.changeAffiliationsInConference(mConversation,
195 MucOptions.Affiliation.NONE,
196 MucOptions.Affiliation.MEMBER);
197 }
198 Bundle options = new Bundle();
199 options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
200 if (values.length == 2) {
201 options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
202 } else if (values.length == 3) {
203 options.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
204 options.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
205 }
206 options.putString("muc#roomconfig_persistentroom", "1");
207 xmppConnectionService.pushConferenceConfiguration(mConversation,
208 options,
209 ConferenceDetailsActivity.this);
210 }
211 });
212 builder.create().show();
213 }
214 };
215 private OnValueEdited onSubjectEdited = new OnValueEdited() {
216
217 @Override
218 public String onValueEdited(String value) {
219 xmppConnectionService.pushSubjectToConference(mConversation,value);
220 return null;
221 }
222 };
223
224 @Override
225 public void onConversationUpdate() {
226 refreshUi();
227 }
228
229 @Override
230 public void onMucRosterUpdate() {
231 refreshUi();
232 }
233
234 @Override
235 protected void refreshUiReal() {
236 updateView();
237 }
238
239 @Override
240 protected void onCreate(Bundle savedInstanceState) {
241 super.onCreate(savedInstanceState);
242 setContentView(R.layout.activity_muc_details);
243 mYourNick = findViewById(R.id.muc_your_nick);
244 mYourPhoto = findViewById(R.id.your_photo);
245 ImageButton mEditNickButton = findViewById(R.id.edit_nick_button);
246 mFullJid = findViewById(R.id.muc_jabberid);
247 membersView = findViewById(R.id.muc_members);
248 mAccountJid = findViewById(R.id.details_account);
249 mMucSettings = findViewById(R.id.muc_settings);
250 mMoreDetails = findViewById(R.id.muc_more_details);
251 mMoreDetails.setVisibility(View.GONE);
252 mChangeConferenceSettingsButton = findViewById(R.id.change_conference_button);
253 mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
254 mInviteButton = findViewById(R.id.invite);
255 mInviteButton.setOnClickListener(inviteListener);
256 mConferenceType = findViewById(R.id.muc_conference_type);
257 if (getSupportActionBar() != null) {
258 getSupportActionBar().setHomeButtonEnabled(true);
259 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
260 }
261 mEditNickButton.setOnClickListener(v -> quickEdit(mConversation.getMucOptions().getActualNick(),
262 0,
263 value -> {
264 if (xmppConnectionService.renameInMuc(mConversation,value,renameCallback)) {
265 return null;
266 } else {
267 return getString(R.string.invalid_username);
268 }
269 }));
270 this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
271 this.mConferenceInfoTable = (TableLayout) findViewById(R.id.muc_info_more);
272 this.mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
273 this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam);
274 this.mNotifyStatusButton = (ImageButton) findViewById(R.id.notification_status_button);
275 this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
276 this.mNotifyStatusText = (TextView) findViewById(R.id.notification_status_text);
277 }
278
279 @Override
280 protected void onStart() {
281 super.onStart();
282 final int theme = findTheme();
283 if (this.mTheme != theme) {
284 recreate();
285 }
286 }
287
288 @Override
289 public boolean onOptionsItemSelected(MenuItem menuItem) {
290 switch (menuItem.getItemId()) {
291 case android.R.id.home:
292 finish();
293 break;
294 case R.id.action_edit_subject:
295 if (mConversation != null) {
296 quickEdit(mConversation.getMucOptions().getSubject(),
297 R.string.edit_subject_hint,
298 this.onSubjectEdited);
299 }
300 break;
301 case R.id.action_share_http:
302 shareLink(true);
303 break;
304 case R.id.action_share_uri:
305 shareLink(false);
306 break;
307 case R.id.action_save_as_bookmark:
308 saveAsBookmark();
309 break;
310 case R.id.action_delete_bookmark:
311 deleteBookmark();
312 break;
313 case R.id.action_advanced_mode:
314 this.mAdvancedMode = !menuItem.isChecked();
315 menuItem.setChecked(this.mAdvancedMode);
316 getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).apply();
317 final boolean online = mConversation != null && mConversation.getMucOptions().online();
318 mConferenceInfoTable.setVisibility(this.mAdvancedMode && online ? View.VISIBLE : View.GONE);
319 invalidateOptionsMenu();
320 updateView();
321 break;
322 }
323 return super.onOptionsItemSelected(menuItem);
324 }
325
326 @Override
327 protected String getShareableUri(boolean http) {
328 if (mConversation != null) {
329 if (http) {
330 return "https://conversations.im/j/"+ mConversation.getJid().toBareJid();
331 } else {
332 return "xmpp:"+mConversation.getJid().toBareJid()+"?join";
333 }
334 } else {
335 return null;
336 }
337 }
338
339 @Override
340 public boolean onPrepareOptionsMenu(Menu menu) {
341 MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
342 MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
343 MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
344 MenuItem menuItemChangeSubject = menu.findItem(R.id.action_edit_subject);
345 menuItemAdvancedMode.setChecked(mAdvancedMode);
346 if (mConversation == null) {
347 return true;
348 }
349 if (mConversation.getBookmark() != null) {
350 menuItemSaveBookmark.setVisible(false);
351 menuItemDeleteBookmark.setVisible(true);
352 } else {
353 menuItemDeleteBookmark.setVisible(false);
354 menuItemSaveBookmark.setVisible(true);
355 }
356 menuItemChangeSubject.setVisible(mConversation.getMucOptions().canChangeSubject());
357 return true;
358 }
359
360 @Override
361 public boolean onCreateOptionsMenu(Menu menu) {
362 getMenuInflater().inflate(R.menu.muc_details, menu);
363 return super.onCreateOptionsMenu(menu);
364 }
365
366 @Override
367 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
368 Object tag = v.getTag();
369 if (tag instanceof User) {
370 getMenuInflater().inflate(R.menu.muc_details_context,menu);
371 final User user = (User) tag;
372 final User self = mConversation.getMucOptions().getSelf();
373 this.mSelectedUser = user;
374 String name;
375 final Contact contact = user.getContact();
376 if (contact != null && contact.showInRoster()) {
377 name = contact.getDisplayName();
378 } else if (user.getRealJid() != null){
379 name = user.getRealJid().toBareJid().toString();
380 } else {
381 name = user.getName();
382 }
383 menu.setHeaderTitle(name);
384 if (user.getRealJid() != null) {
385 MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
386 MenuItem startConversation = menu.findItem(R.id.start_conversation);
387 MenuItem giveMembership = menu.findItem(R.id.give_membership);
388 MenuItem removeMembership = menu.findItem(R.id.remove_membership);
389 MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
390 MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
391 MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
392 MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
393 MenuItem invite = menu.findItem(R.id.invite);
394 startConversation.setVisible(true);
395 if (contact != null && contact.showInRoster()) {
396 showContactDetails.setVisible(!contact.isSelf());
397 }
398 if (user.getRole() == MucOptions.Role.NONE) {
399 invite.setVisible(true);
400 }
401 if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
402 self.getAffiliation().outranks(user.getAffiliation())) {
403 if (mAdvancedMode) {
404 if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
405 giveMembership.setVisible(true);
406 } else {
407 removeMembership.setVisible(true);
408 }
409 banFromConference.setVisible(true);
410 } else {
411 removeFromRoom.setVisible(true);
412 }
413 if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
414 giveAdminPrivileges.setVisible(true);
415 } else {
416 removeAdminPrivileges.setVisible(true);
417 }
418 }
419 } else {
420 MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
421 sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.PARTICIPANT));
422 }
423
424 }
425 super.onCreateContextMenu(menu, v, menuInfo);
426 }
427
428 @Override
429 public boolean onContextItemSelected(MenuItem item) {
430 Jid jid = mSelectedUser.getRealJid();
431 switch (item.getItemId()) {
432 case R.id.action_contact_details:
433 Contact contact = mSelectedUser.getContact();
434 if (contact != null) {
435 switchToContactDetails(contact);
436 }
437 return true;
438 case R.id.start_conversation:
439 startConversation(mSelectedUser);
440 return true;
441 case R.id.give_admin_privileges:
442 xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN,this);
443 return true;
444 case R.id.give_membership:
445 xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
446 return true;
447 case R.id.remove_membership:
448 xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE,this);
449 return true;
450 case R.id.remove_admin_privileges:
451 xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
452 return true;
453 case R.id.remove_from_room:
454 removeFromRoom(mSelectedUser);
455 return true;
456 case R.id.ban_from_conference:
457 xmppConnectionService.changeAffiliationInConference(mConversation,jid, MucOptions.Affiliation.OUTCAST,this);
458 if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
459 xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
460 }
461 return true;
462 case R.id.send_private_message:
463 if (mConversation.getMucOptions().allowPm()) {
464 privateMsgInMuc(mConversation,mSelectedUser.getName());
465 } else {
466 Toast.makeText(this, R.string.private_messages_are_disabled, Toast.LENGTH_SHORT).show();
467 }
468 return true;
469 case R.id.invite:
470 xmppConnectionService.directInvite(mConversation, jid);
471 return true;
472 default:
473 return super.onContextItemSelected(item);
474 }
475 }
476
477 private void removeFromRoom(final User user) {
478 if (mConversation.getMucOptions().membersOnly()) {
479 xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.NONE,this);
480 if (user.getRole() != MucOptions.Role.NONE) {
481 xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
482 }
483 } else {
484 AlertDialog.Builder builder = new AlertDialog.Builder(this);
485 builder.setTitle(R.string.ban_from_conference);
486 builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
487 builder.setNegativeButton(R.string.cancel,null);
488 builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
489 @Override
490 public void onClick(DialogInterface dialog, int which) {
491 xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
492 if (user.getRole() != MucOptions.Role.NONE) {
493 xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
494 }
495 }
496 });
497 builder.create().show();
498 }
499 }
500
501 protected void startConversation(User user) {
502 if (user.getRealJid() != null) {
503 Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getRealJid().toBareJid(),false,true);
504 switchToConversation(conversation);
505 }
506 }
507
508 protected void saveAsBookmark() {
509 xmppConnectionService.saveConversationAsBookmark(mConversation,
510 mConversation.getMucOptions().getSubject());
511 }
512
513 protected void deleteBookmark() {
514 Account account = mConversation.getAccount();
515 Bookmark bookmark = mConversation.getBookmark();
516 account.getBookmarks().remove(bookmark);
517 bookmark.setConversation(null);
518 xmppConnectionService.pushBookmarks(account);
519 updateView();
520 }
521
522 @Override
523 void onBackendConnected() {
524 if (mPendingConferenceInvite != null) {
525 mPendingConferenceInvite.execute(this);
526 mPendingConferenceInvite = null;
527 }
528 if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
529 this.uuid = getIntent().getExtras().getString("uuid");
530 }
531 if (uuid != null) {
532 this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
533 if (this.mConversation != null) {
534 updateView();
535 }
536 }
537 }
538
539 private void updateView() {
540 invalidateOptionsMenu();
541 final MucOptions mucOptions = mConversation.getMucOptions();
542 final User self = mucOptions.getSelf();
543 String account;
544 if (Config.DOMAIN_LOCK != null) {
545 account = mConversation.getAccount().getJid().getLocalpart();
546 } else {
547 account = mConversation.getAccount().getJid().toBareJid().toString();
548 }
549 mAccountJid.setText(getString(R.string.using_account, account));
550 mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
551 setTitle(mConversation.getName());
552 mFullJid.setText(mConversation.getJid().toBareJid().toString());
553 mYourNick.setText(mucOptions.getActualNick());
554 TextView mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
555 if (mucOptions.online()) {
556 mMoreDetails.setVisibility(View.VISIBLE);
557 mMucSettings.setVisibility(View.VISIBLE);
558 mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
559 final String status = getStatus(self);
560 if (status != null) {
561 mRoleAffiliaton.setVisibility(View.VISIBLE);
562 mRoleAffiliaton.setText(status);
563 } else {
564 mRoleAffiliaton.setVisibility(View.GONE);
565 }
566 if (mucOptions.membersOnly()) {
567 mConferenceType.setText(R.string.private_conference);
568 } else {
569 mConferenceType.setText(R.string.public_conference);
570 }
571 if (mucOptions.mamSupport()) {
572 mConferenceInfoMam.setText(R.string.server_info_available);
573 } else {
574 mConferenceInfoMam.setText(R.string.server_info_unavailable);
575 }
576 if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
577 mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
578 } else {
579 mChangeConferenceSettingsButton.setVisibility(View.GONE);
580 }
581 } else {
582 mMoreDetails.setVisibility(View.GONE);
583 mMucSettings.setVisibility(View.GONE);
584 mConferenceInfoTable.setVisibility(View.GONE);
585 }
586
587 int ic_notifications = getThemeResource(R.attr.icon_notifications, R.drawable.ic_notifications_black_24dp);
588 int ic_notifications_off = getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
589 int ic_notifications_paused = getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
590 int ic_notifications_none = getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
591
592 long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0);
593 if (mutedTill == Long.MAX_VALUE) {
594 mNotifyStatusText.setText(R.string.notify_never);
595 mNotifyStatusButton.setImageResource(ic_notifications_off);
596 } else if (System.currentTimeMillis() < mutedTill) {
597 mNotifyStatusText.setText(R.string.notify_paused);
598 mNotifyStatusButton.setImageResource(ic_notifications_paused);
599 } else if (mConversation.alwaysNotify()) {
600 mNotifyStatusButton.setImageResource(ic_notifications);
601 mNotifyStatusText.setText(R.string.notify_on_all_messages);
602 } else {
603 mNotifyStatusButton.setImageResource(ic_notifications_none);
604 mNotifyStatusText.setText(R.string.notify_only_when_highlighted);
605 }
606
607 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
608 membersView.removeAllViews();
609 final ArrayList<User> users = mucOptions.getUsers();
610 Collections.sort(users);
611 for (final User user : users) {
612 View view = inflater.inflate(R.layout.contact, membersView,false);
613 this.setListItemBackgroundOnView(view);
614 view.setOnClickListener(new OnClickListener() {
615 @Override
616 public void onClick(View view) {
617 highlightInMuc(mConversation, user.getName());
618 }
619 });
620 registerForContextMenu(view);
621 view.setTag(user);
622 TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
623 TextView tvKey = (TextView) view.findViewById(R.id.key);
624 TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
625 if (mAdvancedMode && user.getPgpKeyId() != 0) {
626 tvKey.setVisibility(View.VISIBLE);
627 tvKey.setOnClickListener(new OnClickListener() {
628
629 @Override
630 public void onClick(View v) {
631 viewPgpKey(user);
632 }
633 });
634 tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
635 }
636 Contact contact = user.getContact();
637 String name = user.getName();
638 if (contact != null) {
639 tvDisplayName.setText(contact.getDisplayName());
640 tvStatus.setText((name != null ? name+ " \u2022 " : "") + getStatus(user));
641 } else {
642 tvDisplayName.setText(name == null ? "" : name);
643 tvStatus.setText(getStatus(user));
644
645 }
646 ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
647 loadAvatar(user,iv);
648 if (user.getRole() == MucOptions.Role.NONE) {
649 tvDisplayName.setAlpha(INACTIVE_ALPHA);
650 tvKey.setAlpha(INACTIVE_ALPHA);
651 tvStatus.setAlpha(INACTIVE_ALPHA);
652 iv.setAlpha(INACTIVE_ALPHA);
653 }
654 membersView.addView(view);
655 if (mConversation.getMucOptions().canInvite()) {
656 mInviteButton.setVisibility(View.VISIBLE);
657 } else {
658 mInviteButton.setVisibility(View.GONE);
659 }
660 }
661 }
662
663 private String getStatus(User user) {
664 if (mAdvancedMode) {
665 return getString(user.getAffiliation().getResId()) +
666 " (" + getString(user.getRole().getResId()) + ')';
667 } else {
668 return getString(user.getAffiliation().getResId());
669 }
670 }
671
672 private void viewPgpKey(User user) {
673 PgpEngine pgp = xmppConnectionService.getPgpEngine();
674 if (pgp != null) {
675 PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
676 if (intent != null) {
677 try {
678 startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
679 } catch (SendIntentException ignored) {
680
681 }
682 }
683 }
684 }
685
686 @Override
687 public void onAffiliationChangedSuccessful(Jid jid) {
688 refreshUi();
689 }
690
691 @Override
692 public void onAffiliationChangeFailed(Jid jid, int resId) {
693 displayToast(getString(resId,jid.toBareJid().toString()));
694 }
695
696 @Override
697 public void onRoleChangedSuccessful(String nick) {
698
699 }
700
701 @Override
702 public void onRoleChangeFailed(String nick, int resId) {
703 displayToast(getString(resId,nick));
704 }
705
706 @Override
707 public void onPushSucceeded() {
708 displayToast(getString(R.string.modified_conference_options));
709 }
710
711 @Override
712 public void onPushFailed() {
713 displayToast(getString(R.string.could_not_modify_conference_options));
714 }
715
716 private void displayToast(final String msg) {
717 runOnUiThread(() -> Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show());
718 }
719
720
721 class BitmapWorkerTask extends AsyncTask<User, Void, Bitmap> {
722 private final WeakReference<ImageView> imageViewReference;
723 private User o = null;
724
725 private BitmapWorkerTask(ImageView imageView) {
726 imageViewReference = new WeakReference<>(imageView);
727 }
728
729 @Override
730 protected Bitmap doInBackground(User... params) {
731 this.o = params[0];
732 if (imageViewReference.get() == null) {
733 return null;
734 }
735 return avatarService().get(this.o, getPixel(48), isCancelled());
736 }
737
738 @Override
739 protected void onPostExecute(Bitmap bitmap) {
740 if (bitmap != null && !isCancelled()) {
741 final ImageView imageView = imageViewReference.get();
742 if (imageView != null) {
743 imageView.setImageBitmap(bitmap);
744 imageView.setBackgroundColor(0x00000000);
745 }
746 }
747 }
748 }
749
750 public void loadAvatar(User user, ImageView imageView) {
751 if (cancelPotentialWork(user, imageView)) {
752 final Bitmap bm = avatarService().get(user,getPixel(48),true);
753 if (bm != null) {
754 cancelPotentialWork(user, imageView);
755 imageView.setImageBitmap(bm);
756 imageView.setBackgroundColor(0x00000000);
757 } else {
758 String seed = user.getRealJid() != null ? user.getRealJid().toBareJid().toString() : null;
759 imageView.setBackgroundColor(UIHelper.getColorForName(seed == null ? user.getName() : seed));
760 imageView.setImageDrawable(null);
761 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
762 final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), null, task);
763 imageView.setImageDrawable(asyncDrawable);
764 try {
765 task.execute(user);
766 } catch (final RejectedExecutionException ignored) {
767 }
768 }
769 }
770 }
771
772 public static boolean cancelPotentialWork(User user, ImageView imageView) {
773 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
774
775 if (bitmapWorkerTask != null) {
776 final User old = bitmapWorkerTask.o;
777 if (old == null || user != old) {
778 bitmapWorkerTask.cancel(true);
779 } else {
780 return false;
781 }
782 }
783 return true;
784 }
785
786 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
787 if (imageView != null) {
788 final Drawable drawable = imageView.getDrawable();
789 if (drawable instanceof AsyncDrawable) {
790 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
791 return asyncDrawable.getBitmapWorkerTask();
792 }
793 }
794 return null;
795 }
796
797 static class AsyncDrawable extends BitmapDrawable {
798 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
799
800 public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
801 super(res, bitmap);
802 bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
803 }
804
805 public BitmapWorkerTask getBitmapWorkerTask() {
806 return bitmapWorkerTaskReference.get();
807 }
808 }
809
810}