made swipe and select work

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java              |  9 
src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java         | 42 
src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java     | 81 
src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java       | 19 
src/main/java/eu/siacs/conversations/ui/interfaces/OnConversationArchived.java |  1 
src/main/java/eu/siacs/conversations/ui/util/PendingItem.java                  |  4 
src/main/res/values/strings.xml                                                |  2 
7 files changed, 143 insertions(+), 15 deletions(-)

Detailed changes

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

@@ -2211,6 +2211,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
 		}
 	}
 
+	public static Conversation getConversation(Activity activity) {
+		Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.secondary_fragment);
+		if (fragment != null && fragment instanceof ConversationFragment) {
+			return ((ConversationFragment) fragment).getConversation();
+		} else {
+			return null;
+		}
+	}
+
 	public Conversation getConversation() {
 		return conversation;
 	}

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

@@ -53,24 +53,38 @@ import eu.siacs.conversations.ui.service.EmojiService;
 
 public class ConversationsMainActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead {
 
+
+	//secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
+	private static final @IdRes int[] FRAGMENT_ID_NOTIFICATION_ORDER = {R.id.secondary_fragment, R.id.main_fragment};
+
 	private ActivityConversationsBinding binding;
 
 	@Override
 	protected void refreshUiReal() {
-
+		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
+			refreshFragment(id);
+		}
 	}
 
 	@Override
 	void onBackendConnected() {
-		notifyFragment(R.id.main_fragment);
-		notifyFragment(R.id.secondary_fragment);
+		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
+			notifyFragmentOfBackendConnected(id);
+		}
 		invalidateActionBarTitle();
 	}
 
-	private void notifyFragment(@IdRes int id) {
-		Fragment mainFragment = getFragmentManager().findFragmentById(id);
-		if (mainFragment != null && mainFragment instanceof XmppFragment) {
-			((XmppFragment) mainFragment).onBackendConnected();
+	private void notifyFragmentOfBackendConnected(@IdRes int id) {
+		final Fragment fragment = getFragmentManager().findFragmentById(id);
+		if (fragment != null && fragment instanceof XmppFragment) {
+			((XmppFragment) fragment).onBackendConnected();
+		}
+	}
+
+	private void refreshFragment(@IdRes int id) {
+		final Fragment fragment = getFragmentManager().findFragmentById(id);
+		if (fragment != null && fragment instanceof XmppFragment) {
+			((XmppFragment) fragment).refresh();
 		}
 	}
 
@@ -94,14 +108,21 @@ public class ConversationsMainActivity extends XmppActivity implements OnConvers
 	public void onConversationSelected(Conversation conversation) {
 		Log.d(Config.LOGTAG, "selected " + conversation.getName());
 		ConversationFragment conversationFragment = (ConversationFragment) getFragmentManager().findFragmentById(R.id.secondary_fragment);
+		final boolean mainNeedsRefresh;
 		if (conversationFragment == null) {
+			mainNeedsRefresh = false;
 			conversationFragment = new ConversationFragment();
 			FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
 			fragmentTransaction.replace(R.id.main_fragment, conversationFragment);
 			fragmentTransaction.addToBackStack(null);
 			fragmentTransaction.commit();
+		} else {
+			mainNeedsRefresh = true;
 		}
 		conversationFragment.reInit(conversation);
+		if (mainNeedsRefresh) {
+			refreshFragment(R.id.main_fragment);
+		}
 	}
 
 	@Override
@@ -153,6 +174,8 @@ public class ConversationsMainActivity extends XmppActivity implements OnConvers
 		} else {
 			transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
 		}
+
+		//TODO, do this in backendConnected so we can actually decide what to display
 		if (binding.secondaryFragment != null) {
 			transaction.replace(R.id.secondary_fragment, new ConversationFragment());
 		}
@@ -183,7 +206,10 @@ public class ConversationsMainActivity extends XmppActivity implements OnConvers
 
 	@Override
 	public void onConversationsListItemUpdated() {
-
+		Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);
+		if (fragment != null && fragment instanceof ConversationsOverviewFragment) {
+			((ConversationsOverviewFragment) fragment).refresh();
+		}
 	}
 
 	@Override

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

@@ -47,9 +47,11 @@ import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.ui.adapter.ConversationAdapter;
+import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
 import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
+import eu.siacs.conversations.ui.util.PendingItem;
 
