muc PM in the separate conversation

This commit is contained in:
kosyak 2023-09-15 11:28:50 +02:00
parent c62cc344b5
commit 1ca354c208
31 changed files with 427 additions and 157 deletions

View file

@ -46,7 +46,7 @@ public class ShareViaAccountActivity extends XmppActivity {
try { try {
final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT)); final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT));
final Conversation conversation = xmppConnectionService.findOrCreateConversation( final Conversation conversation = xmppConnectionService.findOrCreateConversation(
account, contact, false, false); account, contact, null, false, false, false, null);
switchToConversation(conversation, body); switchToConversation(conversation, body);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// ignore error // ignore error
@ -76,7 +76,7 @@ public class ShareViaAccountActivity extends XmppActivity {
try { try {
final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT)); final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT));
final Conversation conversation = xmppConnectionService.findOrCreateConversation( final Conversation conversation = xmppConnectionService.findOrCreateConversation(
account, contact, false, false); account, contact, null, false, false, false, null);
switchToConversation(conversation, body); switchToConversation(conversation, body);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// ignore error // ignore error

View file

@ -1532,8 +1532,18 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (contact.showInRoster() || contact.isSelf()) { if (contact.showInRoster() || contact.isSelf()) {
return true; return true;
} }
final Conversation conversation = mXmppConnectionService.find(account, jid);
return conversation != null && conversation.sentMessagesCount() > 0; Conversation conversation = mXmppConnectionService.find(account, jid, null);
if (conversation != null && conversation.sentMessagesCount() > 0) {
return true;
}
conversation = mXmppConnectionService.find(account, jid, jid);
if (conversation != null && conversation.sentMessagesCount() > 0) {
return true;
}
return false;
} }
private void completeSession(XmppAxolotlSession session) { private void completeSession(XmppAxolotlSession session) {

View file

@ -14,11 +14,13 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -51,6 +53,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public static final String CREATED = "created"; public static final String CREATED = "created";
public static final String MODE = "mode"; public static final String MODE = "mode";
public static final String ATTRIBUTES = "attributes"; public static final String ATTRIBUTES = "attributes";
public static final String NEXT_COUNTERPART = "next_counterpart";
public static final String ATTRIBUTE_MUTED_TILL = "muted_till"; public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify"; public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
@ -86,17 +89,19 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private String mFirstMamReference = null; private String mFirstMamReference = null;
protected Message replyTo = null; protected Message replyTo = null;
private WeakReference<Conversation> parentConversation = null;
public Conversation(final String name, final Account account, final Jid contactJid, public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) { final int mode, Jid nextCounterpart) {
this(java.util.UUID.randomUUID().toString(), name, null, account this(java.util.UUID.randomUUID().toString(), name, null, account
.getUuid(), contactJid, System.currentTimeMillis(), .getUuid(), contactJid, System.currentTimeMillis(),
STATUS_AVAILABLE, mode, ""); STATUS_AVAILABLE, mode, "", nextCounterpart);
this.account = account; this.account = account;
} }
public Conversation(final String uuid, final String name, final String contactUuid, public Conversation(final String uuid, final String name, final String contactUuid,
final String accountUuid, final Jid contactJid, final long created, final int status, final String accountUuid, final Jid contactJid, final long created, final int status,
final int mode, final String attributes) { final int mode, final String attributes, Jid nextCounterpart) {
this.uuid = uuid; this.uuid = uuid;
this.name = name; this.name = name;
this.contactUuid = contactUuid; this.contactUuid = contactUuid;
@ -110,9 +115,19 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
} catch (JSONException e) { } catch (JSONException e) {
this.attributes = new JSONObject(); this.attributes = new JSONObject();
} }
this.nextCounterpart = nextCounterpart;
}
public String getContactUuid() {
return contactUuid;
}
public JSONObject getAttributes() {
return attributes;
} }
public static Conversation fromCursor(Cursor cursor) { public static Conversation fromCursor(Cursor cursor) {
String counterpart = cursor.getString(cursor.getColumnIndex(NEXT_COUNTERPART));
return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)), return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(NAME)), cursor.getString(cursor.getColumnIndex(NAME)),
cursor.getString(cursor.getColumnIndex(CONTACT)), cursor.getString(cursor.getColumnIndex(CONTACT)),
@ -121,7 +136,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
cursor.getLong(cursor.getColumnIndex(CREATED)), cursor.getLong(cursor.getColumnIndex(CREATED)),
cursor.getInt(cursor.getColumnIndex(STATUS)), cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(MODE)), cursor.getInt(cursor.getColumnIndex(MODE)),
cursor.getString(cursor.getColumnIndex(ATTRIBUTES))); cursor.getString(cursor.getColumnIndex(ATTRIBUTES)),
counterpart == null ? null : JidHelper.parseOrFallbackToInvalid(counterpart));
} }
public static Message getLatestMarkableMessage(final List<Message> messages, boolean isPrivateAndNonAnonymousMuc) { public static Message getLatestMarkableMessage(final List<Message> messages, boolean isPrivateAndNonAnonymousMuc) {
@ -215,6 +231,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return null; return null;
} }
public Conversation getParentConversation() {
if (parentConversation == null) {
return null;
}
return parentConversation.get();
}
public void setParentConversation(Conversation c) {
this.parentConversation = new WeakReference<>(c);
}
public Message findUnsentMessageWithUuid(String uuid) { public Message findUnsentMessageWithUuid(String uuid) {
synchronized (this.messages) { synchronized (this.messages) {
@ -641,6 +668,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return this.accountUuid; return this.accountUuid;
} }
public Jid getContactJid() {
return contactJid;
}
public Account getAccount() { public Account getAccount() {
return this.account; return this.account;
} }
@ -680,6 +711,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
values.put(CREATED, created); values.put(CREATED, created);
values.put(STATUS, status); values.put(STATUS, status);
values.put(MODE, mode); values.put(MODE, mode);
if (nextCounterpart != null) {
values.put(NEXT_COUNTERPART, nextCounterpart.toString());
}
synchronized (this.attributes) { synchronized (this.attributes) {
values.put(ATTRIBUTES, attributes.toString()); values.put(ATTRIBUTES, attributes.toString());
} }
@ -706,6 +742,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
} }
public synchronized MucOptions getMucOptions() { public synchronized MucOptions getMucOptions() {
Conversation parent = parentConversation == null ? null : parentConversation.get();
if (parent != null) {
this.mucOptions = parent.getMucOptions();
}
if (this.mucOptions == null) { if (this.mucOptions == null) {
this.mucOptions = new MucOptions(this); this.mucOptions = new MucOptions(this);
} }
@ -984,22 +1026,69 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
} }
public void add(Message message) { public void add(Message message) {
synchronized (this.messages) { String res1 = message.getCounterpart() == null ? null : message.getCounterpart().getResource();
this.messages.add(message); String res2 = nextCounterpart == null ? null : nextCounterpart.getResource();
if (nextCounterpart == null) {
if (!message.isPrivateMessage()) {
synchronized (this.messages) {
this.messages.add(message);
}
}
} else {
if (message.isPrivateMessage() && Objects.equals(res1, res2)) {
synchronized (this.messages) {
this.messages.add(message);
}
}
} }
} }
public void prepend(int offset, Message message) { public void prepend(int offset, Message message) {
synchronized (this.messages) { String res1 = message.getCounterpart() == null ? null : message.getCounterpart().getResource();
this.messages.add(Math.min(offset, this.messages.size()), message); String res2 = nextCounterpart == null ? null : nextCounterpart.getResource();
if (nextCounterpart == null) {
if (!message.isPrivateMessage()) {
synchronized (this.messages) {
this.messages.add(Math.min(offset, this.messages.size()), message);
}
}
} else {
if (message.isPrivateMessage() && Objects.equals(res1, res2)) {
synchronized (this.messages) {
this.messages.add(Math.min(offset, this.messages.size()), message);
}
}
} }
} }
public void addAll(int index, List<Message> messages) { public void addAll(int index, List<Message> messages) {
synchronized (this.messages) { ArrayList<Message> newM = new ArrayList<>();
this.messages.addAll(index, messages);
if (nextCounterpart == null) {
for(Message m : messages) {
if (!m.isPrivateMessage()) {
newM.add(m);
}
}
} else {
for(Message m : messages) {
String res1 = m.getCounterpart() == null ? null : m.getCounterpart().getResource();
String res2 = nextCounterpart == null ? null : nextCounterpart.getResource();
if (m.isPrivateMessage() && Objects.equals(res1, res2)) {
newM.add(m);
}
}
} }
account.getPgpDecryptionService().decrypt(messages); synchronized (this.messages) {
this.messages.addAll(index, newM);
}
account.getPgpDecryptionService().decrypt(newM);
} }
public void expireOldMessages(long timestamp) { public void expireOldMessages(long timestamp) {

View file

@ -45,4 +45,6 @@ public interface Conversational {
int getMode(); int getMode();
String getUuid(); String getUuid();
Jid getNextCounterpart();
} }

View file

@ -39,11 +39,14 @@ public class StubConversation implements Conversational {
private final Jid jid; private final Jid jid;
private final int mode; private final int mode;
public StubConversation(Account account, String uuid, Jid jid, int mode) { private final Jid nexCounterpart;
public StubConversation(Account account, String uuid, Jid jid, int mode, Jid nextCounterpart) {
this.account = account; this.account = account;
this.uuid = uuid; this.uuid = uuid;
this.jid = jid; this.jid = jid;
this.mode = mode; this.mode = mode;
this.nexCounterpart = nextCounterpart;
} }
@Override @Override
@ -70,4 +73,9 @@ public class StubConversation implements Conversational {
public String getUuid() { public String getUuid() {
return uuid; return uuid;
} }
@Override
public Jid getNextCounterpart() {
return nexCounterpart;
}
} }

View file

@ -269,7 +269,6 @@ class EditActivity : AppCompatActivity(), CropImageView.OnCropImageCompleteListe
updateBackgroundBitmap(bitmap) updateBackgroundBitmap(bitmap)
layoutParams.width = bitmap.width layoutParams.width = bitmap.width
layoutParams.height = bitmap.height layoutParams.height = bitmap.height
android.util.Log.e("31fd", bitmap.height.toString() + " " + height)
translationY = max((height - bitmap.height) / 2f, 0f) translationY = max((height - bitmap.height) / 2f, 0f)
requestLayout() requestLayout()

View file

@ -341,7 +341,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
final Element error = packet.findChild("error"); final Element error = packet.findChild("error");
final boolean pingWorthyError = error != null && (error.hasChild("not-acceptable") || error.hasChild("remote-server-timeout") || error.hasChild("remote-server-not-found")); final boolean pingWorthyError = error != null && (error.hasChild("not-acceptable") || error.hasChild("remote-server-timeout") || error.hasChild("remote-server-not-found"));
if (pingWorthyError) { if (pingWorthyError) {
Conversation conversation = mXmppConnectionService.find(account, from); Conversation conversation = mXmppConnectionService.find(account, from, null);
if (conversation != null && conversation.getMode() == Conversational.MODE_MULTI) { if (conversation != null && conversation.getMode() == Conversational.MODE_MULTI) {
if (conversation.getMucOptions().online()) { if (conversation.getMucOptions().online()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received ping worthy error for seemingly online muc at " + from); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received ping worthy error for seemingly online muc at " + from);
@ -463,9 +463,16 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} }
Jid nextCounterpart = null;
final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain().toEscapedString());
if (conversationIsProbablyMuc && !isTypeGroupChat) {
nextCounterpart = counterpart;
}
if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null) && !isMucStatusMessage) { if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null) && !isMucStatusMessage) {
final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain().toEscapedString()); final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), null, conversationIsProbablyMuc, nextCounterpart != null, false, nextCounterpart);
final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false);
final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI; final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
if (serverMsgId == null) { if (serverMsgId == null) {
@ -545,7 +552,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
message = trial; message = trial;
} }
if (message == null) { if (message == null) {
if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart), isTypeGroupChat, packet)) {
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
if (query != null && status == Message.STATUS_SEND && remoteMsgId != null) { if (query != null && status == Message.STATUS_SEND && remoteMsgId != null) {
@ -649,7 +656,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) { if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
replacedMessage.markUnread(); replacedMessage.markUnread();
} }
extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart), isTypeGroupChat, packet);
mXmppConnectionService.updateMessage(replacedMessage, uuid); mXmppConnectionService.updateMessage(replacedMessage, uuid);
if (mXmppConnectionService.confirmMessages() if (mXmppConnectionService.confirmMessages()
&& replacedMessage.getStatus() == Message.STATUS_RECEIVED && replacedMessage.getStatus() == Message.STATUS_RECEIVED
@ -733,7 +740,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
if (query == null) { if (query == null) {
extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart), isTypeGroupChat, packet);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
@ -759,7 +766,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} else if (!packet.hasChild("body")) { //no body } else if (!packet.hasChild("body")) { //no body
final Conversation conversation = mXmppConnectionService.find(account, from.asBareJid()); final Conversation conversation = mXmppConnectionService.find(account, from.asBareJid(), nextCounterpart);
if (axolotlEncrypted != null) { if (axolotlEncrypted != null) {
Jid origin; Jid origin;
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
@ -784,7 +791,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} }
if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart), isTypeGroupChat, packet)) {
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
@ -864,7 +871,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
final Element description = child.findChild("description"); final Element description = child.findChild("description");
final String namespace = description == null ? null : description.getNamespace(); final String namespace = description == null ? null : description.getNamespace();
if (Namespace.JINGLE_APPS_RTP.equals(namespace)) { if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false); final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), null, false, false, false, nextCounterpart);
final Message preExistingMessage = c.findRtpSession(sessionId, status); final Message preExistingMessage = c.findRtpSession(sessionId, status);
if (preExistingMessage != null) { if (preExistingMessage != null) {
preExistingMessage.setServerMsgId(serverMsgId); preExistingMessage.setServerMsgId(serverMsgId);
@ -885,7 +892,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} else if ("proceed".equals(action)) { } else if ("proceed".equals(action)) {
//status needs to be flipped to find the original propose //status needs to be flipped to find the original propose
final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false); final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), null, false, false, false, nextCounterpart);
final int s = packet.fromAccount(account) ? Message.STATUS_RECEIVED : Message.STATUS_SEND; final int s = packet.fromAccount(account) ? Message.STATUS_RECEIVED : Message.STATUS_SEND;
final Message message = c.findRtpSession(sessionId, s); final Message message = c.findRtpSession(sessionId, s);
if (message != null) { if (message != null) {
@ -906,7 +913,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
final Element description = child.findChild("description"); final Element description = child.findChild("description");
final String namespace = description == null ? null : description.getNamespace(); final String namespace = description == null ? null : description.getNamespace();
if (Namespace.JINGLE_APPS_RTP.equals(namespace)) { if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false); final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), null, false, false, false, nextCounterpart);
final Message preExistingMessage = c.findRtpSession(sessionId, status); final Message preExistingMessage = c.findRtpSession(sessionId, status);
if (preExistingMessage != null) { if (preExistingMessage != null) {
preExistingMessage.setServerMsgId(serverMsgId); preExistingMessage.setServerMsgId(serverMsgId);
@ -963,12 +970,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
final String id = displayed.getAttribute("id"); final String id = displayed.getAttribute("id");
final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender")); final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
if (packet.fromAccount(account) && !selfAddressed) { if (packet.fromAccount(account) && !selfAddressed) {
dismissNotification(account, counterpart, query, id); dismissNotification(account, counterpart, query, id, nextCounterpart);
if (query == null) { if (query == null) {
activateGracePeriod(account); activateGracePeriod(account);
} }
} else if (isTypeGroupChat) { } else if (isTypeGroupChat) {
final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart);
final Message message; final Message message;
if (conversation != null && id != null) { if (conversation != null && id != null) {
if (sender != null) { if (sender != null) {
@ -1004,7 +1011,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
message = message.prev(); message = message.prev();
} }
if (displayedMessage != null && selfAddressed) { if (displayedMessage != null && selfAddressed) {
dismissNotification(account, counterpart, query, id); dismissNotification(account, counterpart, query, id, nextCounterpart);
} }
} }
} }
@ -1033,8 +1040,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} }
private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query, final String id) { private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query, final String id, Jid nextCounterpart) {
final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid(), nextCounterpart);
if (conversation != null && (query == null || query.isCatchup())) { if (conversation != null && (query == null || query.isCatchup())) {
final String displayableId = conversation.findMostRecentRemoteDisplayableId(); final String displayableId = conversation.findMostRecentRemoteDisplayableId();
if (displayableId != null && displayableId.equals(id)) { if (displayableId != null && displayableId.equals(id)) {
@ -1092,7 +1099,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
public boolean execute(Account account) { public boolean execute(Account account) {
if (jid != null) { if (jid != null) {
Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true, false); Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, null, true, false, false, null);
if (conversation.getMucOptions().online()) { if (conversation.getMucOptions().online()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invite to " + jid + " but muc is considered to be online"); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invite to " + jid + " but muc is considered to be online");
mXmppConnectionService.mucSelfPingAndRejoin(conversation); mXmppConnectionService.mucSelfPingAndRejoin(conversation);

View file

@ -36,8 +36,11 @@ public class PresenceParser extends AbstractParser implements
} }
public void parseConferencePresence(PresencePacket packet, Account account) { public void parseConferencePresence(PresencePacket packet, Account account) {
final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().asBareJid()); final List<Conversation> res = packet.getFrom() == null ? null : mXmppConnectionService.findAll(account, packet.getFrom().asBareJid());
if (conversation != null) {
if (res == null) return;
for (Conversation conversation : res) {
final MucOptions mucOptions = conversation.getMucOptions(); final MucOptions mucOptions = conversation.getMucOptions();
boolean before = mucOptions.online(); boolean before = mucOptions.online();
int count = mucOptions.getUserCount(); int count = mucOptions.getUserCount();
@ -362,7 +365,7 @@ public class PresenceParser extends AbstractParser implements
} else { } else {
contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
final Conversation conversation = mXmppConnectionService.findOrCreateConversation( final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
account, contact.getJid().asBareJid(), false, false); account, contact.getJid().asBareJid(), null, false, false, false, null);
final String statusMessage = packet.findChildContent("status"); final String statusMessage = packet.findChildContent("status");
if (statusMessage != null if (statusMessage != null
&& !statusMessage.isEmpty() && !statusMessage.isEmpty()

View file

@ -64,7 +64,7 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
public class DatabaseBackend extends SQLiteOpenHelper { public class DatabaseBackend extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "history"; private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 52; private static final int DATABASE_VERSION = 53;
private static boolean requiresMessageIndexRebuild = false; private static boolean requiresMessageIndexRebuild = false;
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
@ -241,7 +241,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
+ " TEXT, " + Conversation.CREATED + " NUMBER, " + " TEXT, " + Conversation.CREATED + " NUMBER, "
+ Conversation.STATUS + " NUMBER, " + Conversation.MODE + Conversation.STATUS + " NUMBER, " + Conversation.MODE
+ " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY(" + " NUMBER, " + Conversation.NEXT_COUNTERPART + " TEXT, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
+ Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
+ "(" + Account.UUID + ") ON DELETE CASCADE);"); + "(" + Account.UUID + ") ON DELETE CASCADE);");
db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
@ -606,6 +606,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 52 && newVersion >= 52) { if (oldVersion < 52 && newVersion >= 52) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.PAYLOADS + " TEXT"); db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.PAYLOADS + " TEXT");
} }
if (oldVersion < 53 && newVersion >= 53) {
db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN " + Conversation.NEXT_COUNTERPART + " TEXT");
}
} }
private void canonicalizeJids(SQLiteDatabase db) { private void canonicalizeJids(SQLiteDatabase db) {
@ -803,26 +807,42 @@ public class DatabaseBackend extends SQLiteOpenHelper {
SQLiteDatabase db = this.getReadableDatabase(); SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor; Cursor cursor;
if (timestamp == -1) { if (timestamp == -1) {
String[] selectionArgs = {conversation.getUuid()}; if (conversation.getNextCounterpart() == null) {
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION String[] selectionArgs = {conversation.getUuid()};
+ "=?", selectionArgs, null, null, Message.TIME_SENT cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ "=?", selectionArgs, null, null, Message.TIME_SENT
+ " DESC", String.valueOf(limit));
} else {
String[] selectionArgs = {conversation.getUuid(), String.valueOf(Message.TYPE_PRIVATE), String.valueOf(Message.TYPE_PRIVATE_FILE), conversation.getNextCounterpart().toString()};
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ "=? and (" + Message.TYPE + "=? or " + Message.TYPE + "=?) and " + Message.COUNTERPART + "=?" , selectionArgs, null, null, Message.TIME_SENT
+ " DESC", String.valueOf(limit)); + " DESC", String.valueOf(limit));
}
} else { } else {
String[] selectionArgs = {conversation.getUuid(), if (conversation.getNextCounterpart() == null) {
Long.toString(timestamp)}; String[] selectionArgs = {conversation.getUuid(),
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION Long.toString(timestamp)};
+ "=? and " + Message.TIME_SENT + "<?", selectionArgs, cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
null, null, Message.TIME_SENT + " DESC", + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
String.valueOf(limit)); null, null, Message.TIME_SENT + " DESC",
String.valueOf(limit));
} else {
String[] selectionArgs = {conversation.getUuid(), String.valueOf(Message.TYPE_PRIVATE), String.valueOf(Message.TYPE_PRIVATE_FILE), conversation.getNextCounterpart().toString(), Long.toString(timestamp)};
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ "=? and (" + Message.TYPE + "=? or " + Message.TYPE + "=?) and " + Message.COUNTERPART + "=? and " + Message.TIME_SENT + "<?" , selectionArgs, null, null, Message.TIME_SENT
+ " DESC", String.valueOf(limit));
}
} }
CursorUtils.upgradeCursorWindowSize(cursor); CursorUtils.upgradeCursorWindowSize(cursor);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
try { try {
list.add(0, Message.fromCursor(cursor, conversation)); Message m = Message.fromCursor(cursor, conversation);
list.add(0, m);
} catch (Exception e) { } catch (Exception e) {
Log.e(Config.LOGTAG, "unable to restore message"); Log.e(Config.LOGTAG, "unable to restore message");
} }
} }
cursor.close(); cursor.close();
return list; return list;
} }
@ -831,7 +851,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
final SQLiteDatabase db = this.getReadableDatabase(); final SQLiteDatabase db = this.getReadableDatabase();
final StringBuilder SQL = new StringBuilder(); final StringBuilder SQL = new StringBuilder();
final String[] selectionArgs; final String[] selectionArgs;
SQL.append("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 + " JOIN messages_index ON messages_index.rowid=messages.rowid WHERE " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + "," + Message.ENCRYPTION_PGP + "," + Message.ENCRYPTION_DECRYPTION_FAILED + "," + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + "," + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ?"); SQL.append("SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + "." + Conversation.CONTACTJID + "," + Conversation.TABLENAME + "." + Conversation.ACCOUNT + "," + Conversation.TABLENAME + "." + Conversation.MODE + "," + Conversation.TABLENAME + "." + Conversation.NEXT_COUNTERPART + " FROM " + Message.TABLENAME + " JOIN " + Conversation.TABLENAME + " ON " + Message.TABLENAME + "." + Message.CONVERSATION + "=" + Conversation.TABLENAME + "." + Conversation.UUID + " JOIN messages_index ON messages_index.rowid=messages.rowid WHERE " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + "," + Message.ENCRYPTION_PGP + "," + Message.ENCRYPTION_DECRYPTION_FAILED + "," + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + "," + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ?");
if (uuid == null) { if (uuid == null) {
selectionArgs = new String[]{FtsUtils.toMatchString(term)}; selectionArgs = new String[]{FtsUtils.toMatchString(term)};
} else { } else {
@ -949,25 +969,52 @@ public class DatabaseBackend extends SQLiteOpenHelper {
} }
} }
public Conversation findConversation(final Account account, final Jid contactJid) { public Conversation findConversation(final Account account, final Jid contactJid, final Jid counterpart) {
SQLiteDatabase db = this.getReadableDatabase(); SQLiteDatabase db = this.getReadableDatabase();
String[] selectionArgs = {account.getUuid(),
contactJid.asBareJid().toString() + "/%", if (counterpart != null) {
contactJid.asBareJid().toString() String[] selectionArgs = {account.getUuid(),
}; contactJid.asBareJid().toString() + "/%",
try(final Cursor cursor = db.query(Conversation.TABLENAME, null, contactJid.asBareJid().toString(),
Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID counterpart.toString()
+ " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null)) { };
if (cursor.getCount() == 0) { try (final Cursor cursor = db.query(Conversation.TABLENAME, null,
return null; Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
+ " like ? OR " + Conversation.CONTACTJID + "=?) AND " + Conversation.NEXT_COUNTERPART + "=?", selectionArgs, null, null, null)) {
if (cursor.getCount() != 0) {
cursor.moveToFirst();
final Conversation conversation = Conversation.fromCursor(cursor);
if (!(conversation.getJid() instanceof InvalidJid)) {
return conversation;
}
}
} }
cursor.moveToFirst(); } else {
final Conversation conversation = Conversation.fromCursor(cursor); String[] selectionArgs = new String[]{
if (conversation.getJid() instanceof InvalidJid) { account.getUuid(),
return null; contactJid.asBareJid().toString() + "/%",
contactJid.asBareJid().toString()
};
try(final Cursor cursor = db.query(Conversation.TABLENAME, null,
Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
+ " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null)) {
if (cursor.getCount() == 0) {
return null;
}
cursor.moveToFirst();
final Conversation conversation = Conversation.fromCursor(cursor);
if (!(conversation.getJid() instanceof InvalidJid)) {
return conversation;
}
} }
return conversation;
} }
return null;
} }
public void updateConversation(final Conversation conversation) { public void updateConversation(final Conversation conversation) {

View file

@ -302,7 +302,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
if (conversation.getMode() == Conversation.MODE_SINGLE) { if (conversation.getMode() == Conversation.MODE_SINGLE) {
return get(conversation.getContact(), size, cachedOnly); return get(conversation.getContact(), size, cachedOnly);
} else { } else {
return get(conversation.getMucOptions(), size, cachedOnly); return get(conversation.getMucOptions(), size, cachedOnly, conversation.getNextCounterpart());
} }
} }
@ -325,13 +325,21 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
} }
} }
private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) { private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly, Jid nextCounterpart) {
final String KEY = key(mucOptions, size); final String KEY = key(mucOptions, size, nextCounterpart);
Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY); Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
if (bitmap != null || cachedOnly) { if (bitmap != null || cachedOnly) {
return bitmap; return bitmap;
} }
if (nextCounterpart != null) {
for (MucOptions.User u : mucOptions.getUsersRelevantForNameAndAvatar()) {
if (u.getComparableName().equals(nextCounterpart.getResource())) {
return get(u, size, cachedOnly);
}
}
}
bitmap = mXmppConnectionService.getFileBackend().getAvatar(mucOptions.getAvatar(), size); bitmap = mXmppConnectionService.getFileBackend().getAvatar(mucOptions.getAvatar(), size);
if (bitmap == null) { if (bitmap == null) {
@ -404,16 +412,21 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
} }
synchronized (this.sizes) { synchronized (this.sizes) {
for (Integer size : sizes) { for (Integer size : sizes) {
this.mXmppConnectionService.getBitmapCache().remove(key(options, size)); this.mXmppConnectionService.getBitmapCache().remove(key(options, size, null));
} }
} }
} }
private String key(final MucOptions options, int size) { private String key(final MucOptions options, int size, Jid nextCounterpart) {
synchronized (this.sizes) { synchronized (this.sizes) {
this.sizes.add(size); this.sizes.add(size);
} }
return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size;
if (nextCounterpart == null) {
return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size;
} else {
return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size + "_" + nextCounterpart;
}
} }
private String key(List<MucOptions.User> users, int size) { private String key(List<MucOptions.User> users, int size) {

View file

@ -102,6 +102,7 @@ public class MessageSearchTask implements Runnable, Cancellable {
final int indexAccount = cursor.getColumnIndex(Conversation.ACCOUNT); final int indexAccount = cursor.getColumnIndex(Conversation.ACCOUNT);
final int indexContact = cursor.getColumnIndex(Conversation.CONTACTJID); final int indexContact = cursor.getColumnIndex(Conversation.CONTACTJID);
final int indexMode = cursor.getColumnIndex(Conversation.MODE); final int indexMode = cursor.getColumnIndex(Conversation.MODE);
final int indexNextCounterpart = cursor.getColumnIndex(Conversation.NEXT_COUNTERPART);
do { do {
if (isCancelled) { if (isCancelled) {
Log.d(Config.LOGTAG, "canceled search task"); Log.d(Config.LOGTAG, "canceled search task");
@ -117,8 +118,9 @@ public class MessageSearchTask implements Runnable, Cancellable {
if (conversation == null) { if (conversation == null) {
String accountUuid = cursor.getString(indexAccount); String accountUuid = cursor.getString(indexAccount);
String contactJid = cursor.getString(indexContact); String contactJid = cursor.getString(indexContact);
String nextCounterpart = cursor.getString(indexNextCounterpart);
int mode = cursor.getInt(indexMode); int mode = cursor.getInt(indexMode);
conversation = findOrGenerateStub(conversationUuid, accountUuid, contactJid, mode); conversation = findOrGenerateStub(conversationUuid, accountUuid, contactJid, mode, nextCounterpart);
conversationCache.put(conversationUuid, conversation); conversationCache.put(conversationUuid, conversation);
} }
Message message = IndividualMessage.fromCursor(cursor, conversation); Message message = IndividualMessage.fromCursor(cursor, conversation);
@ -137,7 +139,7 @@ public class MessageSearchTask implements Runnable, Cancellable {
} }
} }
private Conversational findOrGenerateStub(String conversationUuid, String accountUuid, String contactJid, int mode) throws Exception { private Conversational findOrGenerateStub(String conversationUuid, String accountUuid, String contactJid, int mode, String nextCounterpart) throws Exception {
Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid); Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid);
if (conversation != null) { if (conversation != null) {
return conversation; return conversation;
@ -145,7 +147,7 @@ public class MessageSearchTask implements Runnable, Cancellable {
Account account = xmppConnectionService.findAccountByUuid(accountUuid); Account account = xmppConnectionService.findAccountByUuid(accountUuid);
Jid jid = Jid.of(contactJid); Jid jid = Jid.of(contactJid);
if (account != null && jid != null) { if (account != null && jid != null) {
return new StubConversation(account, conversationUuid, jid.asBareJid(), mode); return new StubConversation(account, conversationUuid, jid.asBareJid(), mode, Jid.of(nextCounterpart));
} }
throw new Exception("Unable to generate stub for " + contactJid); throw new Exception("Unable to generate stub for " + contactJid);
} }

View file

@ -463,7 +463,7 @@ public class NotificationService {
final int failedDeliveries = conversation.countFailedDeliveries(); final int failedDeliveries = conversation.countFailedDeliveries();
final Notification notification = final Notification notification =
new Builder(mXmppConnectionService, "delivery_failed") new Builder(mXmppConnectionService, "delivery_failed")
.setContentTitle(conversation.getName()) .setContentTitle(getConversationName(conversation))
.setAutoCancel(true) .setAutoCancel(true)
.setSmallIcon(R.drawable.ic_error_white_24dp) .setSmallIcon(R.drawable.ic_error_white_24dp)
.setContentText( .setContentText(
@ -1104,7 +1104,7 @@ public class NotificationService {
continue; continue;
} }
conversation = (Conversation) messages.get(0).getConversation(); conversation = (Conversation) messages.get(0).getConversation();
final String name = conversation.getName().toString(); final String name = getConversationName(conversation);
SpannableString styledString; SpannableString styledString;
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
int count = messages.size(); int count = messages.size();
@ -1166,7 +1166,7 @@ public class NotificationService {
.get( .get(
conversation, conversation,
AvatarService.getSystemUiAvatarSize(mXmppConnectionService))); AvatarService.getSystemUiAvatarSize(mXmppConnectionService)));
mBuilder.setContentTitle(conversation.getName()); mBuilder.setContentTitle(getConversationName(conversation));
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
int count = messages.size(); int count = messages.size();
mBuilder.setContentText( mBuilder.setContentText(
@ -1375,7 +1375,7 @@ public class NotificationService {
new NotificationCompat.MessagingStyle(me); new NotificationCompat.MessagingStyle(me);
final boolean multiple = conversation.getMode() == Conversation.MODE_MULTI; final boolean multiple = conversation.getMode() == Conversation.MODE_MULTI;
if (multiple) { if (multiple) {
messagingStyle.setConversationTitle(conversation.getName()); messagingStyle.setConversationTitle(getConversationName(conversation));
} }
for (Message message : messages) { for (Message message : messages) {
final Person sender = final Person sender =
@ -1913,6 +1913,14 @@ public class NotificationService {
} }
} }
private String getConversationName(Conversation c) {
if (c.getNextCounterpart() == null) {
return c.getName().toString();
} else {
return mXmppConnectionService.getResources().getString(R.string.muc_private_conversation_title, c.getNextCounterpart().getResource(), c.getName());
}
}
private static class MissedCallsInfo { private static class MissedCallsInfo {
private int numberOfCalls; private int numberOfCalls;
private long lastTime; private long lastTime;

View file

@ -71,6 +71,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
@ -642,16 +643,17 @@ public class XmppConnectionService extends Service {
}); });
} }
public Conversation find(Bookmark bookmark) { public Conversation find(final Account account, final Jid jid, final Jid counterpart) {
return find(bookmark.getAccount(), bookmark.getJid()); return find(getConversations(), account, jid, counterpart);
} }
public Conversation find(final Account account, final Jid jid) { public List<Conversation> findAll(final Account account, final Jid jid) {
return find(getConversations(), account, jid); return findAll(getConversations(), account, jid);
} }
public boolean isMuc(final Account account, final Jid jid) { public boolean isMuc(final Account account, final Jid jid) {
final Conversation c = find(account, jid); final Conversation c = find(account, jid, null);
return c != null && c.getMode() == Conversational.MODE_MULTI; return c != null && c.getMode() == Conversational.MODE_MULTI;
} }
@ -1817,7 +1819,7 @@ public class XmppConnectionService extends Service {
} }
public void processDeletedBookmark(Account account, Jid jid) { public void processDeletedBookmark(Account account, Jid jid) {
final Conversation conversation = find(account, jid); final Conversation conversation = find(account, jid, null);
if (conversation != null && conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) { if (conversation != null && conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving destroyed conference (" + conversation.getJid() + ") after receiving pep"); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving destroyed conference (" + conversation.getJid() + ") after receiving pep");
archiveConversation(conversation, false); archiveConversation(conversation, false);
@ -1826,7 +1828,7 @@ public class XmppConnectionService extends Service {
private void processModifiedBookmark(Bookmark bookmark, final boolean pep, final boolean synchronizeWithBookmarks) { private void processModifiedBookmark(Bookmark bookmark, final boolean pep, final boolean synchronizeWithBookmarks) {
final Account account = bookmark.getAccount(); final Account account = bookmark.getAccount();
Conversation conversation = find(bookmark); Conversation conversation = find(bookmark.getAccount(), bookmark.getJid(), null);
if (conversation != null) { if (conversation != null) {
if (conversation.getMode() != Conversation.MODE_MULTI) { if (conversation.getMode() != Conversation.MODE_MULTI) {
return; return;
@ -1847,7 +1849,7 @@ public class XmppConnectionService extends Service {
} }
} }
} else if (synchronizeWithBookmarks && bookmark.autojoin()) { } else if (synchronizeWithBookmarks && bookmark.autojoin()) {
conversation = findOrCreateConversation(account, bookmark.getFullJid(), true, true, false); conversation = findOrCreateConversation(account, bookmark.getFullJid(), null, true, true, false, null);
bookmark.setConversation(conversation); bookmark.setConversation(conversation);
} }
} }
@ -1939,6 +1941,17 @@ public class XmppConnectionService extends Service {
}); });
} }
private Map<String, Conversation> computeBareIdConversationsMap() {
Map<String, Conversation> res = new HashMap<>();
for (Conversation c : this.conversations) {
if (c.getNextCounterpart() == null) {
res.put(c.getJid().asBareJid().toString(), c);
}
}
return res;
}
private void restoreFromDatabase() { private void restoreFromDatabase() {
synchronized (this.conversations) { synchronized (this.conversations) {
final Map<String, Account> accountLookupTable = new Hashtable<>(); final Map<String, Account> accountLookupTable = new Hashtable<>();
@ -1948,7 +1961,24 @@ public class XmppConnectionService extends Service {
Log.d(Config.LOGTAG, "restoring conversations..."); Log.d(Config.LOGTAG, "restoring conversations...");
final long startTimeConversationsRestore = SystemClock.elapsedRealtime(); final long startTimeConversationsRestore = SystemClock.elapsedRealtime();
this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE)); this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
for (Iterator<Conversation> iterator = conversations.listIterator(); iterator.hasNext(); ) {
Map<String, Conversation> map = null;
for (Conversation c : this.conversations) {
if (c.getNextCounterpart() != null) {
if (map == null) {
map = computeBareIdConversationsMap();
}
Conversation parent = map.get(c.getJid().asBareJid().toString());
if (parent != null) {
c.setParentConversation(parent);
}
}
}
for (Iterator<Conversation> iterator = conversations.listIterator(); iterator.hasNext();) {
Conversation conversation = iterator.next(); Conversation conversation = iterator.next();
Account account = accountLookupTable.get(conversation.getAccountUuid()); Account account = accountLookupTable.get(conversation.getAccountUuid());
if (account != null) { if (account != null) {
@ -2140,7 +2170,9 @@ public class XmppConnectionService extends Service {
if (messages.size() > 0) { if (messages.size() > 0) {
conversation.addAll(0, messages); conversation.addAll(0, messages);
callback.onMoreMessagesLoaded(messages.size(), conversation); callback.onMoreMessagesLoaded(messages.size(), conversation);
} else if (conversation.hasMessagesLeftOnServer() } else if (
conversation.getNextCounterpart() == null
&& conversation.hasMessagesLeftOnServer()
&& account.isOnlineAndConnected() && account.isOnlineAndConnected()
&& conversation.getLastClearHistory().getTimestamp() == 0) { && conversation.getLastClearHistory().getTimestamp() == 0) {
final boolean mamAvailable; final boolean mamAvailable;
@ -2195,19 +2227,52 @@ public class XmppConnectionService extends Service {
return null; return null;
} }
public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) { private Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid, final Jid counterpart) {
if (jid == null) { if (jid == null) {
return null; return null;
} }
for (final Conversation conversation : haystack) {
if ((account == null || conversation.getAccount() == account) if (counterpart != null) {
&& (conversation.getJid().asBareJid().equals(jid.asBareJid()))) { for (final Conversation conversation : haystack) {
return conversation; if ((account == null || conversation.getAccount() == account)
&& (conversation.getJid().asBareJid().equals(jid.asBareJid()))
&& Objects.equal(conversation.getNextCounterpart(), counterpart)
) {
return conversation;
}
}
} else {
for (final Conversation conversation : haystack) {
if ((account == null || conversation.getAccount() == account)
&& (conversation.getJid().asBareJid().equals(jid.asBareJid()))
&& conversation.getNextCounterpart() == null
) {
return conversation;
}
} }
} }
return null; return null;
} }
private List<Conversation> findAll(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
if (jid == null) {
return null;
}
List<Conversation> res = new ArrayList<>();
for (final Conversation conversation : haystack) {
if ((account == null || conversation.getAccount() == account)
&& (conversation.getJid().asBareJid().equals(jid.asBareJid()))
) {
res.add(conversation);
}
}
return res;
}
public boolean isConversationsListEmpty(final Conversation ignore) { public boolean isConversationsListEmpty(final Conversation ignore) {
synchronized (this.conversations) { synchronized (this.conversations) {
final int size = this.conversations.size(); final int size = this.conversations.size();
@ -2225,22 +2290,13 @@ public class XmppConnectionService extends Service {
} }
return false; return false;
} }
public Conversation findOrCreateConversation(final Account account, final Jid jid, final MessageArchiveService.Query query, final boolean muc, final boolean joinAfterCreate, final boolean async, Jid counterpart) {
public Conversation findOrCreateConversation(Account account, Jid jid, boolean muc, final boolean async) {
return this.findOrCreateConversation(account, jid, muc, false, async);
}
public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final boolean joinAfterCreate, final boolean async) {
return this.findOrCreateConversation(account, jid, muc, joinAfterCreate, null, async);
}
public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final boolean joinAfterCreate, final MessageArchiveService.Query query, final boolean async) {
synchronized (this.conversations) { synchronized (this.conversations) {
Conversation conversation = find(account, jid); Conversation conversation = find(account, jid, counterpart);
if (conversation != null) { if (conversation != null) {
return conversation; return conversation;
} }
conversation = databaseBackend.findConversation(account, jid); conversation = databaseBackend.findConversation(account, jid, counterpart);
final boolean loadMessagesFromDb; final boolean loadMessagesFromDb;
if (conversation != null) { if (conversation != null) {
conversation.setStatus(Conversation.STATUS_AVAILABLE); conversation.setStatus(Conversation.STATUS_AVAILABLE);
@ -2262,12 +2318,13 @@ public class XmppConnectionService extends Service {
} else { } else {
conversationName = jid.getLocal(); conversationName = jid.getLocal();
} }
if (muc) { if (muc) {
conversation = new Conversation(conversationName, account, jid, conversation = new Conversation(conversationName, account, jid,
Conversation.MODE_MULTI); Conversation.MODE_MULTI, counterpart);
} else { } else {
conversation = new Conversation(conversationName, account, jid.asBareJid(), conversation = new Conversation(conversationName, account, jid.asBareJid(),
Conversation.MODE_SINGLE); Conversation.MODE_SINGLE, counterpart);
} }
this.databaseBackend.createConversation(conversation); this.databaseBackend.createConversation(conversation);
loadMessagesFromDb = false; loadMessagesFromDb = false;
@ -2301,6 +2358,14 @@ public class XmppConnectionService extends Service {
runnable.run(); runnable.run();
} }
this.conversations.add(conversation); this.conversations.add(conversation);
if (counterpart != null) {
Conversation parent = find(account, jid, null);
if (parent != null) {
conversation.setParentConversation(parent);
}
}
updateConversationUi(); updateConversationUi();
return conversation; return conversation;
} }
@ -3284,7 +3349,7 @@ public class XmppConnectionService extends Service {
public void createPublicChannel(final Account account, final String name, final Jid address, final UiCallback<Conversation> callback) { public void createPublicChannel(final Account account, final String name, final Jid address, final UiCallback<Conversation> callback) {
joinMuc(findOrCreateConversation(account, address, true, false, true), conversation -> { joinMuc(findOrCreateConversation(account, address, null, true, false, true, null), conversation -> {
final Bundle configuration = IqGenerator.defaultChannelConfiguration(); final Bundle configuration = IqGenerator.defaultChannelConfiguration();
if (!TextUtils.isEmpty(name)) { if (!TextUtils.isEmpty(name)) {
configuration.putString("muc#roomconfig_roomname", name); configuration.putString("muc#roomconfig_roomname", name);
@ -3323,7 +3388,7 @@ public class XmppConnectionService extends Service {
return false; return false;
} }
final Jid jid = Jid.of(CryptoHelper.pronounceable(), server, null); final Jid jid = Jid.of(CryptoHelper.pronounceable(), server, null);
final Conversation conversation = findOrCreateConversation(account, jid, true, false, true); final Conversation conversation = findOrCreateConversation(account, jid, null, true, false, true, null);
joinMuc(conversation, new OnConferenceJoined() { joinMuc(conversation, new OnConferenceJoined() {
@Override @Override
public void onConferenceJoined(final Conversation conversation) { public void onConferenceJoined(final Conversation conversation) {
@ -3971,7 +4036,7 @@ public class XmppConnectionService extends Service {
} }
updateConversationUi(); updateConversationUi();
} else { } else {
Conversation conversation = find(account, avatar.owner.asBareJid()); Conversation conversation = find(account, avatar.owner.asBareJid(), null);
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner); MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner);
if (user != null) { if (user != null) {
@ -4138,8 +4203,8 @@ public class XmppConnectionService extends Service {
final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid); final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid);
if (message != null) { if (message != null) {
markMessage(message, status, errorMessage); markMessage(message, status, errorMessage);
return message;
} }
return message;
} }
} }
return null; return null;
@ -4371,7 +4436,7 @@ public class XmppConnectionService extends Service {
public Conversation findUniqueConversationByJid(XmppUri xmppUri) { public Conversation findUniqueConversationByJid(XmppUri xmppUri) {
List<Conversation> findings = new ArrayList<>(); List<Conversation> findings = new ArrayList<>();
for (Conversation c : getConversations()) { for (Conversation c : getConversations()) {
if (c.getAccount().isEnabled() && c.getJid().asBareJid().equals(xmppUri.getJid()) && ((c.getMode() == Conversational.MODE_MULTI) == xmppUri.isAction(XmppUri.ACTION_JOIN))) { if (c.getAccount().isEnabled() && c.getJid().asBareJid().equals(xmppUri.getJid()) && c.getNextCounterpart() == null && ((c.getMode() == Conversational.MODE_MULTI) == xmppUri.isAction(XmppUri.ACTION_JOIN))) {
findings.add(c); findings.add(c);
} }
} }
@ -4626,7 +4691,7 @@ public class XmppConnectionService extends Service {
public Conversation findFirstMuc(Jid jid) { public Conversation findFirstMuc(Jid jid) {
for (Conversation conversation : getConversations()) { for (Conversation conversation : getConversations()) {
if (conversation.getAccount().isEnabled() && conversation.getJid().asBareJid().equals(jid.asBareJid()) && conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getAccount().isEnabled() && conversation.getJid().asBareJid().equals(jid.asBareJid()) && conversation.getNextCounterpart() == null && conversation.getMode() == Conversation.MODE_MULTI) {
return conversation; return conversation;
} }
} }

View file

@ -271,7 +271,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK); final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK);
final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin); final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin);
final Account account = xmppConnectionService.findAccountByJid(jid); final Account account = xmppConnectionService.findAccountByJid(jid);
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), null, true, true, true, null);
Bookmark bookmark = conversation.getBookmark(); Bookmark bookmark = conversation.getBookmark();
if (bookmark != null) { if (bookmark != null) {
if (!bookmark.autojoin() && syncAutoJoin) { if (!bookmark.autojoin() && syncAutoJoin) {

View file

@ -99,7 +99,17 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
public static void open(final Activity activity, final Conversation conversation) { public static void open(final Activity activity, final Conversation conversation) {
Intent intent = new Intent(activity, ConferenceDetailsActivity.class); Intent intent = new Intent(activity, ConferenceDetailsActivity.class);
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
intent.putExtra("uuid", conversation.getUuid());
Conversation parentConversation = conversation.getParentConversation();
String uuid;
if (parentConversation != null) {
uuid = parentConversation.getUuid();
} else {
uuid = conversation.getUuid();
}
intent.putExtra("uuid", uuid);
activity.startActivity(intent); activity.startActivity(intent);
} }

View file

@ -556,7 +556,6 @@ public class ConversationFragment extends XmppFragment
binding.textinput.append(conversation.getDraftMessage()); binding.textinput.append(conversation.getDraftMessage());
conversation.setDraftMessage(null); conversation.setDraftMessage(null);
} else if (conversation.getMode() == Conversation.MODE_MULTI) { } else if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextCounterpart(null);
binding.textinput.setText(""); binding.textinput.setText("");
} else { } else {
binding.textinput.setText(""); binding.textinput.setText("");
@ -1008,12 +1007,15 @@ public class ConversationFragment extends XmppFragment
this.binding.textInputHint.setVisibility(View.GONE); this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(R.string.send_corrected_message); this.binding.textinput.setHint(R.string.send_corrected_message);
} else if (multi && conversation.getNextCounterpart() != null) { } else if (multi && conversation.getNextCounterpart() != null) {
this.binding.textinput.setHint(R.string.send_unencrypted_message); /*this.binding.textinput.setHint(R.string.send_unencrypted_message);
this.binding.textInputHint.setVisibility(View.VISIBLE); this.binding.textInputHint.setVisibility(View.VISIBLE);
this.binding.textInputHint.setText( this.binding.textInputHint.setText(
getString( getString(
R.string.send_private_message_to, R.string.send_private_message_to,
conversation.getNextCounterpart().getResource())); conversation.getNextCounterpart().getResource())); */
this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(UIHelper.getMessageHint(getActivity(), conversation));
getActivity().invalidateOptionsMenu();
} else if (multi && !conversation.getMucOptions().participating()) { } else if (multi && !conversation.getMucOptions().participating()) {
this.binding.textInputHint.setVisibility(View.GONE); this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(R.string.you_are_not_participating); this.binding.textinput.setHint(R.string.you_are_not_participating);
@ -1651,7 +1653,7 @@ public class ConversationFragment extends XmppFragment
activity.switchToContactDetails(conversation.getContact()); activity.switchToContactDetails(conversation.getContact());
break; break;
case R.id.action_muc_details: case R.id.action_muc_details:
ConferenceDetailsActivity.open(getActivity(), conversation); ConferenceDetailsActivity.open(activity, conversation);
break; break;
case R.id.action_invite: case R.id.action_invite:
startActivityForResult( startActivityForResult(
@ -2407,14 +2409,8 @@ public class ConversationFragment extends XmppFragment
} }
public void privateMessageWith(final Jid counterpart) { public void privateMessageWith(final Jid counterpart) {
if (conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) { Conversation c = activity.xmppConnectionService.findOrCreateConversation(conversation.getAccount(), conversation.getJid(), null, true, true, false, counterpart);
activity.xmppConnectionService.sendChatState(conversation); activity.switchToConversation(c);
}
this.binding.textinput.setText("");
this.conversation.setNextCounterpart(counterpart);
updateChatMsgHint();
updateSendButton();
updateEditablity();
} }
private void correctMessage(Message message) { private void correctMessage(Message message) {

View file

@ -630,7 +630,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (mainFragment instanceof ConversationFragment) { if (mainFragment instanceof ConversationFragment) {
final Conversation conversation = ((ConversationFragment) mainFragment).getConversation(); final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
if (conversation != null) { if (conversation != null) {
actionBar.setTitle(conversation.getName()); if (conversation.getNextCounterpart() != null) {
actionBar.setTitle(getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName()));
} else {
actionBar.setTitle(conversation.getName());
}
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
ActionBarUtil.setActionBarOnClickListener( ActionBarUtil.setActionBarOnClickListener(
binding.toolbar, binding.toolbar,

View file

@ -246,7 +246,7 @@ public class RtpSessionActivity extends XmppActivity
final Contact contact = getWith(); final Contact contact = getWith();
final Conversation conversation = final Conversation conversation =
xmppConnectionService.findOrCreateConversation( xmppConnectionService.findOrCreateConversation(
contact.getAccount(), contact.getJid(), false, true); contact.getAccount(), contact.getJid(), null, false, false, true, null);
switchToConversation(conversation); switchToConversation(conversation);
} }
@ -1361,7 +1361,7 @@ public class RtpSessionActivity extends XmppActivity
final Account account = extractAccount(intent); final Account account = extractAccount(intent);
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final Conversation conversation = final Conversation conversation =
xmppConnectionService.findOrCreateConversation(account, with, false, true); xmppConnectionService.findOrCreateConversation(account, with, null, false, false, true, null);
final Intent launchIntent = new Intent(this, ConversationsActivity.class); final Intent launchIntent = new Intent(this, ConversationsActivity.class);
launchIntent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION); launchIntent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
launchIntent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid()); launchIntent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());

View file

@ -201,9 +201,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
} else { } else {
return xmppConnectionService.findOrCreateConversation(conversational.getAccount(), return xmppConnectionService.findOrCreateConversation(conversational.getAccount(),
conversational.getJid(), conversational.getJid(),
null,
conversational.getMode() == Conversational.MODE_MULTI, conversational.getMode() == Conversational.MODE_MULTI,
false,
true, true,
true); conversational.getNextCounterpart()
);
} }
} }

View file

@ -178,7 +178,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} }
try { try {
conversation = xmppConnectionService.findOrCreateConversation(account, Jid.of(share.contact), false, true); conversation = xmppConnectionService.findOrCreateConversation(account, Jid.of(share.contact), null, false, false, true, null);
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
return; return;
} }

View file

@ -93,6 +93,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment; import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.StringUtils;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
@ -430,7 +431,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
protected void openConversationForContact(Contact contact) { protected void openConversationForContact(Contact contact) {
Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true); Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), null, false, false, true, null);
SoftKeyboardUtils.hideSoftKeyboard(this); SoftKeyboardUtils.hideSoftKeyboard(this);
switchToConversation(conversation); switchToConversation(conversation);
} }
@ -467,7 +468,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show();
return; return;
} }
Conversation conversation = xmppConnectionService.findOrCreateConversation(bookmark.getAccount(), jid, true, true, true); Conversation conversation = xmppConnectionService.findOrCreateConversation(bookmark.getAccount(), jid, null, true, true, true, null);
bookmark.setConversation(conversation); bookmark.setConversation(conversation);
if (!bookmark.autojoin() && getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))) { if (!bookmark.autojoin() && getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))) {
bookmark.setAutojoin(true); bookmark.setAutojoin(true);
@ -580,13 +581,14 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
bookmark.setNick(nick); bookmark.setNick(nick);
} }
xmppConnectionService.createBookmark(account, bookmark); xmppConnectionService.createBookmark(account, bookmark);
final Conversation conversation = xmppConnectionService final Conversation conversation = xmppConnectionService
.findOrCreateConversation(account, contactJid, true, true, true); .findOrCreateConversation(account, contactJid, null, true, true, true, null);
bookmark.setConversation(conversation); bookmark.setConversation(conversation);
switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody()); switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
} }
} else { } else {
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, true, true, true); final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, null, true, true, true, null);
switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody()); switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
} }
} else { } else {
@ -669,13 +671,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
} }
protected void switchToConversation(Contact contact) {
Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
switchToConversation(conversation);
}
protected void switchToConversationDoNotAppend(Contact contact, String body) { protected void switchToConversationDoNotAppend(Contact contact, String body) {
Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true); Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), null, false, false, true, null);
switchToConversationDoNotAppend(conversation, body); switchToConversationDoNotAppend(conversation, body);
} }
@ -1192,14 +1189,14 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
xmppConnectionService.createBookmark(account, bookmark); xmppConnectionService.createBookmark(account, bookmark);
final Conversation conversation = xmppConnectionService final Conversation conversation = xmppConnectionService
.findOrCreateConversation(account, conferenceJid, true, true, true); .findOrCreateConversation(account, conferenceJid, null, true, true, true, null);
bookmark.setConversation(conversation); bookmark.setConversation(conversation);
dialog.dismiss(); dialog.dismiss();
switchToConversation(conversation); switchToConversation(conversation);
} }
} else { } else {
final Conversation conversation = xmppConnectionService final Conversation conversation = xmppConnectionService
.findOrCreateConversation(account, conferenceJid, true, true, true); .findOrCreateConversation(account, conferenceJid, null, true, true, true, null);
dialog.dismiss(); dialog.dismiss();
switchToConversation(conversation); switchToConversation(conversation);
} }

