include caps in outgoing presence

This commit is contained in:
Daniel Gultsch 2023-01-25 16:55:46 +01:00
parent 9a855a57ac
commit 57d264d72e
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
17 changed files with 300 additions and 81 deletions

View file

@ -43,6 +43,12 @@ public class Element {
return child;
}
public void addExtensions(final Collection<? extends Extension> extensions) {
for(final Extension extension : extensions) {
addExtension(extension);
}
}
public Element addChild(String name) {
this.content = null;
Element child = new Element(name);

View file

@ -1,85 +1,90 @@
package eu.siacs.conversations.xml;
public final class Namespace {
public static final String STREAMS = "http://etherx.jabber.org/streams";
public static final String DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
public static final String DISCO_INFO = "http://jabber.org/protocol/disco#info";
public static final String EXTERNAL_SERVICE_DISCOVERY = "urn:xmpp:extdisco:2";
public static final String ENTITY_CAPABILITIES = "http://jabber.org/protocol/caps";
public static final String ENTITY_CAPABILITIES_2 = "urn:xmpp:caps";
public static final String HASHES = "urn:xmpp:hashes:2";
public static final String BLOCKING = "urn:xmpp:blocking";
public static final String ROSTER = "jabber:iq:roster";
public static final String REGISTER = "jabber:iq:register";
public static final String REGISTER_STREAM_FEATURE = "http://jabber.org/features/iq-register";
public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
public static final String HTTP_UPLOAD = "urn:xmpp:http:upload:0";
public static final String HTTP_UPLOAD_LEGACY = "urn:xmpp:http:upload";
public static final String STANZA_IDS = "urn:xmpp:sid:0";
public static final String IDLE = "urn:xmpp:idle:1";
public static final String DATA = "jabber:x:data";
public static final String OOB = "jabber:x:oob";
public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
public static final String SASL_2 = "urn:xmpp:sasl:2";
public static final String CHANNEL_BINDING = "urn:xmpp:sasl-cb:0";
public static final String FAST = "urn:xmpp:fast:0";
public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls";
public static final String PUBSUB = "http://jabber.org/protocol/pubsub";
public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options";
public static final String PUBSUB_ERROR = PUBSUB + "#errors";
public static final String PUBSUB_OWNER = PUBSUB + "#owner";
public static final String NICK = "http://jabber.org/protocol/nick";
public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL =
"http://jabber.org/protocol/offline";
public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind";
public static final String BIND2 = "urn:xmpp:bind:0";
public static final String STREAM_MANAGEMENT = "urn:xmpp:sm:3";
public static final String CSI = "urn:xmpp:csi:0";
public static final String CARBONS = "urn:xmpp:carbons:2";
public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0";
public static final String BOOKMARKS = "storage:bookmarks";
public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0";
public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0";
public static final String AVATAR_DATA = "urn:xmpp:avatar:data";
public static final String AVATAR_METADATA = "urn:xmpp:avatar:metadata";
public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0";
public static final String JINGLE = "urn:xmpp:jingle:1";
public static final String JINGLE_ERRORS = "urn:xmpp:jingle:errors:1";
public static final String JINGLE_MESSAGE = "urn:xmpp:jingle-message:0";
public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0";
public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0";
public static final String JINGLE_TRANSPORTS_S5B = "urn:xmpp:jingle:transports:s5b:1";
public static final String JINGLE_TRANSPORTS_IBB = "urn:xmpp:jingle:transports:ibb:1";
public static final String JINGLE_TRANSPORT_ICE_UDP = "urn:xmpp:jingle:transports:ice-udp:1";
public static final String JINGLE_APPS_RTP = "urn:xmpp:jingle:apps:rtp:1";
public static final String JINGLE_APPS_DTLS = "urn:xmpp:jingle:apps:dtls:0";
public static final String JINGLE_APPS_GROUPING = "urn:xmpp:jingle:apps:grouping:0";
public static final String JINGLE_FEATURE_AUDIO = "urn:xmpp:jingle:apps:rtp:audio";
public static final String JINGLE_FEATURE_VIDEO = "urn:xmpp:jingle:apps:rtp:video";
public static final String JINGLE_RTP_HEADER_EXTENSIONS =
"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0";
public static final String JINGLE_RTP_FEEDBACK_NEGOTIATION =
"urn:xmpp:jingle:apps:rtp:rtcp-fb:0";
public static final String JINGLE_RTP_SOURCE_SPECIFIC_MEDIA_ATTRIBUTES =
"urn:xmpp:jingle:apps:rtp:ssma:0";
public static final String IBB = "http://jabber.org/protocol/ibb";
public static final String PING = "urn:xmpp:ping";
public static final String PUSH = "urn:xmpp:push:0";
public static final String COMMANDS = "http://jabber.org/protocol/commands";
public static final String MUC_USER = "http://jabber.org/protocol/muc#user";
public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind";
public static final String BIND2 = "urn:xmpp:bind:0";
public static final String BLOCKING = "urn:xmpp:blocking";
public static final String BOOKMARKS = "storage:bookmarks";
public static final String BOOKMARKS2 = "urn:xmpp:bookmarks:0";
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
public static final String INVITE = "urn:xmpp:invite";
public static final String PARS = "urn:xmpp:pars:0";
public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0";
public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
public static final String CARBONS = "urn:xmpp:carbons:2";
public static final String CHANNEL_BINDING = "urn:xmpp:sasl-cb:0";
public static final String CHAT_MARKERS = "urn:xmpp:chat-markers:0";
public static final String CHAT_STATES = "http://jabber.org/protocol/chatstates";
public static final String COMMANDS = "http://jabber.org/protocol/commands";
public static final String CONFERENCE = "jabber:x:conference";
public static final String CSI = "urn:xmpp:csi:0";
public static final String DATA = "jabber:x:data";
public static final String DELIVERY_RECEIPTS = "urn:xmpp:receipts";
public static final String DISCO_INFO = "http://jabber.org/protocol/disco#info";
public static final String DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
public static final String ENTITY_CAPABILITIES = "http://jabber.org/protocol/caps";
public static final String ENTITY_CAPABILITIES_2 = "urn:xmpp:caps";
public static final String EXTERNAL_SERVICE_DISCOVERY = "urn:xmpp:extdisco:2";
public static final String FAST = "urn:xmpp:fast:0";
public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL =
"http://jabber.org/protocol/offline";
public static final String FORWARD = "urn:xmpp:forward:0";
public static final String HASHES = "urn:xmpp:hashes:2";
public static final String HTTP_UPLOAD = "urn:xmpp:http:upload:0";
public static final String HTTP_UPLOAD_LEGACY = "urn:xmpp:http:upload";
public static final String IBB = "http://jabber.org/protocol/ibb";
public static final String IDLE = "urn:xmpp:idle:1";
public static final String INVITE = "urn:xmpp:invite";
public static final String JABBER_CLIENT = "jabber:client";
public static final String JINGLE = "urn:xmpp:jingle:1";
public static final String JINGLE_APPS_DTLS = "urn:xmpp:jingle:apps:dtls:0";
public static final String JINGLE_APPS_GROUPING = "urn:xmpp:jingle:apps:grouping:0";
public static final String JINGLE_APPS_RTP = "urn:xmpp:jingle:apps:rtp:1";
public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0";
public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0";
public static final String JINGLE_ERRORS = "urn:xmpp:jingle:errors:1";
public static final String JINGLE_FEATURE_AUDIO = "urn:xmpp:jingle:apps:rtp:audio";
public static final String JINGLE_FEATURE_VIDEO = "urn:xmpp:jingle:apps:rtp:video";
public static final String JINGLE_FILE_TRANSFER_3 = "urn:xmpp:jingle:apps:file-transfer:3";
public static final String JINGLE_FILE_TRANSFER_4 = "urn:xmpp:jingle:apps:file-transfer:4";
public static final String JINGLE_FILE_TRANSFER_5 = "urn:xmpp:jingle:apps:file-transfer:5";
public static final String JINGLE_MESSAGE = "urn:xmpp:jingle-message:0";
public static final String JINGLE_RTP_FEEDBACK_NEGOTIATION =
"urn:xmpp:jingle:apps:rtp:rtcp-fb:0";
public static final String JINGLE_RTP_HEADER_EXTENSIONS =
"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0";
public static final String JINGLE_RTP_SOURCE_SPECIFIC_MEDIA_ATTRIBUTES =
"urn:xmpp:jingle:apps:rtp:ssma:0";
public static final String JINGLE_TRANSPORTS_IBB = "urn:xmpp:jingle:transports:ibb:1";
public static final String JINGLE_TRANSPORTS_S5B = "urn:xmpp:jingle:transports:s5b:1";
public static final String JINGLE_TRANSPORT_ICE_UDP = "urn:xmpp:jingle:transports:ice-udp:1";
public static final String LAST_MESSAGE_CORRECTION = "urn:xmpp:message-correct:0";
public static final String MUC = "http://jabber.org/protocol/muc";
public static final String MUC_USER = MUC + "#user";
public static final String NICK = "http://jabber.org/protocol/nick";
public static final String OMEMO_DTLS_SRTP_VERIFICATION =
"http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
public static final String JABBER_CLIENT = "jabber:client";
public static final String FORWARD = "urn:xmpp:forward:0";
public static final String OOB = "jabber:x:oob";
public static final String PARS = "urn:xmpp:pars:0";
public static final String PING = "urn:xmpp:ping";
public static final String PUBSUB = "http://jabber.org/protocol/pubsub";
public static final String PUBSUB_ERROR = PUBSUB + "#errors";
public static final String PUBSUB_OWNER = PUBSUB + "#owner";
public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options";
public static final String PUSH = "urn:xmpp:push:0";
public static final String REGISTER = "jabber:iq:register";
public static final String REGISTER_STREAM_FEATURE = "http://jabber.org/features/iq-register";
public static final String ROSTER = "jabber:iq:roster";
public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
public static final String SASL_2 = "urn:xmpp:sasl:2";
public static final String STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas";
public static final String STANZA_IDS = "urn:xmpp:sid:0";
public static final String STREAMS = "http://etherx.jabber.org/streams";
public static final String STREAM_MANAGEMENT = "urn:xmpp:sm:3";
public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0";
public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls";
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
public static final String VERSION = "jabber:iq:version";
}

View file

@ -8,6 +8,7 @@ import java.util.List;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
public class FileTransferDescription extends GenericDescription {
@ -72,9 +73,9 @@ public class FileTransferDescription extends GenericDescription {
}
public enum Version {
FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
FT_4("urn:xmpp:jingle:apps:file-transfer:4"),
FT_5("urn:xmpp:jingle:apps:file-transfer:5");
FT_3(Namespace.JINGLE_FILE_TRANSFER_3),
FT_4(Namespace.JINGLE_FILE_TRANSFER_4),
FT_5(Namespace.JINGLE_FILE_TRANSFER_5);
private final String namespace;

View file

@ -12,6 +12,7 @@ import im.conversations.android.xmpp.model.disco.info.Feature;
import im.conversations.android.xmpp.model.disco.info.Identity;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@ -101,6 +102,19 @@ public final class EntityCapabilities {
}
public abstract String capabilityNode(final String node);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hash hash1 = (Hash) o;
return Arrays.equals(hash, hash1.hash);
}
@Override
public int hashCode() {
return Arrays.hashCode(hash);
}
}
public static class EntityCapsHash extends Hash {

View file

@ -18,6 +18,7 @@ import im.conversations.android.xmpp.model.disco.info.Identity;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Objects;
public class EntityCapabilities2 {
@ -165,5 +166,19 @@ public class EntityCapabilities2 {
return String.format(
"%s#%s.%s", Namespace.ENTITY_CAPABILITIES_2, algorithm.toString(), encoded());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
EntityCaps2Hash that = (EntityCaps2Hash) o;
return algorithm == that.algorithm;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), algorithm);
}
}
}

