diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index a1966a902..b6a8faac1 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1,52 +1,52 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="eu.siacs.conversations">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:required="false" />
+ android:required="false" />
+ android:required="false" />
-
-
+
+
+ tools:node="remove" />
-
+
+ android:required="false" />
+ android:required="false" />
+ android:required="false" />
+ android:resource="@xml/automotive_app_desc" />
-
+
-
-
-
-
+
+
+
+
+ android:label="@string/title_activity_share_location" />
+ android:label="@string/search_messages" />
+ android:theme="@style/ConversationsTheme.Dialog" />
+ android:label="@string/title_activity_show_location" />
-
+
-
+
+ android:minHeight="300dp"
+ android:windowSoftInputMode="stateHidden" />
+ android:windowSoftInputMode="stateAlwaysHidden" />
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
+
+
-
+
+ android:label="@string/title_activity_choose_contact" />
+ android:label="@string/title_activity_block_list" />
-
+ android:label="@string/change_password_on_server" />
+
-
-
+
+
-
+
+ android:launchMode="singleTop" />
+ android:windowSoftInputMode="stateHidden|adjustResize" />
+ android:windowSoftInputMode="stateHidden" />
+ android:windowSoftInputMode="stateHidden" />
+ android:windowSoftInputMode="stateHidden" />
+ android:label="@string/group_chat_avatar" />
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
+ android:value=".services.ContactChooserTargetService" />
+ android:windowSoftInputMode="stateAlwaysHidden" />
+ android:value="eu.siacs.conversations.ui.SettingsActivity" />
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
@@ -262,21 +265,24 @@
android:grantUriPermissions="true">
+ android:resource="@xml/file_paths" />
+ android:grantUriPermissions="true" />
-
+
+
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index a92cb6768..4321d1f80 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -13,6 +13,7 @@ import java.util.Set;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
@@ -382,6 +383,21 @@ public class MucOptions {
return subset;
}
+ public static List sub(List users, int max) {
+ ArrayList subset = new ArrayList<>();
+ HashSet jids = new HashSet<>();
+ for (User user : users) {
+ jids.add(user.getAccount().getJid().asBareJid());
+ if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
+ subset.add(user);
+ }
+ if (subset.size() >= max) {
+ break;
+ }
+ }
+ return subset;
+ }
+
public int getUserCount() {
synchronized (users) {
return users.size();
@@ -705,7 +721,7 @@ public class MucOptions {
}
- public static class User implements Comparable {
+ public static class User implements Comparable, AvatarService.Avatarable {
private Role role = Role.NONE;
private Affiliation affiliation = Affiliation.NONE;
private Jid realJid;
@@ -841,7 +857,7 @@ public class MucOptions {
}
}
- private String getComparableName() {
+ public String getComparableName() {
Contact contact = getContact();
if (contact != null) {
return contact.getDisplayName();
@@ -866,5 +882,11 @@ public class MucOptions {
this.chatState = chatState;
return true;
}
+
+ @Override
+ public int getAvatarBackgroundColor() {
+ final String seed = realJid != null ? realJid.asBareJid().toString() : null;
+ return UIHelper.getColorForName(seed == null ? getName() : seed);
+ }
}
}
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index a13acd074..f4807a11b 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -80,6 +80,8 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
return get((Message) avatarable, size, cachedOnly);
} else if (avatarable instanceof ListItem) {
return get((ListItem) avatarable, size, cachedOnly);
+ } else if (avatarable instanceof MucOptions.User) {
+ return get((MucOptions.User) avatarable, size, cachedOnly);
}
throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName());
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 91b008d31..837e4e92d 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -4,47 +4,29 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
-import android.content.res.Resources;
import android.databinding.DataBindingUtil;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.WindowManager;
-import android.widget.ImageView;
import android.widget.Toast;
-import org.openintents.openpgp.util.OpenPgpUtils;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
-import eu.siacs.conversations.databinding.ContactBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
-import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.User;
@@ -52,13 +34,12 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
import eu.siacs.conversations.ui.adapter.MediaAdapter;
+import eu.siacs.conversations.ui.adapter.UserPreviewAdapter;
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
-import eu.siacs.conversations.ui.service.EmojiService;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.GridManager;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
-import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
import eu.siacs.conversations.ui.util.MyLinkify;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.utils.AccountUtils;
@@ -66,7 +47,6 @@ import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.StringUtils;
import eu.siacs.conversations.utils.StylingHelper;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import me.drakeet.support.toast.ToastCompat;
import rocks.xmpp.addr.Jid;
@@ -77,20 +57,11 @@ import static eu.siacs.conversations.utils.StringUtils.changed;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, XmppConnectionService.OnRoomDestroy, TextWatcher, OnMediaLoaded {
public static final String ACTION_VIEW_MUC = "view_muc";
- private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
-
private Conversation mConversation;
- private OnClickListener inviteListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- inviteToConversation(mConversation);
- }
- };
private ActivityMucDetailsBinding binding;
private MediaAdapter mMediaAdapter;
+ private UserPreviewAdapter mUserPreviewAdapter;
private String uuid = null;
- private User mSelectedUser = null;
private boolean mAdvancedMode = false;
@@ -205,31 +176,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
};
- public static boolean cancelPotentialWork(User user, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final User old = bitmapWorkerTask.o;
- if (old == null || user != old) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
@Override
public void onConversationUpdate() {
@@ -250,9 +196,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details);
- this.binding.mucMoreDetails.setVisibility(View.GONE);
this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings);
- this.binding.invite.setOnClickListener(inviteListener);
setSupportActionBar((Toolbar) binding.toolbar);
configureActionBar(getSupportActionBar());
this.binding.editNickButton.setOnClickListener(v -> quickEdit(mConversation.getMucOptions().getActualNick(),
@@ -285,9 +229,18 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.mucEditTitle.addTextChangedListener(this);
this.binding.mucEditSubject.addTextChangedListener(this);
this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject));
- mMediaAdapter = new MediaAdapter(this,R.dimen.media_size);
+ this.mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
+ this.mUserPreviewAdapter = new UserPreviewAdapter();
this.binding.media.setAdapter(mMediaAdapter);
+ this.binding.users.setAdapter(mUserPreviewAdapter);
GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
+ GridManager.setupLayoutManager(this, this.binding.users, R.dimen.media_size);
+ this.binding.invite.setOnClickListener(v -> inviteToConversation(mConversation));
+ this.binding.showUsers.setOnClickListener(v -> {
+ Intent intent = new Intent(this, MucUsersActivity.class);
+ intent.putExtra("uuid", mConversation.getUuid());
+ startActivity(intent);
+ });
}
@Override
@@ -434,41 +387,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
return super.onCreateOptionsMenu(menu);
}
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- Object tag = v.getTag();
- if (tag instanceof User) {
- getMenuInflater().inflate(R.menu.muc_details_context, menu);
- final User user = (User) tag;
- this.mSelectedUser = user;
- String name;
- final Contact contact = user.getContact();
- if (contact != null && contact.showInContactList()) {
- name = contact.getDisplayName();
- } else if (user.getRealJid() != null) {
- name = user.getRealJid().asBareJid().toString();
- } else {
- name = user.getName();
- }
- menu.setHeaderTitle(name);
- MucDetailsContextMenuHelper.configureMucDetailsContextMenu(this, menu, mConversation, user);
- }
- super.onCreateContextMenu(menu, v, menuInfo);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- if (!MucDetailsContextMenuHelper.onContextItemSelected(item, mSelectedUser, mConversation, this)) {
- return super.onContextItemSelected(item);
- }
- return true;
- }
-
@Override
public void onMediaLoaded(List attachments) {
runOnUiThread(() -> {
int limit = GridManager.getCurrentColumnCount(binding.media);
- mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit,attachments.size())));
+ mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit, attachments.size())));
binding.mediaWrapper.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE);
});
@@ -545,7 +468,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE);
this.binding.detailsAccount.setText(getString(R.string.using_account, account));
this.binding.jid.setText(mConversation.getJid().asBareJid().toEscapedString());
- AvatarWorkerTask.loadAvatar(mConversation,binding.yourPhoto,R.dimen.avatar_on_details_screen_size);
+ AvatarWorkerTask.loadAvatar(mConversation, binding.yourPhoto, R.dimen.avatar_on_details_screen_size);
String roomName = mucOptions.getName();
String subject = mucOptions.getSubject();
final boolean hasTitle;
@@ -566,7 +489,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
StylingHelper.format(spannable, this.binding.mucSubject.getCurrentTextColor());
MyLinkify.addLinks(spannable, false);
this.binding.mucSubject.setText(EmojiWrapper.transform(spannable));
- this.binding.mucSubject.setTextAppearance(this,subject.length() > (hasTitle ? 128 : 196) ? R.style.TextAppearance_Conversations_Body1_Linkified : R.style.TextAppearance_Conversations_Subhead);
+ this.binding.mucSubject.setTextAppearance(this, subject.length() > (hasTitle ? 128 : 196) ? R.style.TextAppearance_Conversations_Body1_Linkified : R.style.TextAppearance_Conversations_Subhead);
this.binding.mucSubject.setAutoLinkMask(0);
this.binding.mucSubject.setVisibility(View.VISIBLE);
} else {
@@ -574,7 +497,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
this.binding.mucYourNick.setText(mucOptions.getActualNick());
if (mucOptions.online()) {
- this.binding.mucMoreDetails.setVisibility(View.VISIBLE);
+ this.binding.usersWrapper.setVisibility(View.VISIBLE);
this.binding.mucSettings.setVisibility(View.VISIBLE);
this.binding.mucInfoMore.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
this.binding.mucRole.setVisibility(View.VISIBLE);
@@ -595,7 +518,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.changeConferenceButton.setVisibility(View.INVISIBLE);
}
} else {
- this.binding.mucMoreDetails.setVisibility(View.GONE);
+ this.binding.usersWrapper.setVisibility(View.GONE);
this.binding.mucInfoMore.setVisibility(View.GONE);
this.binding.mucSettings.setVisibility(View.GONE);
}
@@ -619,73 +542,39 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.notificationStatusText.setText(R.string.notify_only_when_highlighted);
this.binding.notificationStatusButton.setImageResource(ic_notifications_none);
}
-
- final LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- this.binding.mucMembers.removeAllViews();
- if (inflater == null) {
- return;
- }
- final ArrayList users = mucOptions.getUsers();
- Collections.sort(users);
- for (final User user : users) {
- ContactBinding binding = DataBindingUtil.inflate(inflater, R.layout.contact, this.binding.mucMembers, false);
- this.setListItemBackgroundOnView(binding.getRoot());
- binding.getRoot().setOnClickListener(view1 -> highlightInMuc(mConversation, user.getName()));
- registerForContextMenu(binding.getRoot());
- binding.getRoot().setTag(user);
- if (mAdvancedMode && user.getPgpKeyId() != 0) {
- binding.key.setVisibility(View.VISIBLE);
- binding.key.setOnClickListener(v -> viewPgpKey(user));
- binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
- }
- Contact contact = user.getContact();
- String name = user.getName();
- if (contact != null) {
- binding.contactDisplayName.setText(contact.getDisplayName());
- binding.contactJid.setText((name != null ? name + " \u2022 " : "") + getStatus(user));
+ List users = mucOptions.getUsers();
+ Collections.sort(users, (a, b) -> {
+ if (b.getAffiliation().outranks(a.getAffiliation())) {
+ return 1;
+ } else if (a.getAffiliation().outranks(b.getAffiliation())) {
+ return -1;
} else {
- binding.contactDisplayName.setText(name == null ? "" : name);
- binding.contactJid.setText(getStatus(user));
+ if (a.getAvatar() != null && b.getAvatar() == null) {
+ return -1;
+ } else if (a.getAvatar() == null && b.getAvatar() != null) {
+ return 1;
+ } else {
+ return a.getComparableName().compareToIgnoreCase(b.getComparableName());
+ }
+ }
+ });
+ this.mUserPreviewAdapter.setUserList(MucOptions.sub(users, GridManager.getCurrentColumnCount(binding.users)));
+ this.binding.invite.setVisibility(mucOptions.canInvite() ? View.VISIBLE : View.GONE);
- }
- loadAvatar(user, binding.contactPhoto);
- if (user.getRole() == MucOptions.Role.NONE) {
- binding.contactJid.setAlpha(INACTIVE_ALPHA);
- binding.key.setAlpha(INACTIVE_ALPHA);
- binding.contactDisplayName.setAlpha(INACTIVE_ALPHA);
- binding.contactPhoto.setAlpha(INACTIVE_ALPHA);
- }
- this.binding.mucMembers.addView(binding.getRoot());
- if (mConversation.getMucOptions().canInvite()) {
- this.binding.invite.setVisibility(View.VISIBLE);
- } else {
- this.binding.invite.setVisibility(View.GONE);
- }
+ }
+
+ public static String getStatus(Context context, User user, final boolean advanced) {
+ if (advanced) {
+ return String.format("%s (%s)", context.getString(user.getAffiliation().getResId()), context.getString(user.getRole().getResId()));
+ } else {
+ return context.getString(user.getAffiliation().getResId());
}
}
private String getStatus(User user) {
- if (mAdvancedMode) {
- return getString(user.getAffiliation().getResId()) +
- " (" + getString(user.getRole().getResId()) + ')';
- } else {
- return getString(user.getAffiliation().getResId());
- }
+ return getStatus(this, user, mAdvancedMode);
}
- private void viewPgpKey(User user) {
- PgpEngine pgp = xmppConnectionService.getPgpEngine();
- if (pgp != null) {
- PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
- if (intent != null) {
- try {
- startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
- } catch (SendIntentException ignored) {
-
- }
- }
- }
- }
@Override
public void onAffiliationChangedSuccessful(Jid jid) {
@@ -711,6 +600,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
public void onRoomDestroySucceeded() {
finish();
}
+
@Override
public void onRoomDestroyFailed() {
displayToast(getString(R.string.could_not_destroy_room));
@@ -735,28 +625,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
});
}
- public void loadAvatar(User user, ImageView imageView) {
- if (cancelPotentialWork(user, imageView)) {
- final Bitmap bm = avatarService().get(user, getPixel(48), true);
- if (bm != null) {
- cancelPotentialWork(user, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- String seed = user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null;
- imageView.setBackgroundColor(UIHelper.getColorForName(seed == null ? user.getName() : seed));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(user);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -784,46 +652,4 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference bitmapWorkerTaskReference;
-
- AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
-
- class BitmapWorkerTask extends AsyncTask {
- private final WeakReference imageViewReference;
- private User o = null;
-
- private BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(User... params) {
- this.o = params[0];
- if (imageViewReference.get() == null) {
- return null;
- }
- return avatarService().get(this.o, getPixel(48), isCancelled());
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
}
diff --git a/src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java b/src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java
new file mode 100644
index 000000000..275336f14
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java
@@ -0,0 +1,71 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ActivityMucUsersBinding;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.adapter.UserAdapter;
+import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
+
+public class MucUsersActivity extends XmppActivity implements XmppConnectionService.OnRosterUpdate {
+
+ private UserAdapter userAdapter;
+
+ private Conversation mConversation = null;
+
+ @Override
+ protected void refreshUiReal() {
+ }
+
+ @Override
+ void onBackendConnected() {
+ final Intent intent = getIntent();
+ final String uuid = intent == null ? null : intent.getStringExtra("uuid");
+ if (uuid != null) {
+ mConversation = xmppConnectionService.findConversationByUuid(uuid);
+ }
+ loadAndSubmitUsers();
+ }
+
+ private void loadAndSubmitUsers() {
+ if (mConversation != null) {
+ ArrayList users = mConversation.getMucOptions().getUsers();
+ Collections.sort(users);
+ userAdapter.submitList(users);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ if (!MucDetailsContextMenuHelper.onContextItemSelected(item, userAdapter.getSelectedUser(), mConversation, this)) {
+ return super.onContextItemSelected(item);
+ }
+ return true;
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ActivityMucUsersBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_users);
+ setSupportActionBar((Toolbar) binding.toolbar);
+ configureActionBar(getSupportActionBar(), true);
+ this.userAdapter = new UserAdapter(true);
+ binding.list.setAdapter(this.userAdapter);
+ }
+
+
+ @Override
+ public void onRosterUpdate() {
+ loadAndSubmitUsers();
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java
new file mode 100644
index 000000000..753027d56
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java
@@ -0,0 +1,147 @@
+package eu.siacs.conversations.ui.adapter;
+
+import android.app.PendingIntent;
+import android.content.IntentSender;
+import android.databinding.DataBindingUtil;
+import android.support.annotation.NonNull;
+import android.support.v7.recyclerview.extensions.ListAdapter;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.widget.RecyclerView;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.PopupMenu;
+
+import org.openintents.openpgp.util.OpenPgpUtils;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.crypto.PgpEngine;
+import eu.siacs.conversations.databinding.ContactBinding;
+import eu.siacs.conversations.databinding.UserPreviewBinding;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.ConferenceDetailsActivity;
+import eu.siacs.conversations.ui.XmppActivity;
+import eu.siacs.conversations.ui.util.AvatarWorkerTask;
+import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
+
+public class UserAdapter extends ListAdapter implements View.OnCreateContextMenuListener {
+
+ private MucOptions.User selectedUser = null;
+
+ static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) {
+ return a == b;
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) {
+ return a.equals(b);
+ }
+ };
+ private final boolean advancedMode;
+
+ public UserAdapter(final boolean advancedMode) {
+ super(DIFF);
+ this.advancedMode = advancedMode;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
+ return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.contact, viewGroup, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+ final MucOptions.User user = getItem(position);
+ AvatarWorkerTask.loadAvatar(user, viewHolder.binding.contactPhoto, R.dimen.avatar);
+ viewHolder.binding.getRoot().setOnClickListener(v -> {
+ final XmppActivity activity = XmppActivity.find(v);
+ if (activity != null) {
+ activity.highlightInMuc(user.getConversation(), user.getName());
+ }
+ });
+ viewHolder.binding.getRoot().setTag(user);
+ viewHolder.binding.getRoot().setOnCreateContextMenuListener(this);
+ viewHolder.binding.getRoot().setOnLongClickListener(v -> {
+ selectedUser = user;
+ return false;
+ });
+ final String name = user.getName();
+ final Contact contact = user.getContact();
+ if (contact != null) {
+ viewHolder.binding.contactDisplayName.setText(contact.getDisplayName());
+ if (name != null) {
+ viewHolder.binding.contactJid.setText(String.format("%s \u2022 %s", name, ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)));
+ } else {
+ viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
+ }
+ } else {
+ viewHolder.binding.contactDisplayName.setText(name == null ? "" : name);
+ viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
+ }
+ if (advancedMode && user.getPgpKeyId() != 0) {
+ viewHolder.binding.key.setVisibility(View.VISIBLE);
+ viewHolder.binding.key.setOnClickListener(v -> {
+ final XmppActivity activity = XmppActivity.find(v);
+ final XmppConnectionService service = activity == null ? null : activity.xmppConnectionService;
+ final PgpEngine pgpEngine = service == null ? null : service.getPgpEngine();
+ if (pgpEngine != null) {
+ PendingIntent intent = pgpEngine.getIntentForKey(user.getPgpKeyId());
+ if (intent != null) {
+ try {
+ activity.startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException ignored) {
+
+ }
+ }
+ }
+ });
+ viewHolder.binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
+ }
+
+
+ }
+
+ public MucOptions.User getSelectedUser() {
+ return selectedUser;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ final XmppActivity activity = XmppActivity.find(v);
+ final Object tag = v.getTag();
+ if (tag instanceof MucOptions.User && activity != null) {
+ activity.getMenuInflater().inflate(R.menu.muc_details_context, menu);
+ final MucOptions.User user = (MucOptions.User) tag;
+ String name;
+ final Contact contact = user.getContact();
+ if (contact != null && contact.showInContactList()) {
+ name = contact.getDisplayName();
+ } else if (user.getRealJid() != null) {
+ name = user.getRealJid().asBareJid().toString();
+ } else {
+ name = user.getName();
+ }
+ menu.setHeaderTitle(name);
+ MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, user.getConversation(), user);
+ }
+ }
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+
+ private final ContactBinding binding;
+
+ private ViewHolder(ContactBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/UserPreviewAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/UserPreviewAdapter.java
new file mode 100644
index 000000000..d25c164c1
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/UserPreviewAdapter.java
@@ -0,0 +1,73 @@
+package eu.siacs.conversations.ui.adapter;
+
+import android.databinding.DataBindingUtil;
+import android.support.annotation.NonNull;
+import android.support.v7.recyclerview.extensions.ListAdapter;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.ViewGroup;
+import android.widget.PopupMenu;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.UserPreviewBinding;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.ui.XmppActivity;
+import eu.siacs.conversations.ui.util.AvatarWorkerTask;
+import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
+
+public class UserPreviewAdapter extends ListAdapter {
+
+ public UserPreviewAdapter() {
+ super(UserAdapter.DIFF);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
+ return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.user_preview, viewGroup, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+ final MucOptions.User user = getItem(position);
+ AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size);
+ viewHolder.binding.getRoot().setOnClickListener(v -> {
+ final XmppActivity activity = XmppActivity.find(v);
+ if (activity != null) {
+ activity.highlightInMuc(user.getConversation(), user.getName());
+ }
+ });
+ viewHolder.binding.getRoot().setOnLongClickListener(v -> {
+ final XmppActivity activity = XmppActivity.find(v);
+ if (activity == null) {
+ return true;
+ }
+ final PopupMenu popupMenu = new PopupMenu(activity, v);
+ popupMenu.inflate(R.menu.muc_details_context);
+ final Menu menu = popupMenu.getMenu();
+ MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, user.getConversation(), user);
+ popupMenu.setOnMenuItemClickListener(menuItem -> MucDetailsContextMenuHelper.onContextItemSelected(menuItem, user, user.getConversation(), activity));
+ popupMenu.show();
+ return true;
+ });
+ }
+
+ public void setUserList(List users) {
+ submitList(users);
+ }
+
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+
+ private final UserPreviewBinding binding;
+
+ private ViewHolder(UserPreviewBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/util/GridManager.java b/src/main/java/eu/siacs/conversations/ui/util/GridManager.java
index 092e582ec..c84dbd63b 100644
--- a/src/main/java/eu/siacs/conversations/ui/util/GridManager.java
+++ b/src/main/java/eu/siacs/conversations/ui/util/GridManager.java
@@ -29,7 +29,8 @@ public class GridManager {
}
final ColumnInfo columnInfo = calculateColumnCount(context, recyclerView.getMeasuredWidth(), desiredSize);
Log.d(Config.LOGTAG, "final count " + columnInfo.count);
- if (recyclerView.getAdapter().getItemCount() != 0) {
+ final RecyclerView.Adapter adapter = recyclerView.getAdapter();
+ if (adapter != null && adapter.getItemCount() != 0) {
Log.e(Config.LOGTAG, "adapter already has items; just go with it now");
return;
}
diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml
index 4cd44b0aa..75e3c2e54 100644
--- a/src/main/res/layout/account_row.xml
+++ b/src/main/res/layout/account_row.xml
@@ -5,7 +5,7 @@
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index 1258c4db0..713007870 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -360,8 +360,8 @@
-
-
+ android:orientation="horizontal"
+ android:paddingEnd="@dimen/card_padding_regular"
+ android:paddingStart="@dimen/card_padding_regular"
+ android:paddingTop="@dimen/card_padding_regular"
+ android:paddingBottom="@dimen/card_padding_list"
+ android:layout_marginStart="-2dp"
+ android:layout_marginEnd="-2dp"/>
-
+ android:orientation="horizontal"
+ android:layout_gravity="end">
+ android:text="@string/invite"
+ android:textColor="?attr/colorAccent" />
+
diff --git a/src/main/res/layout/activity_muc_users.xml b/src/main/res/layout/activity_muc_users.xml
new file mode 100644
index 000000000..679c5156e
--- /dev/null
+++ b/src/main/res/layout/activity_muc_users.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml
index 76887d300..088f3243d 100644
--- a/src/main/res/layout/contact.xml
+++ b/src/main/res/layout/contact.xml
@@ -5,7 +5,7 @@
+
+
+
+
+
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 2a1955636..e6265d942 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -51,6 +51,7 @@
Share with…
Start conversation
Invite contact
+ Invite
Contacts
Contact
Cancel
@@ -748,6 +749,8 @@
Importance, Sound, Vibrate
Video compression
View media
+ View members
+ Group chat members
Media browser
File omitted due to security violation.
Video Quality