very much unoptimized search functionality
This commit is contained in:
parent
4bbbf86f76
commit
35020702fb
|
@ -48,7 +48,7 @@ public class PgpEngine {
|
||||||
public void encrypt(final Message message, final UiCallback<Message> callback) {
|
public void encrypt(final Message message, final UiCallback<Message> callback) {
|
||||||
Intent params = new Intent();
|
Intent params = new Intent();
|
||||||
params.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
params.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversation conversation = (Conversation) message.getConversation();
|
||||||
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
||||||
long[] keys = {
|
long[] keys = {
|
||||||
conversation.getContact().getPgpKeyId(),
|
conversation.getContact().getPgpKeyId(),
|
||||||
|
|
|
@ -1307,7 +1307,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
if (message.getType() == Message.TYPE_PRIVATE) {
|
if (message.getType() == Message.TYPE_PRIVATE) {
|
||||||
success = buildHeader(axolotlMessage, message.getTrueCounterpart());
|
success = buildHeader(axolotlMessage, message.getTrueCounterpart());
|
||||||
} else {
|
} else {
|
||||||
success = buildHeader(axolotlMessage, message.getConversation());
|
success = buildHeader(axolotlMessage, (Conversation) message.getConversation());
|
||||||
}
|
}
|
||||||
return success ? axolotlMessage : null;
|
return success ? axolotlMessage : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,15 +30,12 @@ import rocks.xmpp.addr.Jid;
|
||||||
import static eu.siacs.conversations.entities.Bookmark.printableValue;
|
import static eu.siacs.conversations.entities.Bookmark.printableValue;
|
||||||
|
|
||||||
|
|
||||||
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> {
|
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation>, Conversational {
|
||||||
public static final String TABLENAME = "conversations";
|
public static final String TABLENAME = "conversations";
|
||||||
|
|
||||||
public static final int STATUS_AVAILABLE = 0;
|
public static final int STATUS_AVAILABLE = 0;
|
||||||
public static final int STATUS_ARCHIVED = 1;
|
public static final int STATUS_ARCHIVED = 1;
|
||||||
|
|
||||||
public static final int MODE_MULTI = 1;
|
|
||||||
public static final int MODE_SINGLE = 0;
|
|
||||||
|
|
||||||
public static final String NAME = "name";
|
public static final String NAME = "name";
|
||||||
public static final String ACCOUNT = "accountUuid";
|
public static final String ACCOUNT = "accountUuid";
|
||||||
public static final String CONTACT = "contactUuid";
|
public static final String CONTACT = "contactUuid";
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
public interface Conversational {
|
||||||
|
|
||||||
|
int MODE_MULTI = 1;
|
||||||
|
int MODE_SINGLE = 0;
|
||||||
|
|
||||||
|
Account getAccount();
|
||||||
|
|
||||||
|
Contact getContact();
|
||||||
|
|
||||||
|
Jid getJid();
|
||||||
|
|
||||||
|
int getMode();
|
||||||
|
|
||||||
|
String getUuid();
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
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, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable) {
|
||||||
|
super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message next() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message prev() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidInSession() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message fromCursor(Cursor cursor, Conversational conversation) {
|
||||||
|
Jid jid;
|
||||||
|
try {
|
||||||
|
String value = cursor.getString(cursor.getColumnIndex(COUNTERPART));
|
||||||
|
if (value != null) {
|
||||||
|
jid = Jid.of(value);
|
||||||
|
} else {
|
||||||
|
jid = null;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
jid = null;
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return null; // message too long?
|
||||||
|
}
|
||||||
|
Jid trueCounterpart;
|
||||||
|
try {
|
||||||
|
String value = cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART));
|
||||||
|
if (value != null) {
|
||||||
|
trueCounterpart = Jid.of(value);
|
||||||
|
} else {
|
||||||
|
trueCounterpart = null;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
trueCounterpart = null;
|
||||||
|
}
|
||||||
|
return new IndividualMessage(conversation,
|
||||||
|
cursor.getString(cursor.getColumnIndex(UUID)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(CONVERSATION)),
|
||||||
|
jid,
|
||||||
|
trueCounterpart,
|
||||||
|
cursor.getString(cursor.getColumnIndex(BODY)),
|
||||||
|
cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(STATUS)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(TYPE)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(CARBON)) > 0,
|
||||||
|
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(FINGERPRINT)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(READ)) > 0,
|
||||||
|
cursor.getString(cursor.getColumnIndex(EDITED)),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(OOB)) > 0,
|
||||||
|
cursor.getString(cursor.getColumnIndex(ERROR_MESSAGE)),
|
||||||
|
ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))),
|
||||||
|
cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,7 +90,7 @@ public class Message extends AbstractEntity {
|
||||||
protected boolean read = true;
|
protected boolean read = true;
|
||||||
protected String remoteMsgId = null;
|
protected String remoteMsgId = null;
|
||||||
protected String serverMsgId = null;
|
protected String serverMsgId = null;
|
||||||
private final Conversation conversation;
|
private final Conversational conversation;
|
||||||
protected Transferable transferable = null;
|
protected Transferable transferable = null;
|
||||||
private Message mNextMessage = null;
|
private Message mNextMessage = null;
|
||||||
private Message mPreviousMessage = null;
|
private Message mPreviousMessage = null;
|
||||||
|
@ -105,15 +105,15 @@ public class Message extends AbstractEntity {
|
||||||
private List<MucOptions.User> counterparts;
|
private List<MucOptions.User> counterparts;
|
||||||
private WeakReference<MucOptions.User> user;
|
private WeakReference<MucOptions.User> user;
|
||||||
|
|
||||||
private Message(Conversation conversation) {
|
private Message(Conversational conversation) {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(Conversation conversation, String body, int encryption) {
|
public Message(Conversational conversation, String body, int encryption) {
|
||||||
this(conversation, body, encryption, STATUS_UNSEND);
|
this(conversation, body, encryption, STATUS_UNSEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(Conversation conversation, String body, int encryption, int status) {
|
public Message(Conversational conversation, String body, int encryption, int status) {
|
||||||
this(conversation, java.util.UUID.randomUUID().toString(),
|
this(conversation, java.util.UUID.randomUUID().toString(),
|
||||||
conversation.getUuid(),
|
conversation.getUuid(),
|
||||||
conversation.getJid() == null ? null : conversation.getJid().asBareJid(),
|
conversation.getJid() == null ? null : conversation.getJid().asBareJid(),
|
||||||
|
@ -136,7 +136,7 @@ public class Message extends AbstractEntity {
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message(final Conversation conversation, final String uuid, final String conversationUUid, final Jid counterpart,
|
protected Message(final Conversational conversation, final String uuid, final String conversationUUid, final Jid counterpart,
|
||||||
final Jid trueCounterpart, final String body, final long timeSent,
|
final Jid trueCounterpart, final String body, final long timeSent,
|
||||||
final int encryption, final int status, final int type, final boolean carbon,
|
final int encryption, final int status, final int type, final boolean carbon,
|
||||||
final String remoteMsgId, final String relativeFilePath,
|
final String remoteMsgId, final String relativeFilePath,
|
||||||
|
@ -252,7 +252,7 @@ public class Message extends AbstractEntity {
|
||||||
} else {
|
} else {
|
||||||
values.put(TRUE_COUNTERPART, trueCounterpart.toString());
|
values.put(TRUE_COUNTERPART, trueCounterpart.toString());
|
||||||
}
|
}
|
||||||
values.put(BODY, body.length() > Config.MAX_STORAGE_MESSAGE_CHARS ? body.substring(0,Config.MAX_STORAGE_MESSAGE_CHARS) : body);
|
values.put(BODY, body.length() > Config.MAX_STORAGE_MESSAGE_CHARS ? body.substring(0, Config.MAX_STORAGE_MESSAGE_CHARS) : body);
|
||||||
values.put(TIME_SENT, timeSent);
|
values.put(TIME_SENT, timeSent);
|
||||||
values.put(ENCRYPTION, encryption);
|
values.put(ENCRYPTION, encryption);
|
||||||
values.put(STATUS, status);
|
values.put(STATUS, status);
|
||||||
|
@ -262,11 +262,11 @@ public class Message extends AbstractEntity {
|
||||||
values.put(RELATIVE_FILE_PATH, relativeFilePath);
|
values.put(RELATIVE_FILE_PATH, relativeFilePath);
|
||||||
values.put(SERVER_MSG_ID, serverMsgId);
|
values.put(SERVER_MSG_ID, serverMsgId);
|
||||||
values.put(FINGERPRINT, axolotlFingerprint);
|
values.put(FINGERPRINT, axolotlFingerprint);
|
||||||
values.put(READ,read ? 1 : 0);
|
values.put(READ, read ? 1 : 0);
|
||||||
values.put(EDITED, edited);
|
values.put(EDITED, edited);
|
||||||
values.put(OOB, oob ? 1 : 0);
|
values.put(OOB, oob ? 1 : 0);
|
||||||
values.put(ERROR_MESSAGE,errorMessage);
|
values.put(ERROR_MESSAGE, errorMessage);
|
||||||
values.put(READ_BY_MARKERS,ReadByMarker.toJson(readByMarkers).toString());
|
values.put(READ_BY_MARKERS, ReadByMarker.toJson(readByMarkers).toString());
|
||||||
values.put(MARKABLE, markable ? 1 : 0);
|
values.put(MARKABLE, markable ? 1 : 0);
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ public class Message extends AbstractEntity {
|
||||||
return conversationUuid;
|
return conversationUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Conversation getConversation() {
|
public Conversational getConversation() {
|
||||||
return this.conversation;
|
return this.conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ public class Message extends AbstractEntity {
|
||||||
}
|
}
|
||||||
return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
|
return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
|
||||||
&& matchingCounterpart
|
&& matchingCounterpart
|
||||||
&& (body.equals(otherBody) ||(message.getEncryption() == Message.ENCRYPTION_PGP && hasUuid));
|
&& (body.equals(otherBody) || (message.getEncryption() == Message.ENCRYPTION_PGP && hasUuid));
|
||||||
} else {
|
} else {
|
||||||
return this.remoteMsgId == null
|
return this.remoteMsgId == null
|
||||||
&& matchingCounterpart
|
&& matchingCounterpart
|
||||||
|
@ -508,36 +508,46 @@ public class Message extends AbstractEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message next() {
|
public Message next() {
|
||||||
synchronized (this.conversation.messages) {
|
if (this.conversation instanceof Conversation) {
|
||||||
|
final Conversation conversation = (Conversation) this.conversation;
|
||||||
|
synchronized (conversation.messages) {
|
||||||
if (this.mNextMessage == null) {
|
if (this.mNextMessage == null) {
|
||||||
int index = this.conversation.messages.indexOf(this);
|
int index = conversation.messages.indexOf(this);
|
||||||
if (index < 0 || index >= this.conversation.messages.size() - 1) {
|
if (index < 0 || index >= conversation.messages.size() - 1) {
|
||||||
this.mNextMessage = null;
|
this.mNextMessage = null;
|
||||||
} else {
|
} else {
|
||||||
this.mNextMessage = this.conversation.messages.get(index + 1);
|
this.mNextMessage = conversation.messages.get(index + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.mNextMessage;
|
return this.mNextMessage;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Calling next should be disabled for stubs");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message prev() {
|
public Message prev() {
|
||||||
synchronized (this.conversation.messages) {
|
if (this.conversation instanceof Conversation) {
|
||||||
|
final Conversation conversation = (Conversation) this.conversation;
|
||||||
|
synchronized (conversation.messages) {
|
||||||
if (this.mPreviousMessage == null) {
|
if (this.mPreviousMessage == null) {
|
||||||
int index = this.conversation.messages.indexOf(this);
|
int index = conversation.messages.indexOf(this);
|
||||||
if (index <= 0 || index > this.conversation.messages.size()) {
|
if (index <= 0 || index > conversation.messages.size()) {
|
||||||
this.mPreviousMessage = null;
|
this.mPreviousMessage = null;
|
||||||
} else {
|
} else {
|
||||||
this.mPreviousMessage = this.conversation.messages.get(index - 1);
|
this.mPreviousMessage = conversation.messages.get(index - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.mPreviousMessage;
|
return this.mPreviousMessage;
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Calling prev should be disabled for stubs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLastCorrectableMessage() {
|
public boolean isLastCorrectableMessage() {
|
||||||
Message next = next();
|
Message next = next();
|
||||||
while(next != null) {
|
while (next != null) {
|
||||||
if (next.isCorrectable()) {
|
if (next.isCorrectable()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -566,7 +576,7 @@ public class Message extends AbstractEntity {
|
||||||
this.edited() == message.edited() &&
|
this.edited() == message.edited() &&
|
||||||
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
||||||
this.getBody().length() + message.getBody().length() <= Config.MAX_DISPLAY_MESSAGE_CHARS &&
|
this.getBody().length() + message.getBody().length() <= Config.MAX_DISPLAY_MESSAGE_CHARS &&
|
||||||
!message.isGeoUri()&&
|
!message.isGeoUri() &&
|
||||||
!this.isGeoUri() &&
|
!this.isGeoUri() &&
|
||||||
!message.treatAsDownloadable() &&
|
!message.treatAsDownloadable() &&
|
||||||
!this.treatAsDownloadable() &&
|
!this.treatAsDownloadable() &&
|
||||||
|
@ -575,7 +585,7 @@ public class Message extends AbstractEntity {
|
||||||
!this.bodyIsOnlyEmojis() &&
|
!this.bodyIsOnlyEmojis() &&
|
||||||
!message.bodyIsOnlyEmojis() &&
|
!message.bodyIsOnlyEmojis() &&
|
||||||
((this.axolotlFingerprint == null && message.axolotlFingerprint == null) || this.axolotlFingerprint.equals(message.getFingerprint())) &&
|
((this.axolotlFingerprint == null && message.axolotlFingerprint == null) || this.axolotlFingerprint.equals(message.getFingerprint())) &&
|
||||||
UIHelper.sameDay(message.getTimeSent(),this.getTimeSent()) &&
|
UIHelper.sameDay(message.getTimeSent(), this.getTimeSent()) &&
|
||||||
this.getReadByMarkers().equals(message.getReadByMarkers()) &&
|
this.getReadByMarkers().equals(message.getReadByMarkers()) &&
|
||||||
!this.conversation.getJid().asBareJid().equals(Config.BUG_REPORTS)
|
!this.conversation.getJid().asBareJid().equals(Config.BUG_REPORTS)
|
||||||
);
|
);
|
||||||
|
@ -599,7 +609,8 @@ public class Message extends AbstractEntity {
|
||||||
return this.counterparts;
|
return this.counterparts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MergeSeparator {}
|
public static class MergeSeparator {
|
||||||
|
}
|
||||||
|
|
||||||
public SpannableStringBuilder getMergedBody() {
|
public SpannableStringBuilder getMergedBody() {
|
||||||
SpannableStringBuilder body = new SpannableStringBuilder(this.body.trim());
|
SpannableStringBuilder body = new SpannableStringBuilder(this.body.trim());
|
||||||
|
@ -624,7 +635,7 @@ public class Message extends AbstractEntity {
|
||||||
public int getMergedStatus() {
|
public int getMergedStatus() {
|
||||||
int status = this.status;
|
int status = this.status;
|
||||||
Message current = this;
|
Message current = this;
|
||||||
while(current.mergeable(current.next())) {
|
while (current.mergeable(current.next())) {
|
||||||
current = current.next();
|
current = current.next();
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
break;
|
break;
|
||||||
|
@ -637,7 +648,7 @@ public class Message extends AbstractEntity {
|
||||||
public long getMergedTimeSent() {
|
public long getMergedTimeSent() {
|
||||||
long time = this.timeSent;
|
long time = this.timeSent;
|
||||||
Message current = this;
|
Message current = this;
|
||||||
while(current.mergeable(current.next())) {
|
while (current.mergeable(current.next())) {
|
||||||
current = current.next();
|
current = current.next();
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
break;
|
break;
|
||||||
|
@ -708,11 +719,11 @@ public class Message extends AbstractEntity {
|
||||||
if (treatAsDownloadable == null) {
|
if (treatAsDownloadable == null) {
|
||||||
try {
|
try {
|
||||||
final String[] lines = body.split("\n");
|
final String[] lines = body.split("\n");
|
||||||
if (lines.length ==0) {
|
if (lines.length == 0) {
|
||||||
treatAsDownloadable = false;
|
treatAsDownloadable = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for(String line : lines) {
|
for (String line : lines) {
|
||||||
if (line.contains("\\s+")) {
|
if (line.contains("\\s+")) {
|
||||||
treatAsDownloadable = false;
|
treatAsDownloadable = false;
|
||||||
return false;
|
return false;
|
||||||
|
@ -735,7 +746,7 @@ public class Message extends AbstractEntity {
|
||||||
|
|
||||||
public synchronized boolean bodyIsOnlyEmojis() {
|
public synchronized boolean bodyIsOnlyEmojis() {
|
||||||
if (isEmojisOnly == null) {
|
if (isEmojisOnly == null) {
|
||||||
isEmojisOnly = Emoticons.isOnlyEmoji(body.replaceAll("\\s",""));
|
isEmojisOnly = Emoticons.isOnlyEmoji(body.replaceAll("\\s", ""));
|
||||||
}
|
}
|
||||||
return isEmojisOnly;
|
return isEmojisOnly;
|
||||||
}
|
}
|
||||||
|
@ -848,8 +859,8 @@ public class Message extends AbstractEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPreviousEncryption() {
|
private int getPreviousEncryption() {
|
||||||
for (Message iterator = this.prev(); iterator != null; iterator = iterator.prev()){
|
for (Message iterator = this.prev(); iterator != null; iterator = iterator.prev()) {
|
||||||
if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
|
if (iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return iterator.getEncryption();
|
return iterator.getEncryption();
|
||||||
|
@ -858,13 +869,18 @@ public class Message extends AbstractEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getNextEncryption() {
|
private int getNextEncryption() {
|
||||||
for (Message iterator = this.next(); iterator != null; iterator = iterator.next()){
|
if (this.conversation instanceof Conversation) {
|
||||||
if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
|
Conversation conversation = (Conversation) this.conversation;
|
||||||
|
for (Message iterator = this.next(); iterator != null; iterator = iterator.next()) {
|
||||||
|
if (iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return iterator.getEncryption();
|
return iterator.getEncryption();
|
||||||
}
|
}
|
||||||
return conversation.getNextEncryption();
|
return conversation.getNextEncryption();
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("This should never be called since isInValidSession should be disabled for stubs");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidInSession() {
|
public boolean isValidInSession() {
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
|
||||||
|
public class StubConversation implements Conversational {
|
||||||
|
|
||||||
|
private final Account account;
|
||||||
|
private final String uuid;
|
||||||
|
private final Jid jid;
|
||||||
|
private final int mode;
|
||||||
|
|
||||||
|
public StubConversation(Account account, String uuid, Jid jid, int mode) {
|
||||||
|
this.account = account;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.jid = jid;
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Account getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Contact getContact() {
|
||||||
|
return account.getRoster().getContact(jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Jid getJid() {
|
||||||
|
return jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ public class MessageGenerator extends AbstractGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagePacket preparePacket(Message message) {
|
private MessagePacket preparePacket(Message message) {
|
||||||
Conversation conversation = message.getConversation();
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
Account account = conversation.getAccount();
|
Account account = conversation.getAccount();
|
||||||
MessagePacket packet = new MessagePacket();
|
MessagePacket packet = new MessagePacket();
|
||||||
final boolean isWithSelf = conversation.getContact().isSelf();
|
final boolean isWithSelf = conversation.getContact().isSelf();
|
||||||
|
|
|
@ -674,8 +674,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
return getMessages(conversations, limit, -1);
|
return getMessages(conversations, limit, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Message> getMessages(Conversation conversation, int limit,
|
public ArrayList<Message> getMessages(Conversation conversation, int limit, long timestamp) {
|
||||||
long timestamp) {
|
|
||||||
ArrayList<Message> list = new ArrayList<>();
|
ArrayList<Message> list = new ArrayList<>();
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
|
@ -705,18 +704,22 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cursor getMessageSearchCursor(String term) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
String SQL = "SELECT "+Message.TABLENAME+".*,"+Conversation.TABLENAME+'.'+Conversation.CONTACTJID+','+Conversation.TABLENAME+'.'+Conversation.ACCOUNT+','+Conversation.TABLENAME+'.'+Conversation.MODE+" FROM "+Message.TABLENAME +" join "+Conversation.TABLENAME+" on "+Message.TABLENAME+'.'+Message.CONVERSATION+'='+Conversation.TABLENAME+'.'+Conversation.UUID+" where "+Message.BODY +" LIKE ? limit 200";
|
||||||
|
return db.rawQuery(SQL,new String[]{'%'+term+'%'});
|
||||||
|
}
|
||||||
|
|
||||||
public Iterable<Message> getMessagesIterable(final Conversation conversation) {
|
public Iterable<Message> getMessagesIterable(final Conversation conversation) {
|
||||||
return new Iterable<Message>() {
|
return () -> {
|
||||||
@Override
|
|
||||||
public Iterator<Message> iterator() {
|
|
||||||
class MessageIterator implements Iterator<Message> {
|
class MessageIterator implements Iterator<Message> {
|
||||||
SQLiteDatabase db = getReadableDatabase();
|
private SQLiteDatabase db = getReadableDatabase();
|
||||||
String[] selectionArgs = {conversation.getUuid()};
|
private String[] selectionArgs = {conversation.getUuid()};
|
||||||
Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
|
private Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
|
||||||
+ "=?", selectionArgs, null, null, Message.TIME_SENT
|
+ "=?", selectionArgs, null, null, Message.TIME_SENT
|
||||||
+ " ASC", null);
|
+ " ASC", null);
|
||||||
|
|
||||||
public MessageIterator() {
|
private MessageIterator() {
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,7 +741,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new MessageIterator();
|
return new MessageIterator();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Bookmark;
|
import eu.siacs.conversations.entities.Bookmark;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.ListItem;
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.MucOptions;
|
import eu.siacs.conversations.entities.MucOptions;
|
||||||
|
@ -388,20 +389,21 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap get(Message message, int size, boolean cachedOnly) {
|
public Bitmap get(Message message, int size, boolean cachedOnly) {
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversational conversation = message.getConversation();
|
||||||
if (message.getType() == Message.TYPE_STATUS && message.getCounterparts() != null && message.getCounterparts().size() > 1) {
|
if (message.getType() == Message.TYPE_STATUS && message.getCounterparts() != null && message.getCounterparts().size() > 1) {
|
||||||
return get(message.getCounterparts(), size, cachedOnly);
|
return get(message.getCounterparts(), size, cachedOnly);
|
||||||
} else if (message.getStatus() == Message.STATUS_RECEIVED) {
|
} else if (message.getStatus() == Message.STATUS_RECEIVED) {
|
||||||
Contact c = message.getContact();
|
Contact c = message.getContact();
|
||||||
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
|
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
|
||||||
return get(c, size, cachedOnly);
|
return get(c, size, cachedOnly);
|
||||||
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
} else if (conversation instanceof Conversation && message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||||
final Jid trueCounterpart = message.getTrueCounterpart();
|
final Jid trueCounterpart = message.getTrueCounterpart();
|
||||||
|
final MucOptions mucOptions = ((Conversation) conversation).getMucOptions();
|
||||||
MucOptions.User user;
|
MucOptions.User user;
|
||||||
if (trueCounterpart != null) {
|
if (trueCounterpart != null) {
|
||||||
user = conversation.getMucOptions().findUserByRealJid(trueCounterpart);
|
user = mucOptions.findUserByRealJid(trueCounterpart);
|
||||||
} else {
|
} else {
|
||||||
user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
|
user = mucOptions.findUserByFullJid(message.getCounterpart());
|
||||||
}
|
}
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return getImpl(user, size, cachedOnly);
|
return getImpl(user, size, cachedOnly);
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.services;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
|
import eu.siacs.conversations.entities.IndividualMessage;
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.entities.StubConversation;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
|
import eu.siacs.conversations.utils.Cancellable;
|
||||||
|
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
public class MessageSearchTask implements Runnable, Cancellable {
|
||||||
|
|
||||||
|
private static final ReplacingSerialSingleThreadExecutor EXECUTOR = new ReplacingSerialSingleThreadExecutor(MessageSearchTask.class.getName());
|
||||||
|
|
||||||
|
private final XmppConnectionService xmppConnectionService;
|
||||||
|
private final String term;
|
||||||
|
private final OnSearchResultsAvailable onSearchResultsAvailable;
|
||||||
|
|
||||||
|
private boolean isCancelled = false;
|
||||||
|
|
||||||
|
private MessageSearchTask(XmppConnectionService xmppConnectionService, String term, OnSearchResultsAvailable onSearchResultsAvailable) {
|
||||||
|
this.xmppConnectionService = xmppConnectionService;
|
||||||
|
this.term = term;
|
||||||
|
this.onSearchResultsAvailable = onSearchResultsAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
this.isCancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long startTimestamp = SystemClock.elapsedRealtime();
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
final HashMap<String,Conversational> conversationCache = new HashMap<>();
|
||||||
|
final List<Message> result = new ArrayList<>();
|
||||||
|
cursor = xmppConnectionService.databaseBackend.getMessageSearchCursor(term);
|
||||||
|
while(cursor.moveToNext()) {
|
||||||
|
final String conversationUuid = cursor.getString(cursor.getColumnIndex(Message.CONVERSATION));
|
||||||
|
Conversational conversation;
|
||||||
|
if (conversationCache.containsKey(conversationUuid)) {
|
||||||
|
conversation = conversationCache.get(conversationUuid);
|
||||||
|
} else {
|
||||||
|
String accountUuid = cursor.getString(cursor.getColumnIndex(Conversation.ACCOUNT));
|
||||||
|
String contactJid = cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID));
|
||||||
|
int mode = cursor.getInt(cursor.getColumnIndex(Conversation.MODE));
|
||||||
|
conversation = findOrGenerateStub(conversationUuid, accountUuid, contactJid, mode);
|
||||||
|
conversationCache.put(conversationUuid, conversation);
|
||||||
|
}
|
||||||
|
Message message = IndividualMessage.fromCursor(cursor, conversation);
|
||||||
|
result.add(message);
|
||||||
|
}
|
||||||
|
long stopTimestamp = SystemClock.elapsedRealtime();
|
||||||
|
Log.d(Config.LOGTAG,"found "+result.size()+" messages in "+(stopTimestamp - startTimestamp)+"ms");
|
||||||
|
onSearchResultsAvailable.onSearchResultsAvailable(term, result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d(Config.LOGTAG,"exception while searching ",e);
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Conversational findOrGenerateStub(String conversationUuid, String accountUuid, String contactJid, int mode) throws Exception {
|
||||||
|
Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid);
|
||||||
|
if (conversation != null) {
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
Account account = xmppConnectionService.findAccountByUuid(accountUuid);
|
||||||
|
Jid jid = Jid.of(contactJid);
|
||||||
|
if (account != null && jid != null) {
|
||||||
|
return new StubConversation(account, conversationUuid, jid.asBareJid(), mode);
|
||||||
|
}
|
||||||
|
throw new Exception("Unable to generate stub for "+contactJid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeInBackground() {
|
||||||
|
EXECUTOR.execute(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void search(XmppConnectionService xmppConnectionService, String term, OnSearchResultsAvailable onSearchResultsAvailable) {
|
||||||
|
new MessageSearchTask(xmppConnectionService, term, onSearchResultsAvailable).executeInBackground();
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.ui.ConversationsActivity;
|
import eu.siacs.conversations.ui.ConversationsActivity;
|
||||||
|
@ -77,11 +78,12 @@ public class NotificationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean notify(final Message message) {
|
public boolean notify(final Message message) {
|
||||||
|
final Conversation conversation = (Conversation) message.getConversation();
|
||||||
return message.getStatus() == Message.STATUS_RECEIVED
|
return message.getStatus() == Message.STATUS_RECEIVED
|
||||||
&& notificationsEnabled()
|
&& notificationsEnabled()
|
||||||
&& !message.getConversation().isMuted()
|
&& !conversation.isMuted()
|
||||||
&& (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message))
|
&& (conversation.alwaysNotify() || wasHighlightedOrPrivate(message))
|
||||||
&& (!message.getConversation().isWithStranger() || notificationsFromStrangers())
|
&& (!conversation.isWithStranger() || notificationsFromStrangers())
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ public class NotificationService {
|
||||||
public void pushFromBacklog(final Message message) {
|
public void pushFromBacklog(final Message message) {
|
||||||
if (notify(message)) {
|
if (notify(message)) {
|
||||||
synchronized (notifications) {
|
synchronized (notifications) {
|
||||||
getBacklogMessageCounter(message.getConversation()).incrementAndGet();
|
getBacklogMessageCounter((Conversation) message.getConversation()).incrementAndGet();
|
||||||
pushToStack(message);
|
pushToStack(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +241,7 @@ public class NotificationService {
|
||||||
if (messages != null && messages.size() > 0) {
|
if (messages != null && messages.size() > 0) {
|
||||||
Message last = messages.get(messages.size() - 1);
|
Message last = messages.get(messages.size() - 1);
|
||||||
if (last.getStatus() != Message.STATUS_RECEIVED) {
|
if (last.getStatus() != Message.STATUS_RECEIVED) {
|
||||||
if (mXmppConnectionService.markRead(last.getConversation(), false)) {
|
if (mXmppConnectionService.markRead((Conversation) last.getConversation(), false)) {
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,7 +339,7 @@ public class NotificationService {
|
||||||
Conversation conversation = null;
|
Conversation conversation = null;
|
||||||
for (final ArrayList<Message> messages : notifications.values()) {
|
for (final ArrayList<Message> messages : notifications.values()) {
|
||||||
if (messages.size() > 0) {
|
if (messages.size() > 0) {
|
||||||
conversation = messages.get(0).getConversation();
|
conversation = (Conversation) messages.get(0).getConversation();
|
||||||
final String name = conversation.getName().toString();
|
final String name = conversation.getName().toString();
|
||||||
SpannableString styledString;
|
SpannableString styledString;
|
||||||
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
|
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
|
||||||
|
@ -376,7 +378,7 @@ public class NotificationService {
|
||||||
private Builder buildSingleConversations(final ArrayList<Message> messages) {
|
private Builder buildSingleConversations(final ArrayList<Message> messages) {
|
||||||
final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
|
final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
|
||||||
if (messages.size() >= 1) {
|
if (messages.size() >= 1) {
|
||||||
final Conversation conversation = messages.get(0).getConversation();
|
final Conversation conversation = (Conversation) messages.get(0).getConversation();
|
||||||
final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString());
|
final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString());
|
||||||
mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
|
mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
|
||||||
.get(conversation, getPixel(64)));
|
.get(conversation, getPixel(64)));
|
||||||
|
@ -510,7 +512,7 @@ public class NotificationService {
|
||||||
private void modifyForTextOnly(final Builder builder, final UnreadConversation.Builder uBuilder, final ArrayList<Message> messages) {
|
private void modifyForTextOnly(final Builder builder, final UnreadConversation.Builder uBuilder, final ArrayList<Message> messages) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(mXmppConnectionService.getString(R.string.me));
|
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(mXmppConnectionService.getString(R.string.me));
|
||||||
Conversation conversation = messages.get(0).getConversation();
|
final Conversation conversation = (Conversation) messages.get(0).getConversation();
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
messagingStyle.setConversationTitle(conversation.getName());
|
messagingStyle.setConversationTitle(conversation.getName());
|
||||||
}
|
}
|
||||||
|
@ -632,7 +634,7 @@ public class NotificationService {
|
||||||
return (actionId * NOTIFICATION_ID_MULTIPLIER) + (uuid.hashCode() % NOTIFICATION_ID_MULTIPLIER);
|
return (actionId * NOTIFICATION_ID_MULTIPLIER) + (uuid.hashCode() % NOTIFICATION_ID_MULTIPLIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int generateRequestCode(Conversation conversation, int actionId) {
|
private int generateRequestCode(Conversational conversation, int actionId) {
|
||||||
return generateRequestCode(conversation.getUuid(), actionId);
|
return generateRequestCode(conversation.getUuid(), actionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +642,7 @@ public class NotificationService {
|
||||||
return createContentIntent(message.getConversationUuid(), message.getUuid());
|
return createContentIntent(message.getConversationUuid(), message.getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent createContentIntent(final Conversation conversation) {
|
private PendingIntent createContentIntent(final Conversational conversation) {
|
||||||
return createContentIntent(conversation.getUuid(), null);
|
return createContentIntent(conversation.getUuid(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,13 +694,18 @@ public class NotificationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean wasHighlightedOrPrivate(final Message message) {
|
private boolean wasHighlightedOrPrivate(final Message message) {
|
||||||
final String nick = message.getConversation().getMucOptions().getActualNick();
|
if (message.getConversation() instanceof Conversation) {
|
||||||
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
|
final String nick = conversation.getMucOptions().getActualNick();
|
||||||
final Pattern highlight = generateNickHighlightPattern(nick);
|
final Pattern highlight = generateNickHighlightPattern(nick);
|
||||||
if (message.getBody() == null || nick == null) {
|
if (message.getBody() == null || nick == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Matcher m = highlight.matcher(message.getBody());
|
final Matcher m = highlight.matcher(message.getBody());
|
||||||
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
|
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pattern generateNickHighlightPattern(final String nick) {
|
public static Pattern generateNickHighlightPattern(final String nick) {
|
||||||
|
|
|
@ -98,6 +98,7 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.ui.SettingsActivity;
|
import eu.siacs.conversations.ui.SettingsActivity;
|
||||||
import eu.siacs.conversations.ui.UiCallback;
|
import eu.siacs.conversations.ui.UiCallback;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||||
|
@ -534,6 +535,10 @@ public class XmppConnectionService extends Service {
|
||||||
return find(getConversations(), account, jid);
|
return find(getConversations(), account, jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void search(String term, OnSearchResultsAvailable onSearchResultsAvailable) {
|
||||||
|
MessageSearchTask.search(this, term, onSearchResultsAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
final String action = intent == null ? null : intent.getAction();
|
final String action = intent == null ? null : intent.getAction();
|
||||||
|
@ -781,7 +786,7 @@ public class XmppConnectionService extends Service {
|
||||||
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
|
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
if (dismissAfterReply) {
|
if (dismissAfterReply) {
|
||||||
markRead(message.getConversation(), true);
|
markRead((Conversation) message.getConversation(), true);
|
||||||
} else {
|
} else {
|
||||||
mNotificationService.pushFromDirectReply(message);
|
mNotificationService.pushFromDirectReply(message);
|
||||||
}
|
}
|
||||||
|
@ -1142,7 +1147,7 @@ public class XmppConnectionService extends Service {
|
||||||
databaseBackend.updateAccount(account);
|
databaseBackend.updateAccount(account);
|
||||||
mNotificationService.updateErrorNotification();
|
mNotificationService.updateErrorNotification();
|
||||||
}
|
}
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversation conversation = (Conversation) message.getConversation();
|
||||||
account.deactivateGracePeriod();
|
account.deactivateGracePeriod();
|
||||||
MessagePacket packet = null;
|
MessagePacket packet = null;
|
||||||
final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
|
final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
|
||||||
|
@ -3242,6 +3247,15 @@ public class XmppConnectionService extends Service {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Account findAccountByUuid(final String uuid) {
|
||||||
|
for(Account account : this.accounts) {
|
||||||
|
if (account.getUuid().equals(uuid)) {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Conversation findConversationByUuid(String uuid) {
|
public Conversation findConversationByUuid(String uuid) {
|
||||||
for (Conversation conversation : getConversations()) {
|
for (Conversation conversation : getConversations()) {
|
||||||
if (conversation.getUuid().equals(uuid)) {
|
if (conversation.getUuid().equals(uuid)) {
|
||||||
|
@ -3528,7 +3542,9 @@ public class XmppConnectionService extends Service {
|
||||||
markMessage(msg, Message.STATUS_WAITING);
|
markMessage(msg, Message.STATUS_WAITING);
|
||||||
this.resendMessage(msg, false);
|
this.resendMessage(msg, false);
|
||||||
}
|
}
|
||||||
message.getConversation().sort();
|
if (message.getConversation() instanceof Conversation) {
|
||||||
|
((Conversation) message.getConversation()).sort();
|
||||||
|
}
|
||||||
updateConversationUi();
|
updateConversationUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -705,7 +705,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
message.setEdited(message.getUuid());
|
message.setEdited(message.getUuid());
|
||||||
message.setUuid(UUID.randomUUID().toString());
|
message.setUuid(UUID.randomUUID().toString());
|
||||||
}
|
}
|
||||||
switch (message.getConversation().getNextEncryption()) {
|
switch (conversation.getNextEncryption()) {
|
||||||
case Message.ENCRYPTION_PGP:
|
case Message.ENCRYPTION_PGP:
|
||||||
sendPgpMessage(message);
|
sendPgpMessage(message);
|
||||||
break;
|
break;
|
||||||
|
@ -932,11 +932,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
messageListAdapter.setOnContactPictureClicked(message -> {
|
messageListAdapter.setOnContactPictureClicked(message -> {
|
||||||
final boolean received = message.getStatus() <= Message.STATUS_RECEIVED;
|
final boolean received = message.getStatus() <= Message.STATUS_RECEIVED;
|
||||||
if (received) {
|
if (received) {
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
if (message.getConversation() instanceof Conversation && message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||||
Jid user = message.getCounterpart();
|
Jid user = message.getCounterpart();
|
||||||
if (user != null && !user.isBareJid()) {
|
if (user != null && !user.isBareJid()) {
|
||||||
final MucOptions mucOptions = message.getConversation().getMucOptions();
|
final MucOptions mucOptions = ((Conversation) message.getConversation()).getMucOptions();
|
||||||
if (mucOptions.participating() || message.getConversation().getNextCounterpart() != null) {
|
if (mucOptions.participating() || ((Conversation) message.getConversation()).getNextCounterpart() != null) {
|
||||||
if (!mucOptions.isUserInRoom(user)) {
|
if (!mucOptions.isUserInRoom(user)) {
|
||||||
Toast.makeText(getActivity(), activity.getString(R.string.user_has_left_conference, user.getResource()), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), activity.getString(R.string.user_has_left_conference, user.getResource()), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
@ -1079,7 +1079,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
if (relevantForCorrection.getType() == Message.TYPE_TEXT
|
if (relevantForCorrection.getType() == Message.TYPE_TEXT
|
||||||
&& relevantForCorrection.isLastCorrectableMessage()
|
&& relevantForCorrection.isLastCorrectableMessage()
|
||||||
&& (m.getConversation().getMucOptions().nonanonymous() || m.getConversation().getMode() == Conversation.MODE_SINGLE)) {
|
&& m.getConversation() instanceof Conversation
|
||||||
|
&& (((Conversation) m.getConversation()).getMucOptions().nonanonymous() || m.getConversation().getMode() == Conversation.MODE_SINGLE)) {
|
||||||
correctMessage.setVisible(true);
|
correctMessage.setVisible(true);
|
||||||
}
|
}
|
||||||
if ((m.isFileOrImage() && !deleted && !receiving) || (m.getType() == Message.TYPE_TEXT && !m.treatAsDownloadable())) {
|
if ((m.isFileOrImage() && !deleted && !receiving) || (m.getType() == Message.TYPE_TEXT && !m.treatAsDownloadable())) {
|
||||||
|
@ -1628,9 +1629,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
|
|
||||||
private void resendMessage(final Message message) {
|
private void resendMessage(final Message message) {
|
||||||
if (message.isFileOrImage()) {
|
if (message.isFileOrImage()) {
|
||||||
|
if (!(message.getConversation() instanceof Conversation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Conversation conversation = (Conversation) message.getConversation();
|
||||||
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
final Conversation conversation = message.getConversation();
|
|
||||||
final XmppConnection xmppConnection = conversation.getAccount().getXmppConnection();
|
final XmppConnection xmppConnection = conversation.getAccount().getXmppConnection();
|
||||||
if (!message.hasFileOnRemoteHost()
|
if (!message.hasFileOnRemoteHost()
|
||||||
&& xmppConnection != null
|
&& xmppConnection != null
|
||||||
|
|
|
@ -47,13 +47,14 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.ActivitySearchBinding;
|
import eu.siacs.conversations.databinding.ActivitySearchBinding;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
import eu.siacs.conversations.ui.util.Color;
|
import eu.siacs.conversations.ui.util.Color;
|
||||||
import eu.siacs.conversations.ui.util.Drawable;
|
import eu.siacs.conversations.ui.util.Drawable;
|
||||||
|
|
||||||
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
|
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
|
||||||
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.showKeyboard;
|
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.showKeyboard;
|
||||||
|
|
||||||
public class SearchActivity extends XmppActivity implements TextWatcher {
|
public class SearchActivity extends XmppActivity implements TextWatcher, OnSearchResultsAvailable {
|
||||||
|
|
||||||
private ActivitySearchBinding binding;
|
private ActivitySearchBinding binding;
|
||||||
private MessageAdapter messageListAdapter;
|
private MessageAdapter messageListAdapter;
|
||||||
|
@ -122,7 +123,23 @@ public class SearchActivity extends XmppActivity implements TextWatcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {
|
||||||
Log.d(Config.LOGTAG,"searching for "+s);
|
String term = s.toString().trim();
|
||||||
|
if (term.length() > 0) {
|
||||||
|
xmppConnectionService.search(s.toString().trim(), this);
|
||||||
|
} else {
|
||||||
|
this.messages.clear();
|
||||||
|
messageListAdapter.notifyDataSetChanged();
|
||||||
|
changeBackground(false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSearchResultsAvailable(String term, List<Message> messages) {
|
||||||
|
this.messages.clear();
|
||||||
|
this.messages.addAll(messages);
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
messageListAdapter.notifyDataSetChanged();
|
||||||
|
changeBackground(true, messages.size() > 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,11 +87,12 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
|
||||||
} else {
|
} else {
|
||||||
resId = R.string.shared_file_with_x;
|
resId = R.string.shared_file_with_x;
|
||||||
}
|
}
|
||||||
replaceToast(getString(resId, message.getConversation().getName()));
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
|
replaceToast(getString(resId, conversation.getName()));
|
||||||
if (mReturnToPrevious) {
|
if (mReturnToPrevious) {
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
switchToConversation(message.getConversation());
|
switchToConversation(conversation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,6 +58,7 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
|
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.Message.FileParams;
|
import eu.siacs.conversations.entities.Message.FileParams;
|
||||||
|
@ -112,15 +113,15 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
|
|
||||||
private static String removeTrailingBracket(final String url) {
|
private static String removeTrailingBracket(final String url) {
|
||||||
int numOpenBrackets = 0;
|
int numOpenBrackets = 0;
|
||||||
for(char c : url.toCharArray()) {
|
for (char c : url.toCharArray()) {
|
||||||
if (c=='(') {
|
if (c == '(') {
|
||||||
++numOpenBrackets;
|
++numOpenBrackets;
|
||||||
} else if (c==')') {
|
} else if (c == ')') {
|
||||||
--numOpenBrackets;
|
--numOpenBrackets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (numOpenBrackets != 0 && url.charAt(url.length() - 1) == ')') {
|
if (numOpenBrackets != 0 && url.charAt(url.length() - 1) == ')') {
|
||||||
return url.substring(0,url.length() - 1);
|
return url.substring(0, url.length() - 1);
|
||||||
} else {
|
} else {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
@ -530,12 +531,15 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) {
|
if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) {
|
||||||
Pattern pattern = NotificationService.generateNickHighlightPattern(message.getConversation().getMucOptions().getActualNick());
|
if (message.getConversation() instanceof Conversation) {
|
||||||
|
final Conversation conversation = (Conversation) message.getConversation();
|
||||||
|
Pattern pattern = NotificationService.generateNickHighlightPattern(conversation.getMucOptions().getActualNick());
|
||||||
Matcher matcher = pattern.matcher(body);
|
Matcher matcher = pattern.matcher(body);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
body.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
body.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Matcher matcher = Emoticons.generatePattern(body).matcher(body);
|
Matcher matcher = Emoticons.generatePattern(body).matcher(body);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
if (matcher.start() < matcher.end()) {
|
if (matcher.start() < matcher.end()) {
|
||||||
|
@ -649,7 +653,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
final Message message = getItem(position);
|
final Message message = getItem(position);
|
||||||
final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL;
|
final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL;
|
||||||
final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted());
|
final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted());
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversational conversation = message.getConversation();
|
||||||
final Account account = conversation.getAccount();
|
final Account account = conversation.getAccount();
|
||||||
final int type = getItemViewType(position);
|
final int type = getItemViewType(position);
|
||||||
ViewHolder viewHolder;
|
ViewHolder viewHolder;
|
||||||
|
@ -727,7 +731,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
viewHolder.status_message.setVisibility(View.GONE);
|
viewHolder.status_message.setVisibility(View.GONE);
|
||||||
viewHolder.contact_picture.setVisibility(View.GONE);
|
viewHolder.contact_picture.setVisibility(View.GONE);
|
||||||
viewHolder.load_more_messages.setVisibility(View.VISIBLE);
|
viewHolder.load_more_messages.setVisibility(View.VISIBLE);
|
||||||
viewHolder.load_more_messages.setOnClickListener(v -> loadMoreMessages(message.getConversation()));
|
viewHolder.load_more_messages.setOnClickListener(v -> loadMoreMessages((Conversation) message.getConversation()));
|
||||||
} else {
|
} else {
|
||||||
viewHolder.status_message.setVisibility(View.VISIBLE);
|
viewHolder.status_message.setVisibility(View.VISIBLE);
|
||||||
viewHolder.load_more_messages.setVisibility(View.GONE);
|
viewHolder.load_more_messages.setVisibility(View.GONE);
|
||||||
|
@ -794,7 +798,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
|
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
|
||||||
if (account.isPgpDecryptionServiceConnected()) {
|
if (account.isPgpDecryptionServiceConnected()) {
|
||||||
if (!account.hasPendingPgpIntent(conversation)) {
|
if (conversation instanceof Conversation && !account.hasPendingPgpIntent((Conversation) conversation)) {
|
||||||
displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
|
displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
|
||||||
} else {
|
} else {
|
||||||
displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
|
displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
|
||||||
|
@ -863,7 +867,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void resetClickListener(View... views) {
|
private static void resetClickListener(View... views) {
|
||||||
for(View view : views) {
|
for (View view : views) {
|
||||||
view.setOnClickListener(null);
|
view.setOnClickListener(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.ui.interfaces;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
|
||||||
|
public interface OnSearchResultsAvailable {
|
||||||
|
|
||||||
|
void onSearchResultsAvailable(String term, List<Message> messages);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
public interface Cancellable {
|
||||||
|
void cancel();
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
|
||||||
public class GeoHelper {
|
public class GeoHelper {
|
||||||
|
@ -38,11 +39,11 @@ public class GeoHelper {
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
return intents;
|
return intents;
|
||||||
}
|
}
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversational conversation = message.getConversation();
|
||||||
String label;
|
String label;
|
||||||
if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
|
if (conversation instanceof Conversation && conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
|
||||||
try {
|
try {
|
||||||
label = "(" + URLEncoder.encode(message.getConversation().getName().toString(), "UTF-8") + ")";
|
label = "(" + URLEncoder.encode(((Conversation)conversation).getName().toString(), "UTF-8") + ")";
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
label = "";
|
label = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecutor {
|
public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecutor {
|
||||||
|
|
||||||
|
public ReplacingSerialSingleThreadExecutor(String name) {
|
||||||
|
super(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
public ReplacingSerialSingleThreadExecutor(boolean prepareLooper) {
|
public ReplacingSerialSingleThreadExecutor(boolean prepareLooper) {
|
||||||
super(ReplacingSerialSingleThreadExecutor.class.getName(), prepareLooper);
|
super(ReplacingSerialSingleThreadExecutor.class.getName(), prepareLooper);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +13,9 @@ public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecu
|
||||||
@Override
|
@Override
|
||||||
public synchronized void execute(final Runnable r) {
|
public synchronized void execute(final Runnable r) {
|
||||||
tasks.clear();
|
tasks.clear();
|
||||||
|
if (active != null && active instanceof Cancellable) {
|
||||||
|
((Cancellable) active).cancel();
|
||||||
|
}
|
||||||
super.execute(r);
|
super.execute(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class SerialSingleThreadExecutor implements Executor {
|
||||||
|
|
||||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||||
final ArrayDeque<Runnable> tasks = new ArrayDeque<>();
|
final ArrayDeque<Runnable> tasks = new ArrayDeque<>();
|
||||||
private Runnable active;
|
protected Runnable active;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
public SerialSingleThreadExecutor(String name) {
|
public SerialSingleThreadExecutor(String name) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.ListItem;
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.MucOptions;
|
import eu.siacs.conversations.entities.MucOptions;
|
||||||
|
@ -448,7 +449,7 @@ public class UIHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMessageDisplayName(final Message message) {
|
public static String getMessageDisplayName(final Message message) {
|
||||||
final Conversation conversation = message.getConversation();
|
final Conversational conversation = message.getConversation();
|
||||||
if (message.getStatus() == Message.STATUS_RECEIVED) {
|
if (message.getStatus() == Message.STATUS_RECEIVED) {
|
||||||
final Contact contact = message.getContact();
|
final Contact contact = message.getContact();
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
|
@ -461,8 +462,8 @@ public class UIHelper {
|
||||||
return contact != null ? contact.getDisplayName() : "";
|
return contact != null ? contact.getDisplayName() : "";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation instanceof Conversation && conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
return conversation.getMucOptions().getSelf().getName();
|
return ((Conversation) conversation).getMucOptions().getSelf().getName();
|
||||||
} else {
|
} else {
|
||||||
final Jid jid = conversation.getAccount().getJid();
|
final Jid jid = conversation.getAccount().getJid();
|
||||||
return jid.getLocal() != null ? jid.getLocal() : Jid.ofDomain(jid.getDomain()).toString();
|
return jid.getLocal() != null ? jid.getLocal() : Jid.ofDomain(jid.getDomain()).toString();
|
||||||
|
|
|
@ -261,7 +261,7 @@ public class JingleConnection implements Transferable {
|
||||||
|
|
||||||
public void init(final Message message) {
|
public void init(final Message message) {
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
||||||
Conversation conversation = message.getConversation();
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
|
conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void run(XmppAxolotlMessage xmppAxolotlMessage) {
|
public void run(XmppAxolotlMessage xmppAxolotlMessage) {
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/activity_background_search"
|
android:background="?attr/activity_background_search"
|
||||||
android:divider="@android:color/transparent"
|
android:divider="@android:color/transparent"
|
||||||
android:dividerHeight="0dp"/>
|
android:dividerHeight="0dp"
|
||||||
|
android:listSelector="@android:color/transparent"
|
||||||
|
android:stackFromBottom="true"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
Loading…
Reference in New Issue