Long-press context menu on list of conversations

Stephen Paul Weber created

For easy access to basics like pin to top or disable notifications

Change summary

src/cheogram/java/com/cheogram/android/ContextMenuRecyclerView.java        | 38 
src/cheogram/res/menu/conversations.xml                                    | 41 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java          | 14 
src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java | 60 
src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java   |  3 
src/main/res/layout/fragment_conversations_overview.xml                    |  4 
6 files changed, 150 insertions(+), 10 deletions(-)

Detailed changes

src/cheogram/java/com/cheogram/android/ContextMenuRecyclerView.java 🔗

@@ -0,0 +1,38 @@
+package com.cheogram.android;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ContextMenu;
+import android.view.View;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+public class ContextMenuRecyclerView extends androidx.recyclerview.widget.RecyclerView {
+	protected AdapterContextMenuInfo mAdapterContextMenuInfo = null;
+
+	public ContextMenuRecyclerView(Context context) {
+		super(context);
+	}
+
+	public ContextMenuRecyclerView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+	}
+
+	public ContextMenuRecyclerView(Context context, AttributeSet attrs, int defStyle) {
+		super(context, attrs, defStyle);
+	}
+
+	@Override
+	protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
+		return mAdapterContextMenuInfo;
+	}
+
+	@Override
+	public boolean showContextMenuForChild(View originalView) {
+		mAdapterContextMenuInfo = new AdapterContextMenuInfo(
+			originalView,
+			getChildAdapterPosition(originalView),
+			getChildItemId(originalView)
+		);
+		return super.showContextMenuForChild(originalView);
+	}
+}

src/cheogram/res/menu/conversations.xml 🔗

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item
+        android:id="@+id/action_toggle_pinned"
+        android:orderInCategory="10"
+        android:title="@string/add_to_favorites"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_mute"
+        android:orderInCategory="20"
+        android:title="@string/disable_notifications"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_unmute"
+        android:orderInCategory="21"
+        android:title="@string/enable_notifications"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_ongoing_call"
+        android:icon="?attr/icon_ongoing_call"
+        android:orderInCategory="30"
+        android:title="@string/return_to_ongoing_call"
+        app:showAsAction="always" />
+    <item
+        android:id="@+id/action_contact_details"
+        android:orderInCategory="40"
+        android:title="@string/action_contact_details"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_muc_details"
+        android:icon="?attr/icon_group"
+        android:orderInCategory="40"
+        android:title="@string/action_muc_details"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_archive"
+        android:orderInCategory="60"
+        android:title="@string/action_end_conversation"
+        app:showAsAction="never" />
+</menu>

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -1331,7 +1331,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
         final Optional<OngoingRtpSession> ongoingRtpSession = activity.xmppConnectionService.getJingleConnectionManager().getOngoingRtpConnection(conversation.getContact());
         if (ongoingRtpSession.isPresent()) {
             final OngoingRtpSession id = ongoingRtpSession.get();
-            final Intent intent = new Intent(getActivity(), RtpSessionActivity.class);
+            final Intent intent = new Intent(activity, RtpSessionActivity.class);
             intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.getAccount().getJid().asBareJid().toEscapedString());
             intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.getWith().toEscapedString());
             if (id instanceof AbstractJingleConnection.Id) {
@@ -1344,7 +1344,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
                     intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL);
                 }
             }
-            startActivity(intent);
+            activity.startActivity(intent);
         }
 
     }
@@ -1652,13 +1652,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
     }
 
     protected void muteConversationDialog(final Conversation conversation) {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
+        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
         builder.setTitle(R.string.disable_notifications);
-        final int[] durations = getResources().getIntArray(R.array.mute_options_durations);
+        final int[] durations = activity.getResources().getIntArray(R.array.mute_options_durations);
         final CharSequence[] labels = new CharSequence[durations.length];
         for (int i = 0; i < durations.length; ++i) {
             if (durations[i] == -1) {
-                labels[i] = getString(R.string.until_further_notice);
+                labels[i] = activity.getString(R.string.until_further_notice);
             } else {
                 labels[i] = TimeFrameUtils.resolve(activity, 1000L * durations[i]);
             }
@@ -1674,7 +1674,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
             activity.xmppConnectionService.updateConversation(conversation);
             activity.onConversationsListItemUpdated();
             refresh();
-            requireActivity().invalidateOptionsMenu();
+            activity.invalidateOptionsMenu();
         });
         builder.create().show();
     }
@@ -1706,7 +1706,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
         this.activity.xmppConnectionService.updateConversation(conversation);
         this.activity.onConversationsListItemUpdated();
         refresh();
-        requireActivity().invalidateOptionsMenu();
+        this.activity.invalidateOptionsMenu();
     }
 
 

src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java 🔗

@@ -37,12 +37,14 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.ContextMenu;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.Toast;
 
 import androidx.databinding.DataBindingUtil;
