diff --git a/res/menu/conversation_context.xml b/res/menu/conversation_context.xml
index 4224c2bc5c..1204fedb31 100644
--- a/res/menu/conversation_context.xml
+++ b/res/menu/conversation_context.xml
@@ -11,6 +11,12 @@
android:icon="?menu_trash_icon"
app:showAsAction="always" />
+
+
- Yes
No
Delete
+ Ban
Please wait...
Save
Note to Self
@@ -206,6 +207,7 @@
- This will permanently delete the selected message.
- This will permanently delete all %1$d selected messages.
+ Ban this user?
Save to storage?
- Saving this media to storage will allow any other apps on your device to access it.\n\nContinue?
@@ -230,6 +232,8 @@
SMS
Deleting
Deleting messages...
+ Banning
+ Banning user…
Original message not found
Original message no longer available
@@ -1340,6 +1344,7 @@
Message details
Copy text
Delete message
+ Ban user
Forward message
Resend message
Reply to message
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
index e8a766f0e4..31a9dea171 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
@@ -111,12 +111,16 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.TreeSet;
+import kotlin.Unit;
import network.loki.messenger.R;
+import nl.komponents.kovenant.Kovenant;
@SuppressLint("StaticFieldLeak")
public class ConversationFragment extends Fragment
@@ -406,18 +410,24 @@ public class ConversationFragment extends Fragment
boolean isPublicChat = (publicChat != null);
int selectedMessageCount = messageRecords.size();
boolean areAllSentByUser = true;
+ Set uniqueUserSet = new HashSet<>();
for (MessageRecord message : messageRecords) {
if (!message.isOutgoing()) { areAllSentByUser = false; }
+ uniqueUserSet.add(message.getRecipient().getAddress().toPhoneString());
}
menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser);
menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
boolean userCanModerate = isPublicChat && PublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
+ // allow banning if moderating a public chat and only one user's messages are selected
+ boolean isBanOptionVisible = isPublicChat && userCanModerate && !areAllSentByUser && uniqueUserSet.size() == 1;
menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible);
+ menu.findItem(R.id.menu_context_ban_user).setVisible(isBanOptionVisible);
} else {
menu.findItem(R.id.menu_context_copy_public_key).setVisible(false);
menu.findItem(R.id.menu_context_delete_message).setVisible(true);
+ menu.findItem(R.id.menu_context_ban_user).setVisible(false);
}
}
@@ -576,6 +586,59 @@ public class ConversationFragment extends Fragment
builder.show();
}
+ private void handleBanUser(Set messageRecords) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ String userPublicKey = null;
+ for (MessageRecord record: messageRecords) {
+ String currentPublicKey = record.getRecipient().getAddress().toPhoneString();
+ if (userPublicKey == null) {
+ userPublicKey = currentPublicKey;
+ }
+ }
+ final String finalPublicKey = userPublicKey;
+
+ builder.setIconAttribute(R.attr.dialog_alert_icon);
+ builder.setTitle(R.string.ConversationFragment_ban_selected_user);
+ builder.setCancelable(true);
+
+ PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
+
+ builder.setPositiveButton(R.string.ban, (dialog, which) -> {
+ ConversationAdapter chatAdapter = getListAdapter();
+ chatAdapter.clearSelection();
+ chatAdapter.notifyDataSetChanged();
+ new ProgressDialogAsyncTask(getActivity(),
+ R.string.ConversationFragment_banning,
+ R.string.ConversationFragment_banning_user) {
+ @Override
+ protected Void doInBackground(String... userPublicKeyParam) {
+ String userPublicKey = userPublicKeyParam[0];
+ if (publicChat != null) {
+ PublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getPublicChatAPI();
+ if (publicChat != null && publicChatAPI != null) {
+ publicChatAPI
+ .ban(userPublicKey, publicChat.getServer())
+ .success(l -> {
+ Log.d("Loki", "User banned");
+ return Unit.INSTANCE;
+ }).fail(e -> {
+ Log.d("Loki", "Couldn't ban user due to error: " + e.toString() + ".");
+ return null;
+ });
+ }
+ } else {
+ Log.d("Loki", "Tried to ban user from a non-public chat");
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, finalPublicKey);
+ });
+
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.show();
+ }
+
private void handleDisplayDetails(MessageRecord message) {
Intent intent = new Intent(getActivity(), MessageDetailsActivity.class);
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
@@ -1133,6 +1196,9 @@ public class ConversationFragment extends Fragment
handleDeleteMessages(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
+ case R.id.menu_context_ban_user:
+ handleBanUser(getListAdapter().getSelectedItems());
+ return true;
case R.id.menu_context_details:
handleDisplayDetails(getSelectedMessageRecord());
actionMode.finish();