View file

@ -8,6 +8,7 @@ import im.conversations.android.xmpp.manager.BlockingManager;
import im.conversations.android.xmpp.manager.BookmarkManager;
import im.conversations.android.xmpp.manager.CarbonsManager;
import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.manager.RosterManager;
public final class Managers {
@ -21,6 +22,7 @@ public final class Managers {
.put(BookmarkManager.class, new BookmarkManager(context, connection))
.put(CarbonsManager.class, new CarbonsManager(context, connection))
.put(DiscoManager.class, new DiscoManager(context, connection))
.put(PresenceManager.class, new PresenceManager(context, connection))
.put(RosterManager.class, new RosterManager(context, connection))
.build();
}

View file

@ -5,15 +5,22 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.io.BaseEncoding;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.disco.info.Feature;
import im.conversations.android.xmpp.model.disco.info.Identity;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import im.conversations.android.xmpp.model.disco.items.Item;
import im.conversations.android.xmpp.model.disco.items.ItemsQuery;
@ -25,6 +32,42 @@ import java.util.Objects;
public class DiscoManager extends AbstractManager {
public static final String CAPABILITY_NODE = "http://conversations.im";
private static final Collection<String> FEATURES_BASE =
Arrays.asList(
Namespace.JINGLE,
Namespace.JINGLE_FILE_TRANSFER_3,
Namespace.JINGLE_FILE_TRANSFER_4,
Namespace.JINGLE_FILE_TRANSFER_5,
Namespace.JINGLE_TRANSPORTS_S5B,
Namespace.JINGLE_TRANSPORTS_IBB,
Namespace.JINGLE_ENCRYPTED_TRANSPORT,
Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO,
Namespace.MUC,
Namespace.CONFERENCE,
Namespace.OOB,
Namespace.ENTITY_CAPABILITIES,
Namespace.ENTITY_CAPABILITIES_2,
Namespace.DISCO_INFO,
Namespace.PING,
Namespace.VERSION,
Namespace.CHAT_STATES,
Namespace.LAST_MESSAGE_CORRECTION,
Namespace.DELIVERY_RECEIPTS);
private static final Collection<String> FEATURES_AV_CALLS =
Arrays.asList(
Namespace.JINGLE_TRANSPORT_ICE_UDP,
Namespace.JINGLE_FEATURE_AUDIO,
Namespace.JINGLE_FEATURE_VIDEO,
Namespace.JINGLE_APPS_RTP,
Namespace.JINGLE_APPS_DTLS,
Namespace.JINGLE_MESSAGE);
private static final Collection<String> FEATURES_NOTIFY =
Arrays.asList(Namespace.NICK, Namespace.AVATAR_METADATA, Namespace.BOOKMARKS2);
public DiscoManager(Context context, XmppConnection connection) {
super(context, connection);
}
@ -161,4 +204,52 @@ public class DiscoManager extends AbstractManager {
public boolean hasServerFeature(final String feature) {
return hasFeature(getAccount().address.getDomain(), feature);
}
public InfoQuery getInfo() {
return getInfo(false);
}
private InfoQuery getInfo(final boolean privacyMode) {
final var infoQuery = new InfoQuery();
final ImmutableList.Builder<String> stringFeatureBuilder = ImmutableList.builder();
stringFeatureBuilder.addAll(FEATURES_BASE);
stringFeatureBuilder.addAll(
Collections2.transform(FEATURES_NOTIFY, fn -> String.format("%s+notify", fn)));
if (!privacyMode) {
stringFeatureBuilder.addAll(FEATURES_AV_CALLS);
}
final var stringFeatures = stringFeatureBuilder.build();
final Collection<Feature> features =
Collections2.transform(
stringFeatures,
sf -> {
final var feature = new Feature();
feature.setVar(sf);
return feature;
});
infoQuery.addExtensions(features);
final var identity = infoQuery.addExtension(new Identity());
identity.setIdentityName(getIdentityName());
identity.setCategory("client");
identity.setType(getIdentityType());
return infoQuery;
}
String getIdentityVersion() {
return BuildConfig.VERSION_NAME;
}
String getIdentityName() {
return BuildConfig.APP_NAME;
}
String getIdentityType() {
if ("chromium".equals(android.os.Build.BRAND)) {
return "pc";
} else if (context.getResources().getBoolean(R.bool.is_device_table)) {
return "tablet";
} else {
return "phone";
}
}
}

View file

@ -0,0 +1,48 @@
package im.conversations.android.xmpp.manager;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.capabilties.Capabilities;
import im.conversations.android.xmpp.model.capabilties.LegacyCapabilities;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import im.conversations.android.xmpp.model.stanza.Presence;
public class PresenceManager extends AbstractManager {
private static final Logger LOGGER = LoggerFactory.getLogger(PresenceManager.class);
private final Map<EntityCapabilities.Hash, InfoQuery> outgoingCapsHash = new HashMap<>();
public PresenceManager(Context context, XmppConnection connection) {
super(context, connection);
}
public void sendPresence() {
final var infoQuery = getManager(DiscoManager.class).getInfo();
final var capsHash = EntityCapabilities.hash(infoQuery);
final var caps2Hash = EntityCapabilities2.hash(infoQuery);
outgoingCapsHash.put(capsHash, infoQuery);
outgoingCapsHash.put(caps2Hash, infoQuery);
final var capabilities = new Capabilities();
capabilities.setHash(caps2Hash);
final var legacyCapabilities = new LegacyCapabilities();
legacyCapabilities.setNode(DiscoManager.CAPABILITY_NODE);
legacyCapabilities.setHash(capsHash);
final var presence = new Presence();
presence.addExtension(capabilities);
presence.addExtension(legacyCapabilities);
LOGGER.info(presence.toString());
connection.sendPresencePacket(presence);
}
}

View file

@ -17,6 +17,10 @@ public class Hash extends Extension {
return Algorithm.tryParse(this.getAttribute("algo"));
}
public void setAlgorithm(final Algorithm algorithm) {
this.setAttribute("algo", algorithm.toString());
}
public enum Algorithm {
SHA_1,
SHA_256,

View file

@ -32,4 +32,11 @@ public class Capabilities extends Extension {
}
return null;
}
public void setHash(final EntityCapabilities2.EntityCaps2Hash caps2Hash) {
final Hash hash = new Hash();
hash.setAlgorithm(caps2Hash.algorithm);
hash.setContent(caps2Hash.encoded());
this.addExtension(hash);
}
}

View file

@ -32,4 +32,13 @@ public class LegacyCapabilities extends Extension {
return null;
}
}
public void setNode(final String node) {
this.setAttribute("node", node);
}
public void setHash(final EntityCapabilities.EntityCapsHash hash) {
this.setAttribute("hash", HASH_ALGORITHM);
this.setAttribute("ver", hash.encoded());
}
}