View file

@ -552,7 +552,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
protected void switchToConversationDoNotAppend(Contact contact, String body, String postInit) { protected void switchToConversationDoNotAppend(Contact contact, String body, String postInit) {
Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true); Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), null, false, false, true, null);
switchToConversation(conversation, body, false, null, false, true, postInit); switchToConversation(conversation, body, false, null, false, true, postInit);
} }

View file

@ -59,7 +59,12 @@ public class ConversationAdapter
if (conversation == null) { if (conversation == null) {
return; return;
} }
CharSequence name = conversation.getName(); CharSequence name = conversation.getName();
if (conversation.getNextCounterpart() != null) {
name = viewHolder.binding.getRoot().getResources().getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName());
}
if (name instanceof Jid) { if (name instanceof Jid) {
viewHolder.binding.conversationName.setText( viewHolder.binding.conversationName.setText(
IrregularUnicodeDetector.style(activity, (Jid) name)); IrregularUnicodeDetector.style(activity, (Jid) name));

View file

@ -523,7 +523,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
} else { } else {
String privateMarker; /*String privateMarker;
if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getStatus() <= Message.STATUS_RECEIVED) {
privateMarker = activity.getString(R.string.private_message); privateMarker = activity.getString(R.string.private_message);
} else { } else {
@ -544,7 +544,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (hasMeCommand) { if (hasMeCommand) {
body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1,
privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }*/
} }
if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) { if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) {
if (message.getConversation() instanceof Conversation) { if (message.getConversation() instanceof Conversation) {
@ -644,7 +644,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final boolean darkBackground) { private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
if (message.isPrivateMessage()) { if (false && message.isPrivateMessage()) {
final String privateMarker; final String privateMarker;
if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getStatus() <= Message.STATUS_RECEIVED) {
privateMarker = activity.getString(R.string.private_message); privateMarker = activity.getString(R.string.private_message);

View file

@ -120,7 +120,8 @@ public final class MucDetailsContextMenuHelper {
} }
} }
managePermissions.setVisible(managePermissionsVisible); managePermissions.setVisible(managePermissionsVisible);
sendPrivateMessage.setVisible(!isGroupChat && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR)); sendPrivateMessage.setVisible(true);
sendPrivateMessage.setEnabled(true);
} else { } else {
sendPrivateMessage.setVisible(true); sendPrivateMessage.setVisible(true);
sendPrivateMessage.setEnabled(user != null && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR)); sendPrivateMessage.setEnabled(user != null && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR));
@ -222,7 +223,7 @@ public final class MucDetailsContextMenuHelper {
private static void startConversation(User user, XmppActivity activity) { private static void startConversation(User user, XmppActivity activity) {
if (user.getRealJid() != null) { if (user.getRealJid() != null) {
Conversation newConversation = activity.xmppConnectionService.findOrCreateConversation(user.getAccount(), user.getRealJid().asBareJid(), false, true); Conversation newConversation = activity.xmppConnectionService.findOrCreateConversation(user.getAccount(), user.getRealJid().asBareJid(), null, false, false, true, null);
activity.switchToConversation(newConversation); activity.switchToConversation(newConversation);
} }
} }

View file

@ -59,7 +59,7 @@ public class SendButtonTool {
} }
} else { } else {
if (empty) { if (empty) {
if (conference && c.getNextCounterpart() != null) { if (false && conference && c.getNextCounterpart() != null) {
return SendButtonAction.CANCEL; return SendButtonAction.CANCEL;
} else { } else {
String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action)); String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action));

View file

@ -89,7 +89,7 @@ public class ExceptionHelper {
builder.setPositiveButton(activity.getText(R.string.send_now), (dialog, which) -> { builder.setPositiveButton(activity.getText(R.string.send_now), (dialog, which) -> {
Log.d(Config.LOGTAG, "using account=" + account.getJid().asBareJid() + " to send in stack trace"); Log.d(Config.LOGTAG, "using account=" + account.getJid().asBareJid() + " to send in stack trace");
Conversation conversation = service.findOrCreateConversation(account, Config.BUG_REPORTS, false, true); Conversation conversation = service.findOrCreateConversation(account, Config.BUG_REPORTS, null, false, false, true, null);
Message message = new Message(conversation, report.toString(), Message.ENCRYPTION_NONE); Message message = new Message(conversation, report.toString(), Message.ENCRYPTION_NONE);
service.sendMessage(message); service.sendMessage(message);
}); });

View file

@ -297,7 +297,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if ("proceed".equals(message.getName())) { if ("proceed".equals(message.getName())) {
final Conversation c = final Conversation c =
mXmppConnectionService.findOrCreateConversation( mXmppConnectionService.findOrCreateConversation(
account, id.with, false, false); account, id.with, null, false, false, false, null);
final Message previousBusy = c.findRtpSession(sessionId, Message.STATUS_RECEIVED); final Message previousBusy = c.findRtpSession(sessionId, Message.STATUS_RECEIVED);
if (previousBusy != null) { if (previousBusy != null) {
previousBusy.setBody(new RtpSessionStatus(true, 0).toString()); previousBusy.setBody(new RtpSessionStatus(true, 0).toString());
@ -503,7 +503,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
long timestamp) { long timestamp) {
final Conversation conversation = final Conversation conversation =
mXmppConnectionService.findOrCreateConversation( mXmppConnectionService.findOrCreateConversation(
account, with.asBareJid(), false, false); account, with.asBareJid(), null, false, false, false, null);
final Message message = final Message message =
new Message(conversation, Message.STATUS_SEND, Message.TYPE_RTP_SESSION, sessionId); new Message(conversation, Message.STATUS_SEND, Message.TYPE_RTP_SESSION, sessionId);
message.setBody(new RtpSessionStatus(false, 0).toString()); message.setBody(new RtpSessionStatus(false, 0).toString());
@ -520,7 +520,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
long timestamp) { long timestamp) {
final Conversation conversation = final Conversation conversation =
mXmppConnectionService.findOrCreateConversation( mXmppConnectionService.findOrCreateConversation(
account, with.asBareJid(), false, false); account, with.asBareJid(), null, false, false, false, null);
final Message message = final Message message =
new Message( new Message(
conversation, Message.STATUS_RECEIVED, Message.TYPE_RTP_SESSION, sessionId); conversation, Message.STATUS_RECEIVED, Message.TYPE_RTP_SESSION, sessionId);

View file

@ -426,7 +426,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private void init(JinglePacket packet) { //should move to deliverPacket private void init(JinglePacket packet) { //should move to deliverPacket
//TODO if not 'OFFERED' reply with out-of-order //TODO if not 'OFFERED' reply with out-of-order
this.mJingleStatus = JINGLE_STATUS_INITIATED; this.mJingleStatus = JINGLE_STATUS_INITIATED;
final Conversation conversation = this.xmppConnectionService.findOrCreateConversation(id.account, id.with.asBareJid(), false, false); final Conversation conversation = this.xmppConnectionService.findOrCreateConversation(id.account, id.with.asBareJid(), null, false, false, false, null);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setStatus(Message.STATUS_RECEIVED); this.message.setStatus(Message.STATUS_RECEIVED);
this.mStatus = Transferable.STATUS_OFFER; this.mStatus = Transferable.STATUS_OFFER;

View file

@ -180,7 +180,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
final Conversation conversation = final Conversation conversation =
jingleConnectionManager jingleConnectionManager
.getXmppConnectionService() .getXmppConnectionService()
.findOrCreateConversation(id.account, id.with.asBareJid(), false, false); .findOrCreateConversation(id.account, id.with.asBareJid(), null, false, false, false, null);
this.message = this.message =
new Message( new Message(
conversation, conversation,

View file

@ -1045,4 +1045,5 @@
<string name="resize">resize</string> <string name="resize">resize</string>
<string name="filter">filter</string> <string name="filter">filter</string>
<string name="could_not_create_file">could_not_create_file</string> <string name="could_not_create_file">could_not_create_file</string>
<string name="muc_private_conversation_title">%1$s (%2$s)</string>
</resources> </resources>