forked from mirror/monocles_chat_clean
Support XEP-0425 message moderation (Stephen Paul Weber)
+ fix unhandled exception in DatabaseBackend
This commit is contained in:
parent
bb9ea37925
commit
48f384b117
12 changed files with 164 additions and 35 deletions
|
@ -452,6 +452,13 @@
|
|||
<xmpp:version>0.2.0</xmpp:version>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0425.html"/>
|
||||
<xmpp:status>complete</xmpp:status>
|
||||
<xmpp:version>0.2.1</xmpp:version>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0454.html"/>
|
||||
|
@ -476,4 +483,4 @@
|
|||
</Version>
|
||||
</release>
|
||||
</Project>
|
||||
</rdf:RDF>
|
||||
</rdf:RDF>
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
|
|||
|
||||
import static eu.siacs.conversations.entities.Bookmark.printableValue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
|
@ -128,6 +129,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public static Conversation fromCursor(Cursor cursor) {
|
||||
return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
|
||||
cursor.getString(cursor.getColumnIndex(NAME)),
|
||||
|
@ -488,7 +490,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
return null;
|
||||
}
|
||||
|
||||
public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
|
||||
public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart) {
|
||||
synchronized (this.messages) {
|
||||
for (int i = this.messages.size() - 1; i >= 0; --i) {
|
||||
final Message message = messages.get(i);
|
||||
|
@ -496,12 +498,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
if (mcp == null) {
|
||||
continue;
|
||||
}
|
||||
if (mcp.equals(counterpart) && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
|
||||
&& (carbon == message.isCarbon() || received)) {
|
||||
final boolean idMatch = id.equals(message.getRemoteMsgId()) || message.remoteMsgIdMatchInEdit(id);
|
||||
if (idMatch && !message.isFileOrImage() && !message.treatAsDownloadable()) {
|
||||
return message;
|
||||
}
|
||||
if (mcp.equals(counterpart) || mcp.asBareJid().equals(counterpart)) {
|
||||
final boolean idMatch = id.equals(message.getRemoteMsgId()) || message.remoteMsgIdMatchInEdit(id) || (getMode() == MODE_MULTI && id.equals(message.getServerMsgId()));
|
||||
if (idMatch) return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
*/
|
||||
package eu.siacs.conversations.entities;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.database.Cursor;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -42,7 +43,7 @@ public class IndividualMessage extends Message {
|
|||
}
|
||||
|
||||
private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, boolean deleted, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable, boolean file_deleted, String bodyLanguage, String retractId) {
|
||||
super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, deleted, edited, oob, errorMessage, readByMarkers, markable, file_deleted, bodyLanguage,retractId);
|
||||
super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, deleted, edited, oob, errorMessage, readByMarkers, markable, file_deleted, bodyLanguage,retractId,null);
|
||||
}
|
||||
|
||||
public static Message createDateSeparator(Message message) {
|
||||
|
@ -53,10 +54,11 @@ public class IndividualMessage extends Message {
|
|||
return separator;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public static Message fromCursor(Cursor cursor, Conversational conversation) {
|
||||
Jid jid;
|
||||
try {
|
||||
String value = cursor.getString(cursor.getColumnIndex(COUNTERPART));
|
||||
@SuppressLint("Range") String value = cursor.getString(cursor.getColumnIndex(COUNTERPART));
|
||||
if (value != null) {
|
||||
jid = Jid.of(value);
|
||||
} else {
|
||||
|
@ -69,7 +71,7 @@ public class IndividualMessage extends Message {
|
|||
}
|
||||
Jid trueCounterpart;
|
||||
try {
|
||||
String value = cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART));
|
||||
@SuppressLint("Range") String value = cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART));
|
||||
if (value != null) {
|
||||
trueCounterpart = Jid.of(value);
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.siacs.conversations.entities;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
|
@ -8,10 +9,12 @@ import android.util.Log;
|
|||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
@ -35,6 +38,9 @@ import eu.siacs.conversations.utils.MimeUtils;
|
|||
import eu.siacs.conversations.utils.Patterns;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Tag;
|
||||
import eu.siacs.conversations.xml.XmlReader;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
||||
public class Message extends AbstractEntity implements AvatarService.Avatarable {
|
||||
|
@ -108,6 +114,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
protected boolean file_deleted = false;
|
||||
protected boolean carbon = false;
|
||||
protected boolean oob = false;
|
||||
protected List<Element> payloads = new ArrayList<>();
|
||||
|
||||
protected List<Edit> edits = new ArrayList<>();
|
||||
protected String relativeFilePath;
|
||||
protected boolean read = true;
|
||||
|
@ -168,6 +176,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
|
@ -195,6 +204,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
|
@ -204,7 +214,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
final String remoteMsgId, final String relativeFilePath,
|
||||
final String serverMsgId, final String fingerprint, final boolean read, final boolean deleted,
|
||||
final String edited, final boolean oob, final String errorMessage, final Set<ReadByMarker> readByMarkers,
|
||||
final boolean markable, final boolean file_deleted, final String bodyLanguage, final String retractId) {
|
||||
final boolean markable, final boolean file_deleted, final String bodyLanguage, final String retractId, final List<Element> payloads) {
|
||||
this.conversation = conversation;
|
||||
this.uuid = uuid;
|
||||
this.conversationUuid = conversationUUid;
|
||||
|
@ -230,9 +240,22 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
this.file_deleted = file_deleted;
|
||||
this.bodyLanguage = bodyLanguage;
|
||||
this.retractId = retractId;
|
||||
if (payloads != null) this.payloads = payloads;
|
||||
}
|
||||
|
||||
public static Message fromCursor(Cursor cursor, Conversation conversation) {
|
||||
@SuppressLint("Range")
|
||||
public static Message fromCursor(Cursor cursor, Conversation conversation) throws IOException {
|
||||
@SuppressLint("Range") String payloadsStr = cursor.getString(cursor.getColumnIndex("payloads"));
|
||||
List<Element> payloads = new ArrayList<>();
|
||||
if (payloadsStr != null) {
|
||||
final XmlReader xmlReader = new XmlReader();
|
||||
xmlReader.setInputStream(ByteSource.wrap(payloadsStr.getBytes()).openStream());
|
||||
Tag tag;
|
||||
while ((tag = xmlReader.readTag()) != null) {
|
||||
payloads.add(xmlReader.readElement(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return new Message(conversation,
|
||||
cursor.getString(cursor.getColumnIndex(UUID)),
|
||||
cursor.getString(cursor.getColumnIndex(CONVERSATION)),
|
||||
|
@ -257,7 +280,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0,
|
||||
cursor.getInt(cursor.getColumnIndex(FILE_DELETED)) > 0,
|
||||
cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)),
|
||||
cursor.getString(cursor.getColumnIndex(RETRACT_ID))
|
||||
cursor.getString(cursor.getColumnIndex(RETRACT_ID)),
|
||||
payloads
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -459,7 +483,9 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
public void setFileDeleted(boolean file_deleted) {
|
||||
this.file_deleted = file_deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
public void markRead() {
|
||||
this.read = true;
|
||||
}
|
||||
|
@ -852,6 +878,18 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
public void setOob(boolean isOob) {
|
||||
this.oob = isOob;
|
||||
}
|
||||
public void clearPayloads() {
|
||||
this.payloads.clear();
|
||||
}
|
||||
public void addPayload(Element el) {
|
||||
if (el == null) return;
|
||||
|
||||
this.payloads.add(el);
|
||||
}
|
||||
|
||||
public List<Element> getPayloads() {
|
||||
return new ArrayList<>(this.payloads);
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
String extension;
|
||||
|
@ -922,9 +960,17 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
}
|
||||
|
||||
public synchronized void resetFileParams() {
|
||||
this.fileParams = null;
|
||||
if (fileParams != null && this.fileParams != null && this.fileParams.sims != null && fileParams.sims == null) {
|
||||
fileParams.sims = this.fileParams.sims;
|
||||
}
|
||||
this.fileParams = fileParams;
|
||||
}
|
||||
public synchronized void setFileParams(FileParams fileParams) {
|
||||
if (fileParams != null && this.fileParams != null && this.fileParams.sims != null && fileParams.sims == null) {
|
||||
fileParams.sims = this.fileParams.sims;
|
||||
}
|
||||
this.fileParams = fileParams;
|
||||
}
|
||||
|
||||
public synchronized FileParams getFileParams() {
|
||||
if (fileParams == null) {
|
||||
fileParams = new FileParams();
|
||||
|
@ -1022,6 +1068,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
public int height = 0;
|
||||
public int runtime = 0;
|
||||
public String subject = "";
|
||||
public Element sims = null;
|
||||
|
||||
public long getSize() {
|
||||
return size == null ? 0 : size;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ import eu.siacs.conversations.xmpp.Jid;
|
|||
import eu.siacs.conversations.xmpp.forms.Data;
|
||||
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
|
||||
|
||||
public class IqGenerator extends AbstractGenerator {
|
||||
|
||||
|
@ -407,7 +409,18 @@ public class IqGenerator extends AbstractGenerator {
|
|||
item.setAttribute("role", role);
|
||||
return packet;
|
||||
}
|
||||
|
||||
public IqPacket moderateMessage(Account account, Message m, String reason) {
|
||||
IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
|
||||
packet.setTo(m.getConversation().getJid().asBareJid());
|
||||
packet.setFrom(account.getJid());
|
||||
Element moderate =
|
||||
packet.addChild("apply-to", "urn:xmpp:fasten:0")
|
||||
.setAttribute("id", m.getServerMsgId())
|
||||
.addChild("moderate", "urn:xmpp:message-moderate:0");
|
||||
moderate.addChild("retract", "urn:xmpp:message-retract:0");
|
||||
moderate.addChild("reason", "urn:xmpp:message-moderate:0").setContent(reason);
|
||||
return packet;
|
||||
}
|
||||
public IqPacket destroyRoom(Conversation conference) {
|
||||
IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
|
||||
packet.setTo(conference.getJid().asBareJid());
|
||||
|
|
|
@ -496,8 +496,16 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0");
|
||||
final Element oob = packet.findChild("x", Namespace.OOB);
|
||||
final String oobUrl = oob != null ? oob.findChildContent("url") : null;
|
||||
final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
|
||||
|
||||
String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
|
||||
boolean replaceAsRetraction = false;
|
||||
if (replacementId == null) {
|
||||
Element fasten = packet.findChild("apply-to", "urn:xmpp:fasten:0");
|
||||
if (fasten != null && (fasten.findChild("retract", "urn:xmpp:message-retract:0") != null || fasten.findChild("moderated", "urn:xmpp:message-moderate:0") != null)) {
|
||||
replacementId = fasten.getAttribute("id");
|
||||
packet.setBody("");
|
||||
replaceAsRetraction = true;
|
||||
}
|
||||
}
|
||||
final Element applyToElement = packet.findChild("apply-to", "urn:xmpp:fasten:0");
|
||||
final String retractId = applyToElement != null && applyToElement.findChild("retract", "urn:xmpp:message-retract:0") != null ? applyToElement.getAttribute("id") : null;
|
||||
|
||||
|
@ -726,9 +734,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
|
||||
if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
|
||||
Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId,
|
||||
counterpart,
|
||||
message.getStatus() == Message.STATUS_RECEIVED,
|
||||
message.isCarbon());
|
||||
counterpart
|
||||
);
|
||||
|
||||
if (replacedMessage == null) {
|
||||
replacedMessage = conversation.findSentMessageWithUuidOrRemoteId(replacementId, true, true);
|
||||
|
@ -741,7 +748,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
&& replacedMessage.getTrueCounterpart().asBareJid().equals(message.getTrueCounterpart().asBareJid());
|
||||
final boolean mucUserMatches = query == null && replacedMessage.sameMucUser(message); //can not be checked when using mam
|
||||
final boolean duplicate = conversation.hasDuplicateMessage(message);
|
||||
if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode || mucUserMatches) && !duplicate) {
|
||||
if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode || mucUserMatches || counterpart.isBareJid()) && !duplicate) {
|
||||
Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
|
||||
synchronized (replacedMessage) {
|
||||
final String uuid = replacedMessage.getUuid();
|
||||
|
@ -751,6 +758,13 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
replacedMessage.setUuid(UUID.randomUUID().toString());
|
||||
replacedMessage.setBody(message.getBody());
|
||||
replacedMessage.setRemoteMsgId(remoteMsgId);
|
||||
if (replaceAsRetraction) {
|
||||
mXmppConnectionService.getFileBackend().deleteFile(replacedMessage);
|
||||
mXmppConnectionService.evictPreview(message.getUuid());
|
||||
replacedMessage.clearPayloads();
|
||||
replacedMessage.setFileParams(null);
|
||||
replacedMessage.setDeleted(true);
|
||||
}
|
||||
if (replacedMessage.getServerMsgId() == null || message.getServerMsgId() != null) {
|
||||
replacedMessage.setServerMsgId(message.getServerMsgId());
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.siacs.conversations.persistance;
|
|||
|
||||
import static eu.siacs.conversations.ui.util.UpdateHelper.moveData_PAM_monocles;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
@ -702,6 +703,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return isExist;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
private void canonicalizeJids(SQLiteDatabase db) {
|
||||
// migrate db to new, canonicalized JID domainpart representation
|
||||
|
||||
|
@ -956,7 +958,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
|
||||
@Override
|
||||
public Message next() {
|
||||
Message message = Message.fromCursor(cursor, conversation);
|
||||
Message message = null;
|
||||
try {
|
||||
message = Message.fromCursor(cursor, conversation);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
cursor.moveToNext();
|
||||
return message;
|
||||
}
|
||||
|
@ -1345,6 +1352,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
null, null, null);
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public SessionRecord loadSession(Account account, SignalProtocolAddress contact) {
|
||||
SessionRecord session = null;
|
||||
Cursor cursor = getCursorForSession(account, contact);
|
||||
|
@ -1366,6 +1374,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return getSubDeviceSessions(db, account, contact);
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, SignalProtocolAddress contact) {
|
||||
List<Integer> devices = new ArrayList<>();
|
||||
String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
|
||||
|
@ -1460,6 +1469,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return cursor;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public PreKeyRecord loadPreKey(Account account, int preKeyId) {
|
||||
PreKeyRecord record = null;
|
||||
Cursor cursor = getCursorForPreKey(account, preKeyId);
|
||||
|
@ -1513,6 +1523,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return cursor;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
|
||||
SignedPreKeyRecord record = null;
|
||||
Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
|
||||
|
@ -1528,6 +1539,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return record;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
|
||||
List<SignedPreKeyRecord> prekeys = new ArrayList<>();
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
|
@ -1645,6 +1657,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return loadOwnIdentityKeyPair(db, account);
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
|
||||
String name = account.getJid().asBareJid().toString();
|
||||
IdentityKeyPair identityKeyPair = null;
|
||||
|
@ -1675,7 +1688,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
continue;
|
||||
}
|
||||
try {
|
||||
String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
|
||||
@SuppressLint("Range") String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
|
||||
if (key != null) {
|
||||
identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
|
||||
} else {
|
||||
|
@ -1798,7 +1811,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return null;
|
||||
} else {
|
||||
cursor.moveToFirst();
|
||||
byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
|
||||
@SuppressLint("Range") byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
|
||||
cursor.close();
|
||||
if (certificate == null || certificate.length == 0) {
|
||||
return null;
|
||||
|
|
|
@ -3933,6 +3933,17 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
});
|
||||
}
|
||||
public void moderateMessage(final Account account, final Message m, final String reason) {
|
||||
IqPacket request = this.mIqGenerator.moderateMessage(account, m, reason);
|
||||
Log.d(Config.LOGTAG, "moderate: " + request);
|
||||
sendIqPacket(account, request, (a, packet) -> {
|
||||
Log.d(Config.LOGTAG, "moderate1: " + packet);
|
||||
if (packet.getType() != IqPacket.TYPE.RESULT) {
|
||||
showErrorToastInUi(R.string.unable_to_moderate);
|
||||
Log.d(Config.LOGTAG, a.getJid().asBareJid() + " unable to moderate: " + packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void destroyRoom(final Conversation conversation, final OnRoomDestroy callback) {
|
||||
try {
|
||||
|
|
|
@ -1482,6 +1482,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
|||
MenuItem quoteMessage = menu.findItem(R.id.quote_message);
|
||||
MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
|
||||
MenuItem correctMessage = menu.findItem(R.id.correct_message);
|
||||
MenuItem moderateMessage = menu.findItem(R.id.moderate_message);
|
||||
MenuItem deleteMessage = menu.findItem(R.id.delete_message);
|
||||
MenuItem shareWith = menu.findItem(R.id.share_with);
|
||||
MenuItem sendAgain = menu.findItem(R.id.send_again);
|
||||
|
@ -1512,6 +1513,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
|||
&& m.getConversation() instanceof Conversation) {
|
||||
correctMessage.setVisible(true);
|
||||
}
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().getSelf().getRole().ranks(MucOptions.Role.MODERATOR) && conversation.getMucOptions().hasFeature("urn:xmpp:message-moderate:0")) {
|
||||
moderateMessage.setVisible(true);
|
||||
}
|
||||
if ((m.isFileOrImage() && !fileDeleted && !receiving) || (m.getType() == Message.TYPE_TEXT && !m.treatAsDownloadable()) && !unInitiatedButKnownSize && t == null && !messageDeleted) {
|
||||
shareWith.setVisible(true);
|
||||
|
||||
|
@ -1586,6 +1590,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
|||
case R.id.correct_message:
|
||||
correctMessage(selectedMessage);
|
||||
return true;
|
||||
case R.id.moderate_message:
|
||||
activity.quickEdit("Spam", (reason) -> {
|
||||
activity.xmppConnectionService.moderateMessage(conversation.getAccount(), selectedMessage, reason);
|
||||
return null;
|
||||
}, R.string.moderate_reason, false, false, true);
|
||||
return true;
|
||||
case R.id.copy_message:
|
||||
ShareUtil.copyToClipboard(activity, selectedMessage);
|
||||
return true;
|
||||
|
@ -3004,6 +3014,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
|||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private void updateSnackBar(final Conversation conversation) {
|
||||
if (conversation == null) {
|
||||
return;
|
||||
|
|
|
@ -298,6 +298,7 @@ public abstract class XmppActivity extends ActionBarActivity {
|
|||
return xmppConnectionService.getPgpEngine() != null;
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
public void showInstallPgpDialog() {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getString(R.string.openkeychain_required));
|
||||
|
@ -887,13 +888,16 @@ public abstract class XmppActivity extends ActionBarActivity {
|
|||
protected void quickPasswordEdit(String previousValue, OnValueEdited callback) {
|
||||
quickEdit(previousValue, callback, R.string.password, true, false);
|
||||
}
|
||||
|
||||
protected void quickEdit(final String previousValue, final OnValueEdited callback, final @StringRes int hint, boolean password, boolean permitEmpty) {
|
||||
quickEdit(previousValue, callback, hint, password, permitEmpty, false);
|
||||
}
|
||||
@SuppressLint("InflateParams")
|
||||
private void quickEdit(final String previousValue,
|
||||
final OnValueEdited callback,
|
||||
final @StringRes int hint,
|
||||
boolean password,
|
||||
boolean permitEmpty) {
|
||||
void quickEdit(final String previousValue,
|
||||
final OnValueEdited callback,
|
||||
final @StringRes int hint,
|
||||
boolean password,
|
||||
boolean permitEmpty,
|
||||
boolean alwaysCallback) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
DialogQuickeditBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_quickedit, null, false);
|
||||
if (password) {
|
||||
|
@ -914,7 +918,7 @@ public abstract class XmppActivity extends ActionBarActivity {
|
|||
dialog.show();
|
||||
View.OnClickListener clickListener = v -> {
|
||||
String value = binding.inputEditText.getText().toString();
|
||||
if (!value.equals(previousValue) && (!value.trim().isEmpty() || permitEmpty)) {
|
||||
if ((alwaysCallback || !value.equals(previousValue)) && (!value.trim().isEmpty() || permitEmpty)) {
|
||||
String error = callback.onValueEdited(value);
|
||||
if (error != null) {
|
||||
binding.inputLayout.setError(error);
|
||||
|
@ -1057,7 +1061,7 @@ public abstract class XmppActivity extends ActionBarActivity {
|
|||
inviteURL = Config.inviteUserURL + user + "/" + domain;
|
||||
}
|
||||
Log.d(Config.LOGTAG, "Invite uri = " + inviteURL);
|
||||
final String inviteText = getString(R.string.InviteText, user);
|
||||
@SuppressLint({"StringFormatInvalid", "LocalSuppress"}) final String inviteText = getString(R.string.InviteText, user);
|
||||
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, user + " " + getString(R.string.inviteUser_Subject) + " " + getString(R.string.app_name));
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
android:id="@+id/delete_message"
|
||||
android:title="@string/delete_message"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/moderate_message"
|
||||
android:title="@string/moderate_message"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/copy_url"
|
||||
android:title="@string/copy_original_url"
|
||||
|
|
|
@ -1252,4 +1252,7 @@
|
|||
<string name="pref_up_push_server_summary">A user-chosen push server to relay push messages via XMPP to your device.</string>
|
||||
<string name="no_account_deactivated">None (deactivated)</string>
|
||||
<string name="pref_theme_custom">Custom</string>
|
||||
<string name="moderate_message">Moderate</string>
|
||||
<string name="moderate_reason">Moderation Resaon</string>
|
||||
<string name="unable_to_moderate">Unable to Moderate</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue