diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index fd3eb0d15..cb38ce27b 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/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; } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java index 57058ead2..090550e72 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java +++ b/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 diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java index a2605e2d9..d11d516b5 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java +++ b/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 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); + } + } + }; + } } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index bea61cd2b..d1ffb44e3 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/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 { private XmppActivity activity; + private Conversation selectedConversation = null; public ConversationAdapter(XmppActivity activity, List conversations) { super(activity, 0, conversations); @@ -45,10 +49,9 @@ public class ConversationAdapter extends ArrayAdapter { 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 { 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; diff --git a/src/main/java/eu/siacs/conversations/ui/interfaces/OnConversationArchived.java b/src/main/java/eu/siacs/conversations/ui/interfaces/OnConversationArchived.java index be7f66604..482870ed8 100644 --- a/src/main/java/eu/siacs/conversations/ui/interfaces/OnConversationArchived.java +++ b/src/main/java/eu/siacs/conversations/ui/interfaces/OnConversationArchived.java @@ -32,5 +32,6 @@ package eu.siacs.conversations.ui.interfaces; import eu.siacs.conversations.entities.Conversation; public interface OnConversationArchived { + void onConversationArchived(Conversation conversation); } diff --git a/src/main/java/eu/siacs/conversations/ui/util/PendingItem.java b/src/main/java/eu/siacs/conversations/ui/util/PendingItem.java index 45933e483..82252b516 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/PendingItem.java +++ b/src/main/java/eu/siacs/conversations/ui/util/PendingItem.java @@ -42,4 +42,8 @@ public class PendingItem { this.item = null; return item; } + + public synchronized T peek() { + return item; + } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 541e79a9d..25c8a6e7e 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -509,7 +509,7 @@ Choose quick action Search for contacts or groups Send private message - %s has left the group chat! + %1$s has left the group chat! Username Username This is not a valid username