@@ -51,6 +53,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.google.android.material.snackbar.Snackbar;
+import com.google.common.base.Optional;
 import com.google.common.collect.Collections2;
 
 import java.util.ArrayList;
@@ -63,6 +66,7 @@ import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Conversational;
+import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.adapter.ConversationAdapter;
 import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
 import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
@@ -74,6 +78,7 @@ import eu.siacs.conversations.ui.util.StyledAttributes;
 import eu.siacs.conversations.utils.AccountUtils;
 import eu.siacs.conversations.utils.EasyOnboardingInvite;
 import eu.siacs.conversations.utils.ThemeHelper;
+import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
 
 import static androidx.recyclerview.widget.ItemTouchHelper.LEFT;
 import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
@@ -299,6 +304,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
 		});
 		this.binding.list.setAdapter(this.conversationsAdapter);
 		this.binding.list.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
+		registerForContextMenu(this.binding.list);
 		this.touchHelper = new ItemTouchHelper(this.callback);
 		this.touchHelper.attachToRecyclerView(this.binding.list);
 		return binding.getRoot();
@@ -312,6 +318,60 @@ public class ConversationsOverviewFragment extends XmppFragment {
 		easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
 	}
 
+	 @Override
+	 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+		 activity.getMenuInflater().inflate(R.menu.conversations, menu);
+
+		 final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
+		 final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
+		 final MenuItem menuMute = menu.findItem(R.id.action_mute);
+		 final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
+		 final MenuItem menuOngoingCall = menu.findItem(R.id.action_ongoing_call);
+		 final MenuItem menuTogglePinned = menu.findItem(R.id.action_toggle_pinned);
+
+		 Conversation conversation = conversations.get(((AdapterContextMenuInfo) menuInfo).position);
+		 if (conversation != null) {
+			 if (conversation.getMode() == Conversation.MODE_MULTI) {
+				 menuContactDetails.setVisible(false);
+				 menuMucDetails.setTitle(conversation.getMucOptions().isPrivateAndNonAnonymous() ? R.string.action_muc_details : R.string.channel_details);
+				 menuOngoingCall.setVisible(false);
+			 } else {
+				 final XmppConnectionService service = activity == null ? null : activity.xmppConnectionService;
+				 final Optional<OngoingRtpSession> ongoingRtpSession = service == null ? Optional.absent() : service.getJingleConnectionManager().getOngoingRtpConnection(conversation.getContact());
+				 if (ongoingRtpSession.isPresent()) {
+					 menuOngoingCall.setVisible(true);
+				 } else {
+					 menuOngoingCall.setVisible(false);
+				 }
+				 menuContactDetails.setVisible(!conversation.withSelf());
+				 menuMucDetails.setVisible(false);
+			}
+			if (conversation.isMuted()) {
+				menuMute.setVisible(false);
+			} else {
+				menuUnmute.setVisible(false);
+			}
+			if (conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)) {
+				menuTogglePinned.setTitle(R.string.remove_from_favorites);
+			} else {
+				menuTogglePinned.setTitle(R.string.add_to_favorites);
+			}
+		}
+		super.onCreateContextMenu(menu, view, menuInfo);
+	}
+
+	@Override
+	public boolean onContextItemSelected(MenuItem item) {
+		Conversation conversation = conversations.get(((AdapterContextMenuInfo) item.getMenuInfo()).position);
+		ConversationFragment fragment = new ConversationFragment();
+		fragment.setHasOptionsMenu(false);
+		fragment.onAttach(activity);
+		fragment.reInit(conversation, null);
+		boolean r = fragment.onOptionsItemSelected(item);
+		refresh();
+		return r;
+	}
+
 	@Override
 	public void onBackendConnected() {
 		refresh();

src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java 🔗

@@ -2,6 +2,7 @@ package eu.siacs.conversations.ui.adapter;
 
 import android.graphics.Typeface;
 import android.util.Pair;
+import android.view.MenuItem;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -237,8 +238,8 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
         private ConversationViewHolder(ConversationListRowBinding binding) {
             super(binding.getRoot());
             this.binding = binding;
+            binding.getRoot().setLongClickable(true);
         }
-
     }
 
 }

src/main/res/layout/fragment_conversations_overview.xml 🔗

@@ -7,7 +7,7 @@
         android:layout_height="match_parent">
 
 
-        <androidx.recyclerview.widget.RecyclerView
+        <com.cheogram.android.ContextMenuRecyclerView
             android:id="@+id/list"
             android:scrollbars="vertical"
             android:layout_width="match_parent"
@@ -24,4 +24,4 @@
             android:layout_margin="16dp"
             android:src="@drawable/ic_chat_white_24dp"/>
     </androidx.coordinatorlayout.widget.CoordinatorLayout>
-</layout>
+</layout>