-public class ConversationsOverviewFragment extends XmppFragment {
+public class ConversationsOverviewFragment extends XmppFragment implements EnhancedListView.OnDismissCallback {
 
 	private FragmentConversationsOverviewBinding binding;
 
@@ -57,6 +59,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
 	private ConversationAdapter conversationsAdapter;
 	private XmppActivity activity;
 
+	private final PendingItem<Conversation> swipedConversation = new PendingItem<>();
+
 	@Override
 	public void onAttach(Activity activity) {
 		super.onAttach(activity);
@@ -76,7 +80,6 @@ public class ConversationsOverviewFragment extends XmppFragment {
 
 		this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations);
 		this.binding.list.setAdapter(this.conversationsAdapter);
-		this.binding.list.setSwipeDirection(EnhancedListView.SwipeDirection.BOTH);
 		this.binding.list.setOnItemClickListener((parent, view, position, id) -> {
 			Conversation conversation = this.conversations.get(position);
 			if (activity instanceof OnConversationSelected) {
@@ -85,7 +88,13 @@ public class ConversationsOverviewFragment extends XmppFragment {
 				Log.w(ConversationsOverviewFragment.class.getCanonicalName(),"Activity does not implement OnConversationSelected");
 			}
 		});
-
+		this.binding.list.setDismissCallback(this);
+		this.binding.list.enableSwipeToDismiss();
+		this.binding.list.setSwipeDirection(EnhancedListView.SwipeDirection.BOTH);
+		this.binding.list.setSwipingLayout(R.id.swipeable_item);
+		this.binding.list.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
+		this.binding.list.setUndoHideDelay(5000);
+		this.binding.list.setRequireTouchBeforeDismiss(false);
 		return binding.getRoot();
 	}
 
@@ -113,6 +122,72 @@ public class ConversationsOverviewFragment extends XmppFragment {
 	@Override
 	void refresh() {
 		this.activity.xmppConnectionService.populateWithOrderedConversations(this.conversations);
+		Conversation removed = this.swipedConversation.peek();
+		if (removed != null) {
+			if (removed.isRead()) {
+				this.conversations.remove(removed);
+			} else {
+				this.binding.list.discardUndo(); //will be ignored during discard when conversation is unRead
+			}
+		}
 		this.conversationsAdapter.notifyDataSetChanged();
 	}
+
+	@Override
+	public EnhancedListView.Undoable onDismiss(EnhancedListView listView, int position) {
+		try {
+			swipedConversation.push(this.conversationsAdapter.getItem(position));
+		} catch (IndexOutOfBoundsException e) {
+			return null;
+		}
+		this.conversationsAdapter.remove(swipedConversation.peek());
+		this.activity.xmppConnectionService.markRead(swipedConversation.peek());
+
+		if (position == 0 && this.conversationsAdapter.getCount() == 0) {
+			final Conversation c = swipedConversation.pop();
+			activity.xmppConnectionService.archiveConversation(c);
+			if (activity instanceof OnConversationArchived) {
+				((OnConversationArchived) activity).onConversationArchived(c);
+			}
+			return null;
+		}
+		final boolean formerlySelected = ConversationFragment.getConversation(getActivity()) == swipedConversation.peek();
+		if (activity instanceof OnConversationArchived) {
+			((OnConversationArchived) activity).onConversationArchived(swipedConversation.peek());
+		}
+		return new EnhancedListView.Undoable() {
+
+			@Override
+			public void undo() {
+				Conversation c = swipedConversation.pop();
+				conversationsAdapter.insert(c, position);
+				if (formerlySelected) {
+					if (activity instanceof OnConversationSelected) {
+						((OnConversationSelected) activity).onConversationSelected(c);
+					}
+				}
+				if (position > listView.getLastVisiblePosition()) {
+					listView.smoothScrollToPosition(position);
+				}
+			}
+
+			@Override
+			public void discard() {
+				Conversation c = swipedConversation.pop();
+				if (!c.isRead() && c.getMode() == Conversation.MODE_SINGLE) {
+					return;
+				}
+				activity.xmppConnectionService.archiveConversation(c);
+			}
+
+			@Override
+			public String getTitle() {
+				if (swipedConversation.peek().getMode() == Conversation.MODE_MULTI) {
+					return getResources().getString(R.string.title_undo_swipe_out_muc);
+				} else {
+					return getResources().getString(R.string.title_undo_swipe_out_conversation);
+				}
+			}
+		};
+	}
 }

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

@@ -7,6 +7,7 @@ import android.graphics.Typeface;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -19,11 +20,13 @@ import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.concurrent.RejectedExecutionException;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.ui.ConversationActivity;
+import eu.siacs.conversations.ui.ConversationFragment;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.ui.widget.UnreadCountCustomView;
 import eu.siacs.conversations.utils.EmojiWrapper;
@@ -32,6 +35,7 @@ import eu.siacs.conversations.utils.UIHelper;
 public class ConversationAdapter extends ArrayAdapter<Conversation> {
 
 	private XmppActivity activity;
+	private Conversation selectedConversation = null;
 
 	public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
 		super(activity, 0, conversations);
@@ -45,10 +49,9 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 			view = inflater.inflate(R.layout.conversation_list_row,parent, false);
 		}
 		Conversation conversation = getItem(position);
-		if (this.activity instanceof ConversationActivity) {
+		if (this.activity instanceof XmppActivity) {
 			View swipeableItem = view.findViewById(R.id.swipeable_item);
-			ConversationActivity a = (ConversationActivity) this.activity;
-			int c = a.highlightSelectedConversations() && conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor();
+			int c = conversation == selectedConversation ? this.activity.getSecondaryBackgroundColor() : this.activity.getPrimaryBackgroundColor();
 			swipeableItem.setBackgroundColor(c);
 		}
 		ViewHolder viewHolder = ViewHolder.get(view);
@@ -168,6 +171,16 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 		return view;
 	}
 
+	@Override
+	public void notifyDataSetChanged() {
+		this.selectedConversation = ConversationFragment.getConversation(activity);
+		Log.d(Config.LOGTAG,"notify data set changed");
+		if (this.selectedConversation == null) {
+			Log.d(Config.LOGTAG,"selected conversation is null");
+		}
+		super.notifyDataSetChanged();
+	}
+
 	public static class ViewHolder {
 		private TextView name;
 		private TextView lastMessage;

src/main/res/values/strings.xml 🔗

@@ -509,7 +509,7 @@
 	<string name="choose_quick_action">Choose quick action</string>
 	<string name="search_for_contacts_or_groups">Search for contacts or groups</string>
 	<string name="send_private_message">Send private message</string>
-	<string name="user_has_left_conference">%s has left the group chat!</string>
+	<string name="user_has_left_conference">%1$s has left the group chat!</string>
 	<string name="username">Username</string>
 	<string name="username_hint">Username</string>
 	<string name="invalid_username">This is not a valid username</string>