View file

@ -12,4 +12,8 @@ public class Feature extends Extension {
public String getVar() {
return this.getAttribute("var");
}
public void setVar(final String feature) {
this.setAttribute("var", feature);
}
}

View file

@ -24,4 +24,16 @@ public class Identity extends Extension {
public String getIdentityName() {
return this.getAttribute("name");
}
public void setIdentityName(final String name) {
this.setAttribute("name", name);
}
public void setType(final String type) {
this.setAttribute("type", type);
}
public void setCategory(final String category) {
this.setAttribute("category", category);
}
}

View file

@ -8,6 +8,7 @@ import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.BlockingManager;
import im.conversations.android.xmpp.manager.BookmarkManager;
import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.manager.RosterManager;
import java.util.function.Consumer;
@ -46,8 +47,6 @@ public class BindProcessor extends XmppConnection.Delegate implements Consumer<J
getManager(BookmarkManager.class).fetch();
// connection.sendPresencePacket(new Presence());
// TODO send initial presence
getManager(PresenceManager.class).sendPresence();
}
}

View file

@ -71,7 +71,7 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq>
for (final Extension extension : extensions) {
response.addExtension(extension);
}
connection.sendIqPacket(response);
connection.sendIqPacket(response, null);
}
public void sendErrorFor(final Iq request, final Condition condition) {
@ -82,6 +82,6 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq>
response.setId(id);
final Error error = response.addExtension(new Error());
error.setCondition(condition);
connection.sendIqPacket(response);
connection.sendIqPacket(response, null);
}
}

View file

@ -2,4 +2,5 @@
<resources>
<string name="default_resource">Tablet</string>
<bool name="portrait_only">false</bool>
<bool name="is_device_table">true</bool>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_device_table">true</bool>
<string name="default_resource">Phone</string>
<bool name="portrait_only">true</bool>
<bool name="enter_is_send">false</bool>