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">