~singpolyma/cheogram-android

b6703dbe38b1e1482c8f60d73d89d791a8090ca5 — Daniel Gultsch 3 years ago 3510f10
switch xmpp-addr to jxmpp-jid
110 files changed, 460 insertions(+), 1787 deletions(-)

M build.gradle
D libs/xmpp-addr/.gitignore
D libs/xmpp-addr/build.gradle
D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/AbstractJid.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/FullJid.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/Jid.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/MalformedJid.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/package-info.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/DirectoryCache.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/LruCache.java
D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/package-info.java
M src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java
M src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
M src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java
M src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java
M src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java
M src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java
M src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
M src/main/java/eu/siacs/conversations/Config.java
M src/main/java/eu/siacs/conversations/android/JabberIdContact.java
M src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
M src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
M src/main/java/eu/siacs/conversations/crypto/sasl/External.java
M src/main/java/eu/siacs/conversations/crypto/sasl/ScramMechanism.java
M src/main/java/eu/siacs/conversations/entities/Account.java
M src/main/java/eu/siacs/conversations/entities/Blockable.java
M src/main/java/eu/siacs/conversations/entities/Bookmark.java
M src/main/java/eu/siacs/conversations/entities/Contact.java
M src/main/java/eu/siacs/conversations/entities/Conversation.java
M src/main/java/eu/siacs/conversations/entities/Conversational.java
M src/main/java/eu/siacs/conversations/entities/IndividualMessage.java
M src/main/java/eu/siacs/conversations/entities/ListItem.java
M src/main/java/eu/siacs/conversations/entities/Message.java
M src/main/java/eu/siacs/conversations/entities/MucOptions.java
M src/main/java/eu/siacs/conversations/entities/RawBlockable.java
M src/main/java/eu/siacs/conversations/entities/ReadByMarker.java
M src/main/java/eu/siacs/conversations/entities/ReceiptRequest.java
M src/main/java/eu/siacs/conversations/entities/Room.java
M src/main/java/eu/siacs/conversations/entities/Roster.java
M src/main/java/eu/siacs/conversations/entities/StubConversation.java
M src/main/java/eu/siacs/conversations/generator/IqGenerator.java
M src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
M src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
M src/main/java/eu/siacs/conversations/http/SlotRequester.java
M src/main/java/eu/siacs/conversations/parser/AbstractParser.java
M src/main/java/eu/siacs/conversations/parser/IqParser.java
M src/main/java/eu/siacs/conversations/parser/MessageParser.java
M src/main/java/eu/siacs/conversations/parser/PresenceParser.java
M src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
M src/main/java/eu/siacs/conversations/services/AvatarService.java
M src/main/java/eu/siacs/conversations/services/BarcodeProvider.java
M src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java
M src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
M src/main/java/eu/siacs/conversations/services/MessageSearchTask.java
M src/main/java/eu/siacs/conversations/services/ShortcutService.java
M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
M src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
M src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
M src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java
M src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java
M src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
M src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
M src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
M src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
M src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java
M src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
M src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
M src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java
M src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java
M src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
M src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
M src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
M src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
M src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
M src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
M src/main/java/eu/siacs/conversations/ui/XmppActivity.java
M src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java
M src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
M src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
M src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
M src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java
M src/main/java/eu/siacs/conversations/ui/forms/FormJidSingleFieldWrapper.java
M src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java
M src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java
M src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java
M src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java
M src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
M src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java
M src/main/java/eu/siacs/conversations/utils/JidHelper.java
M src/main/java/eu/siacs/conversations/utils/NickValidityChecker.java
M src/main/java/eu/siacs/conversations/utils/UIHelper.java
M src/main/java/eu/siacs/conversations/utils/XmppUri.java
M src/main/java/eu/siacs/conversations/xml/Element.java
M src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java
A src/main/java/eu/siacs/conversations/xmpp/Jid.java
A src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java
M src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java
M src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java
M src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java
M src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java
M src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java
M build.gradle => build.gradle +1 -1
@@ 67,7 67,7 @@ dependencies {
    implementation 'com.makeramen:roundedimageview:2.3.0'
    implementation "com.wefika:flowlayout:0.4.1"
    implementation 'net.ypresto.androidtranscoder:android-transcoder:0.3.0'
    implementation project(':libs:xmpp-addr')
    implementation 'org.jxmpp:jxmpp-jid:0.6.4'
    implementation 'org.osmdroid:osmdroid-android:6.1.5'
    implementation 'org.hsluv:hsluv:0.2'
    implementation 'org.conscrypt:conscrypt-android:2.2.1'

D libs/xmpp-addr/.gitignore => libs/xmpp-addr/.gitignore +0 -1
@@ 1,1 0,0 @@
/build

D libs/xmpp-addr/build.gradle => libs/xmpp-addr/build.gradle +0 -14
@@ 1,14 0,0 @@
apply plugin: 'java-library'

repositories {
    google()
    jcenter()
    mavenCentral()
}

dependencies {
    implementation 'rocks.xmpp:precis:1.0.0'
}

sourceCompatibility = "8"
targetCompatibility = "8"

D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/AbstractJid.java => libs/xmpp-addr/src/main/java/rocks/xmpp/addr/AbstractJid.java +0 -179
@@ 1,179 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2017 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.addr;

import java.text.Collator;
import java.util.Arrays;

/**
 * Abstract Jid implementation for both full and bare JIDs.
 *
 * @author Christian Schudt
 */
abstract class AbstractJid implements Jid {

    /**
     * Checks if the JID is a full JID.
     * <blockquote>
     * <p>The term "full JID" refers to an XMPP address of the form &lt;localpart@domainpart/resourcepart&gt; (for a particular authorized client or device associated with an account) or of the form &lt;domainpart/resourcepart&gt; (for a particular resource or script associated with a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a full JID; otherwise false.
     */
    @Override
    public final boolean isFullJid() {
        return getResource() != null;
    }

    /**
     * Checks if the JID is a bare JID.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a bare JID; otherwise false.
     */
    @Override
    public final boolean isBareJid() {
        return getResource() == null;
    }

    @Override
    public final boolean isDomainJid() {
        return getLocal() == null;
    }

    @Override
    public final boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof Jid)) {
            return false;
        }
        Jid other = (Jid) o;

        return (getLocal() == other.getLocal() || getLocal() != null && getLocal().equals(other.getLocal()))
            && (getDomain() == other.getDomain() || getDomain() != null && getDomain().equals(other.getDomain()))
            && (getResource() == other.getResource() || getResource() != null && getResource().equals(other.getResource()));
    }

    @Override
    public final int hashCode() {
        return Arrays.hashCode(new String[]{getLocal(), getDomain(), getResource()});
    }

    /**
     * Compares this JID with another JID. First domain parts are compared. If these are equal, local parts are compared
     * and if these are equal, too, resource parts are compared.
     *
     * @param o The other JID.
     * @return The comparison result.
     */
    @Override
    public final int compareTo(Jid o) {

        if (this == o) {
            return 0;
        }

        if (o != null) {
            final Collator collator = Collator.getInstance();
            int result;
            // First compare domain parts.
            if (getDomain() != null) {
                result = o.getDomain() != null ? collator.compare(getDomain(), o.getDomain()) : -1;
            } else {
                result = o.getDomain() != null ? 1 : 0;
            }
            // If the domains are equal, compare local parts.
            if (result == 0) {
                if (getLocal() != null) {
                    // If this local part is not null, but the other is null, move this down (1).
                    result = o.getLocal() != null ? collator.compare(getLocal(), o.getLocal()) : 1;
                } else {
                    // If this local part is null, but the other is not, move this up (-1).
                    result = o.getLocal() != null ? -1 : 0;
                }
            }
            // If the local parts are equal, compare resource parts.
            if (result == 0) {
                if (getResource() != null) {
                    // If this resource part is not null, but the other is null, move this down (1).
                    return o.getResource() != null ? collator.compare(getResource(), o.getResource()) : 1;
                } else {
                    // If this resource part is null, but the other is not, move this up (-1).
                    return o.getResource() != null ? -1 : 0;
                }
            }
            return result;
        } else {
            return -1;
        }
    }

    @Override
    public final int length() {
        return toString().length();
    }

    @Override
    public final char charAt(int index) {
        return toString().charAt(index);
    }

    @Override
    public final CharSequence subSequence(int start, int end) {
        return toString().subSequence(start, end);
    }

    /**
     * Returns the JID in its string representation, i.e. [ localpart "@" ] domainpart [ "/" resourcepart ].
     *
     * @return The JID.
     * @see #toEscapedString()
     */
    @Override
    public final String toString() {
        return toString(getLocal(), getDomain(), getResource());
    }

    @Override
    public final String toEscapedString() {
        return toString(getEscapedLocal(), getDomain(), getResource());
    }

    static String toString(String local, String domain, String resource) {
        StringBuilder sb = new StringBuilder();
        if (local != null) {
            sb.append(local).append('@');
        }
        sb.append(domain);
        if (resource != null) {
            sb.append('/').append(resource);
        }
        return sb.toString();
    }
}

D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/FullJid.java => libs/xmpp-addr/src/main/java/rocks/xmpp/addr/FullJid.java +0 -498
@@ 1,498 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2017 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.addr;

import rocks.xmpp.precis.PrecisProfile;
import rocks.xmpp.precis.PrecisProfiles;
import rocks.xmpp.util.cache.LruCache;

import java.net.IDN;
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The implementation of the JID as described in <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>.
 * <p>
 * This class is thread-safe and immutable.
 *
 * @author Christian Schudt
 * @see <a href="https://tools.ietf.org/html/rfc7622">RFC 7622 - Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
 */
final class FullJid extends AbstractJid {

    /**
     * Escapes all disallowed characters and also backslash, when followed by a defined hex code for escaping. See 4. Business Rules.
     */
    private static final Pattern ESCAPE_PATTERN = Pattern.compile("[ \"&'/:<>@]|\\\\(?=20|22|26|27|2f|3a|3c|3e|40|5c)");

    private static final Pattern UNESCAPE_PATTERN = Pattern.compile("\\\\(20|22|26|27|2f|3a|3c|3e|40|5c)");

    private static final Pattern JID = Pattern.compile("^((.*?)@)?([^/@]+)(/(.*))?$");

    private static final IDNProfile IDN_PROFILE = new IDNProfile();

    /**
     * Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full stop).
     */
    private static final String DOTS = "[.\u3002\uFF0E\uFF61]";

    /**
     * Label separators for domain labels, which should be mapped to "." (dot): IDEOGRAPHIC FULL STOP character (U+3002)
     */
    private static final Pattern LABEL_SEPARATOR = Pattern.compile(DOTS);

    private static final Pattern LABEL_SEPARATOR_FINAL = Pattern.compile(DOTS + "$");

    /**
     * Caches the escaped JIDs.
     */
    private static final Map<CharSequence, Jid> ESCAPED_CACHE = new LruCache<>(5000);

    /**
     * Caches the unescaped JIDs.
     */
    private static final Map<CharSequence, Jid> UNESCAPED_CACHE = new LruCache<>(5000);

    private static final long serialVersionUID = -3824234106101731424L;

    private final String escapedLocal;

    private final String local;

    private final String domain;

    private final String resource;

    private final Jid bareJid;

    /**
     * Creates a full JID with local, domain and resource part.
     *
     * @param local    The local part.
     * @param domain   The domain part.
     * @param resource The resource part.
     */
    FullJid(CharSequence local, CharSequence domain, CharSequence resource) {
        this(local, domain, resource, false, null);
    }

    private FullJid(final CharSequence local, final CharSequence domain, final CharSequence resource, final boolean doUnescape, Jid bareJid) {
        final String enforcedLocalPart;
        final String enforcedDomainPart;
        final String enforcedResource;

        final String unescapedLocalPart;

        if (domain == null) {
            throw new NullPointerException();
        }

        if (doUnescape) {
            unescapedLocalPart = unescape(local);
        } else {
            unescapedLocalPart = local != null ? local.toString() : null;
        }

        // Escape the local part, so that disallowed characters like the space characters pass the UsernameCaseMapped profile.
        final String escapedLocalPart = escape(unescapedLocalPart);

        // If the domainpart includes a final character considered to be a label
        // separator (dot) by [RFC1034], this character MUST be stripped from
        // the domainpart before the JID of which it is a part is used for the
        // purpose of routing an XML stanza, comparing against another JID, or
        // constructing an XMPP URI or IRI [RFC5122].  In particular, such a
        // character MUST be stripped before any other canonicalization steps
        // are taken.
        // Also validate, that the domain name can be converted to ASCII, i.e. validate the domain name (e.g. must not start with "_").
        final String strDomain = IDN.toASCII(LABEL_SEPARATOR_FINAL.matcher(domain).replaceAll(""), IDN.USE_STD3_ASCII_RULES);
        enforcedLocalPart = escapedLocalPart != null ? PrecisProfiles.USERNAME_CASE_MAPPED.enforce(escapedLocalPart) : null;
        enforcedResource = resource != null ? PrecisProfiles.OPAQUE_STRING.enforce(resource) : null;
        // See https://tools.ietf.org/html/rfc5895#section-2
        enforcedDomainPart = IDN_PROFILE.enforce(strDomain);

        validateLength(enforcedLocalPart, "local");
        validateLength(enforcedResource, "resource");
        validateDomain(strDomain);

        this.local = unescape(enforcedLocalPart);
        this.escapedLocal = enforcedLocalPart;
        this.domain = enforcedDomainPart;
        this.resource = enforcedResource;
        if (bareJid != null) {
            this.bareJid = bareJid;
        } else {
            this.bareJid = isBareJid() ? this : new AbstractJid() {

                @Override
                public Jid asBareJid() {
                    return this;
                }

                @Override
                public Jid withLocal(CharSequence local) {
                    if (local == this.getLocal() || local != null && local.equals(this.getLocal())) {
                        return this;
                    }
                    return new FullJid(local, getDomain(), getResource(), false, null);
                }

                @Override
                public Jid withResource(CharSequence resource) {
                    if (resource == this.getResource() || resource != null && resource.equals(this.getResource())) {
                        return this;
                    }
                    return new FullJid(getLocal(), getDomain(), resource, false, asBareJid());
                }

                @Override
                public Jid atSubdomain(CharSequence subdomain) {
                    if (subdomain == null) {
                        throw new NullPointerException();
                    }
                    return new FullJid(getLocal(), subdomain + "." + getDomain(), getResource(), false, null);
                }

                @Override
                public String getLocal() {
                    return FullJid.this.getLocal();
                }

                @Override
                public String getEscapedLocal() {
                    return FullJid.this.getEscapedLocal();
                }

                @Override
                public String getDomain() {
                    return FullJid.this.getDomain();
                }

                @Override
                public String getResource() {
                    return null;
                }
            };
        }
    }

    /**
     * Creates a JID from a string. The format must be
     * <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
     *
     * @param jid        The JID.
     * @param doUnescape If the jid parameter will be unescaped.
     * @return The JID.
     * @throws NullPointerException     If the jid is null.
     * @throws IllegalArgumentException If the jid could not be parsed or is not valid.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    static Jid of(String jid, final boolean doUnescape) {
        if (jid == null) {
            throw new NullPointerException("jid must not be null.");
        }

        jid = jid.trim();

        if (jid.isEmpty()) {
            throw new IllegalArgumentException("jid must not be empty.");
        }

        Jid result;
        if (doUnescape) {
            result = UNESCAPED_CACHE.get(jid);
        } else {
            result = ESCAPED_CACHE.get(jid);
        }

        if (result != null) {
            return result;
        }

        Matcher matcher = JID.matcher(jid);
        if (matcher.matches()) {
            Jid jidValue = new FullJid(matcher.group(2), matcher.group(3), matcher.group(5), doUnescape, null);
            if (doUnescape) {
                UNESCAPED_CACHE.put(jid, jidValue);
            } else {
                ESCAPED_CACHE.put(jid, jidValue);
            }
            return jidValue;
        } else {
            throw new IllegalArgumentException("Could not parse JID: " + jid);
        }
    }

    /**
     * Escapes a local part. The characters {@code "&'/:<>@} (+ whitespace) are replaced with their respective escape characters.
     *
     * @param localPart The local part.
     * @return The escaped local part or null.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    private static String escape(final CharSequence localPart) {
        if (localPart != null) {
            final Matcher matcher = ESCAPE_PATTERN.matcher(localPart);
            final StringBuffer sb = new StringBuffer();
            while (matcher.find()) {
                matcher.appendReplacement(sb, "\\\\" + Integer.toHexString(matcher.group().charAt(0)));
            }
            matcher.appendTail(sb);
            return sb.toString();
        }
        return null;
    }

    private static String unescape(final CharSequence localPart) {
        if (localPart != null) {
            final Matcher matcher = UNESCAPE_PATTERN.matcher(localPart);
            final StringBuffer sb = new StringBuffer();
            while (matcher.find()) {
                final char c = (char) Integer.parseInt(matcher.group(1), 16);
                if (c == '\\') {
                    matcher.appendReplacement(sb, "\\\\");
                } else {
                    matcher.appendReplacement(sb, String.valueOf(c));
                }
            }
            matcher.appendTail(sb);
            return sb.toString();
        }
        return null;
    }

    private static void validateDomain(String domain) {
        if (domain == null) {
            throw new NullPointerException("domain must not be null.");
        }
        if (domain.contains("@")) {
            // Prevent misuse of API.
            throw new IllegalArgumentException("domain must not contain a '@' sign");
        }
        validateLength(domain, "domain");
    }

    /**
     * Validates that the length of a local, domain or resource part is not longer than 1023 characters.
     *
     * @param value The value.
     * @param part  The part, only used to produce an exception message.
     */
    private static void validateLength(CharSequence value, CharSequence part) {
        if (value != null) {
            if (value.length() == 0) {
                throw new IllegalArgumentException(part + " must not be empty.");
            }
            if (value.toString().getBytes(Charset.forName("UTF-8")).length > 1023) {
                throw new IllegalArgumentException(part + " must not be greater than 1023 bytes.");
            }
        }
    }

    /**
     * Converts this JID into a bare JID, i.e. removes the resource part.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return The bare JID.
     * @see #withResource(CharSequence)
     */
    @Override
    public final Jid asBareJid() {
        return bareJid;
    }

    /**
     * Gets the local part of the JID, also known as the name or node.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.3">3.3.  Localpart</a></cite></p>
     * <p>The localpart of a JID is an optional identifier placed before the
     * domainpart and separated from the latter by the '@' character.
     * Typically, a localpart uniquely identifies the entity requesting and
     * using network access provided by a server (i.e., a local account),
     * although it can also represent other kinds of entities (e.g., a
     * chatroom associated with a multi-user chat service [XEP-0045]).  The
     * entity represented by an XMPP localpart is addressed within the
     * context of a specific domain (i.e., &lt;localpart@domainpart&gt;).</p>
     * </blockquote>
     *
     * @return The local part or null.
     */
    @Override
    public final String getLocal() {
        return local;
    }

    @Override
    public final String getEscapedLocal() {
        return escapedLocal;
    }

    /**
     * Gets the domain part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.2">3.2.  Domainpart</a></cite></p>
     * <p>The domainpart is the primary identifier and is the only REQUIRED
     * element of a JID (a mere domainpart is a valid JID).  Typically,
     * a domainpart identifies the "home" server to which clients connect
     * for XML routing and data management functionality.</p>
     * </blockquote>
     *
     * @return The domain part.
     */
    @Override
    public final String getDomain() {
        return domain;
    }

    /**
     * Gets the resource part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.4">3.4.  Resourcepart</a></cite></p>
     * <p>The resourcepart of a JID is an optional identifier placed after the
     * domainpart and separated from the latter by the '/' character.  A
     * resourcepart can modify either a &lt;localpart@domainpart&gt; address or a
     * mere &lt;domainpart&gt; address.  Typically, a resourcepart uniquely
     * identifies a specific connection (e.g., a device or location) or
     * object (e.g., an occupant in a multi-user chatroom [XEP-0045])
     * belonging to the entity associated with an XMPP localpart at a domain
     * (i.e., &lt;localpart@domainpart/resourcepart&gt;).</p>
     * </blockquote>
     *
     * @return The resource part or null.
     */
    @Override
    public final String getResource() {
        return resource;
    }

    /**
     * Creates a new JID with a new local part and the same domain and resource part of the current JID.
     *
     * @param local The local part.
     * @return The JID with a new local part.
     * @throws IllegalArgumentException If the local is not a valid local part.
     * @see #withResource(CharSequence)
     */
    @Override
    public final Jid withLocal(CharSequence local) {
        if (local == this.getLocal() || local != null && local.equals(this.getLocal())) {
            return this;
        }
        return new FullJid(local, getDomain(), getResource(), false, null);
    }

    /**
     * Creates a new full JID with a resource and the same local and domain part of the current JID.
     *
     * @param resource The resource.
     * @return The full JID with a resource.
     * @throws IllegalArgumentException If the resource is not a valid resource part.
     * @see #asBareJid()
     * @see #withLocal(CharSequence)
     */
    @Override
    public final Jid withResource(CharSequence resource) {
        if (resource == this.getResource() || resource != null && resource.equals(this.getResource())) {
            return this;
        }
        return new FullJid(getLocal(), getDomain(), resource, false, asBareJid());
    }

    /**
     * Creates a new JID at a subdomain and at the same domain as this JID.
     *
     * @param subdomain The subdomain.
     * @return The JID at a subdomain.
     * @throws NullPointerException     If subdomain is null.
     * @throws IllegalArgumentException If subdomain is not a valid subdomain name.
     */
    @Override
    public final Jid atSubdomain(CharSequence subdomain) {
        if (subdomain != null) {
            throw new NullPointerException();
        }
        return new FullJid(getLocal(), subdomain + "." + getDomain(), getResource(), false, null);
    }

    /**
     * A profile for applying the rules for IDN as in RFC 5895. Although IDN doesn't use Precis, it's still very similar so that we can use the base class.
     *
     * @see <a href="https://tools.ietf.org/html/rfc5895#section-2">RFC 5895</a>
     */
    private static final class IDNProfile extends PrecisProfile {

        private IDNProfile() {
            super(false);
        }

        @Override
        public String prepare(CharSequence input) {
            return IDN.toUnicode(input.toString(), IDN.USE_STD3_ASCII_RULES);
        }

        @Override
        public String enforce(CharSequence input) {
            // 4. Map IDEOGRAPHIC FULL STOP character (U+3002) to dot.
            return applyAdditionalMappingRule(
                    // 3.  All characters are mapped using Unicode Normalization Form C (NFC).
                    applyNormalizationRule(
                            // 2. Fullwidth and halfwidth characters (those defined with
                            // Decomposition Types <wide> and <narrow>) are mapped to their
                            // decomposition mappings
                            applyWidthMappingRule(
                                    // 1. Uppercase characters are mapped to their lowercase equivalents
                                    applyCaseMappingRule(prepare(input))))).toString();
        }

        @Override
        protected CharSequence applyWidthMappingRule(CharSequence charSequence) {
            return widthMap(charSequence);
        }

        @Override
        protected CharSequence applyAdditionalMappingRule(CharSequence charSequence) {
            return LABEL_SEPARATOR.matcher(charSequence).replaceAll(".");
        }

        @Override
        protected CharSequence applyCaseMappingRule(CharSequence charSequence) {
            return charSequence.toString().toLowerCase();
        }

        @Override
        protected CharSequence applyNormalizationRule(CharSequence charSequence) {
            return Normalizer.normalize(charSequence, Normalizer.Form.NFC);
        }

        @Override
        protected CharSequence applyDirectionalityRule(CharSequence charSequence) {
            return charSequence;
        }
    }
}

D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/Jid.java => libs/xmpp-addr/src/main/java/rocks/xmpp/addr/Jid.java +0 -313
@@ 1,313 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2017 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.addr;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;

/**
 * Represents the JID as described in <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>.
 * <p>
 * A JID consists of three parts:
 * <p>
 * [ localpart "@" ] domainpart [ "/" resourcepart ]
 * </p>
 * The easiest way to create a JID is to use the {@link #of(CharSequence)} method:
 * ```java
 * Jid jid = Jid.of("juliet@capulet.lit/balcony");
 * ```
 * You can then get the parts from it via the respective methods:
 * ```java
 * String local = jid.getLocal(); // juliet
 * String domain = jid.getDomain(); // capulet.lit
 * String resource = jid.getResource(); // balcony
 * ```
 * Implementations of this interface should override <code>equals()</code> and <code>hashCode()</code>, so that different instances with the same value are equal:
 * ```java
 * Jid.of("romeo@capulet.lit/balcony").equals(Jid.of("romeo@capulet.lit/balcony")); // true
 * ```
 * The default implementation of this class also supports <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>, i.e.
 * ```java
 * Jid.of("d'artagnan@musketeers.lit")
 * ```
 * is escaped as <code>d\\27artagnan@musketeers.lit</code>.
 * <p>
 * Implementations of this interface should be thread-safe and immutable.
 *
 * @author Christian Schudt
 * @see <a href="https://tools.ietf.org/html/rfc7622">RFC 7622 - Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
 */
public interface Jid extends Comparable<Jid>, Serializable, CharSequence {

    /**
     * The maximal length of a full JID, which is 3071.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.1">3.1.  Fundamentals</a></cite></p>
     * <p>Each allowable portion of a JID (localpart, domainpart, and
     * resourcepart) is 1 to 1023 octets in length, resulting in a maximum
     * total size (including the '@' and '/' separators) of 3071 octets.
     * </p>
     * </blockquote>
     * Note that the length is based on bytes, not characters.
     *
     * @see #MAX_BARE_JID_LENGTH
     */
    int MAX_FULL_JID_LENGTH = 3071;

    /**
     * The maximal length of a bare JID, which is 2047 (1023 + 1 + 1023).
     * Note that the length is based on bytes, not characters.
     *
     * @see #MAX_FULL_JID_LENGTH
     */
    int MAX_BARE_JID_LENGTH = 2047;

    /**
     * The service discovery feature used for determining support of JID escaping (<code>jid\20escaping</code>).
     */
    String ESCAPING_FEATURE = "jid\\20escaping";

    /**
     * Returns a full JID with a domain and resource part, e.g. <code>capulet.com/balcony</code>
     *
     * @param local    The local part.
     * @param domain   The domain.
     * @param resource The resource part.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain, local or resource part are not valid.
     */
    static Jid of(CharSequence local, CharSequence domain, CharSequence resource) {
        return new FullJid(local, domain, resource);
    }

    /**
     * Creates a bare JID with only the domain part, e.g. <code>capulet.com</code>
     *
     * @param domain The domain.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain or local part are not valid.
     */
    static Jid ofDomain(CharSequence domain) {
        return new FullJid(null, domain, null);
    }

    /**
     * Creates a bare JID with a local and domain part, e.g. <code>juliet@capulet.com</code>
     *
     * @param local  The local part.
     * @param domain The domain.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain or local part are not valid.
     */
    static Jid ofLocalAndDomain(CharSequence local, CharSequence domain) {
        return new FullJid(local, domain, null);
    }

    /**
     * Creates a full JID with a domain and resource part, e.g. <code>capulet.com/balcony</code>
     *
     * @param domain   The domain.
     * @param resource The resource part.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain or resource are not valid.
     */
    static Jid ofDomainAndResource(CharSequence domain, CharSequence resource) {
        return new FullJid(null, domain, resource);
    }

    /**
     * Creates a JID from an unescaped string. The format must be
     * <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
     * The input string will be escaped.
     *
     * @param jid The JID.
     * @return The JID.
     * @throws NullPointerException     If the jid is null.
     * @throws IllegalArgumentException If the jid could not be parsed or is not valid.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    static Jid of(CharSequence jid) {
        if (jid instanceof Jid) {
            return (Jid) jid;
        }
        return FullJid.of(jid.toString(), false);
    }

    /**
     * Creates a JID from a escaped JID string. The format must be
     * <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
     * This method should be used, when parsing JIDs from the XMPP stream.
     *
     * @param jid The JID.
     * @return The JID.
     * @throws NullPointerException     If the jid is null.
     * @throws IllegalArgumentException If the jid could not be parsed or is not valid.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    static Jid ofEscaped(CharSequence jid) {
        return FullJid.of(jid.toString(), true);
    }

    /**
     * Checks if the JID is a full JID.
     * <blockquote>
     * <p>The term "full JID" refers to an XMPP address of the form &lt;localpart@domainpart/resourcepart&gt; (for a particular authorized client or device associated with an account) or of the form &lt;domainpart/resourcepart&gt; (for a particular resource or script associated with a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a full JID; otherwise false.
     */
    boolean isFullJid();

    /**
     * Checks if the JID is a bare JID.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a bare JID; otherwise false.
     */
    boolean isBareJid();

    /**
     * Checks if the JID is a domain JID, i.e. if it has no local part.
     *
     * @return True, if the JID is a domain JID, i.e. if it has no local part.
     */
    boolean isDomainJid();

    /**
     * Gets the bare JID representation of this JID, i.e. removes the resource part.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return The bare JID.
     * @see #withResource(CharSequence)
     */
    Jid asBareJid();

    /**
     * Creates a new JID with a new local part and the same domain and resource part of the current JID.
     *
     * @param local The local part.
     * @return The JID with a new local part.
     * @throws IllegalArgumentException If the local is not a valid local part.
     * @see #withResource(CharSequence)
     */
    Jid withLocal(CharSequence local);

    /**
     * Creates a new full JID with a resource and the same local and domain part of the current JID.
     *
     * @param resource The resource.
     * @return The full JID with a resource.
     * @throws IllegalArgumentException If the resource is not a valid resource part.
     * @see #asBareJid()
     * @see #withLocal(CharSequence)
     */
    Jid withResource(CharSequence resource);

    /**
     * Creates a new JID at a subdomain and at the same domain as this JID.
     *
     * @param subdomain The subdomain.
     * @return The JID at a subdomain.
     * @throws NullPointerException     If subdomain is null.
     * @throws IllegalArgumentException If subdomain is not a valid subdomain name.
     */
    Jid atSubdomain(CharSequence subdomain);

    /**
     * Gets the local part of the JID, also known as the name or node.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.3">3.3.  Localpart</a></cite></p>
     * <p>The localpart of a JID is an optional identifier placed before the
     * domainpart and separated from the latter by the '@' character.
     * Typically, a localpart uniquely identifies the entity requesting and
     * using network access provided by a server (i.e., a local account),
     * although it can also represent other kinds of entities (e.g., a
     * chatroom associated with a multi-user chat service [XEP-0045]).  The
     * entity represented by an XMPP localpart is addressed within the
     * context of a specific domain (i.e., &lt;localpart@domainpart&gt;).</p>
     * </blockquote>
     *
     * @return The local part or null.
     * @see #getEscapedLocal()
     */
    String getLocal();

    /**
     * Gets the escaped local part of the JID.
     *
     * @return The escaped local part or null.
     * @see #getLocal()
     * @since 0.8.0
     */
    String getEscapedLocal();

    /**
     * Gets the domain part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.2">3.2.  Domainpart</a></cite></p>
     * <p>The domainpart is the primary identifier and is the only REQUIRED
     * element of a JID (a mere domainpart is a valid JID).  Typically,
     * a domainpart identifies the "home" server to which clients connect
     * for XML routing and data management functionality.</p>
     * </blockquote>
     *
     * @return The domain part.
     */
    String getDomain();

    /**
     * Gets the resource part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.4">3.4.  Resourcepart</a></cite></p>
     * <p>The resourcepart of a JID is an optional identifier placed after the
     * domainpart and separated from the latter by the '/' character.  A
     * resourcepart can modify either a &lt;localpart@domainpart&gt; address or a
     * mere &lt;domainpart&gt; address.  Typically, a resourcepart uniquely
     * identifies a specific connection (e.g., a device or location) or
     * object (e.g., an occupant in a multi-user chatroom [XEP-0045])
     * belonging to the entity associated with an XMPP localpart at a domain
     * (i.e., &lt;localpart@domainpart/resourcepart&gt;).</p>
     * </blockquote>
     *
     * @return The resource part or null.
     */
    String getResource();

    /**
     * Returns the JID in escaped form as described in <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>.
     *
     * @return The escaped JID.
     * @see #toString()
     */
    String toEscapedString();
}

D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/MalformedJid.java => libs/xmpp-addr/src/main/java/rocks/xmpp/addr/MalformedJid.java +0 -131
@@ 1,131 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2017 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.addr;

/**
 * Represents a malformed JID in order to handle the <code>jid-malformed</code> error.
 * <p>
 * This class is not intended to be publicly instantiable, but is used for malformed JIDs during parsing automatically.
 *
 * @author Christian Schudt
 * @see <a href="https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions-jid-malformed">RFC 6120, 8.3.3.8.  jid-malformed</a>
 */
public final class MalformedJid extends AbstractJid {

    private static final long serialVersionUID = -2896737611021417985L;

    private final String localPart;

    private final String domainPart;

    private final String resourcePart;

    private final Throwable cause;

    static MalformedJid of(final String jid, final Throwable cause) {
        // Do some basic parsing without any further checks or validation.
        final StringBuilder sb = new StringBuilder(jid);
        // 1.  Remove any portion from the first '/' character to the end of the
        // string (if there is a '/' character present).
        final int indexOfResourceDelimiter = jid.indexOf('/');
        final String resourcePart;
        if (indexOfResourceDelimiter > -1) {
            resourcePart = sb.substring(indexOfResourceDelimiter + 1);
            sb.delete(indexOfResourceDelimiter, sb.length());
        } else {
            resourcePart = null;
        }
        // 2.  Remove any portion from the beginning of the string to the first
        // '@' character (if there is an '@' character present).
        final int indexOfAt = jid.indexOf('@');
        final String localPart;
        if (indexOfAt > -1) {
            localPart = sb.substring(0, indexOfAt);
            sb.delete(0, indexOfAt + 1);
        } else {
            localPart = null;
        }
        return new MalformedJid(localPart, sb.toString(), resourcePart, cause);
    }

    private MalformedJid(final String localPart, final String domainPart, final String resourcePart, final Throwable cause) {
        this.localPart = localPart;
        this.domainPart = domainPart;
        this.resourcePart = resourcePart;
        this.cause = cause;
    }

    @Override
    public final Jid asBareJid() {
        return new MalformedJid(localPart, domainPart, null, cause);
    }

    @Override
    public Jid withLocal(CharSequence local) {
        return new MalformedJid(local.toString(), domainPart, resourcePart, cause);
    }

    @Override
    public Jid withResource(CharSequence resource) {
        return new MalformedJid(localPart, domainPart, resource.toString(), cause);
    }

    @Override
    public Jid atSubdomain(CharSequence subdomain) {
        if (subdomain == null) {
            throw new NullPointerException();
        }
        return new MalformedJid(localPart, subdomain + "." + domainPart, resourcePart, cause);
    }

    @Override
    public final String getLocal() {
        return localPart;
    }

    @Override
    public final String getEscapedLocal() {
        return localPart;
    }

    @Override
    public final String getDomain() {
        return domainPart;
    }

    @Override
    public final String getResource() {
        return resourcePart;
    }

    /**
     * Gets the cause why the JID is malformed.
     *
     * @return The cause.
     */
    public final Throwable getCause() {
        return cause;
    }
}

D libs/xmpp-addr/src/main/java/rocks/xmpp/addr/package-info.java => libs/xmpp-addr/src/main/java/rocks/xmpp/addr/package-info.java +0 -31
@@ 1,31 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * Provides classes for the <a href="https://tools.ietf.org/html/rfc7622">XMPP Address Format</a> (JID).
 *
 * @see <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
 */
package rocks.xmpp.addr;


D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/DirectoryCache.java => libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/DirectoryCache.java +0 -192
@@ 1,192 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.util.cache;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * A simple directory based cache for caching of persistent items like avatars or entity capabilities.
 *
 * @author Christian Schudt
 */
public final class DirectoryCache implements Map<String, byte[]> {

    private final Path cacheDirectory;

    public DirectoryCache(Path cacheDirectory) {
        this.cacheDirectory = cacheDirectory;
    }

    @Override
    public final int size() {
        try (final Stream<Path> files = cacheContent()) {
            return (int) Math.min(files.count(), Integer.MAX_VALUE);
        }
    }

    @Override
    public final boolean isEmpty() {
        try (final Stream<Path> files = cacheContent()) {
            return files.findAny().map(file -> Boolean.FALSE).orElse(Boolean.TRUE);
        }
    }

    @Override
    public final boolean containsKey(Object key) {
        return Files.exists(cacheDirectory.resolve(key.toString()));
    }

    @Override
    public final boolean containsValue(Object value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public final byte[] get(final Object key) {
        return Optional.ofNullable(key).map(Object::toString).filter(((Predicate<String>) String::isEmpty).negate()).map(cacheDirectory::resolve).filter(Files::isReadable).map(file -> {
            try {
                return Files.readAllBytes(file);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }).orElse(null);
    }

    @Override
    public final byte[] put(String key, byte[] value) {
        // Make sure the directory exists.
        byte[] data = get(key);
        if (!Arrays.equals(data, value))
            try {
                if (Files.notExists(cacheDirectory)) {
                    Files.createDirectories(cacheDirectory);
                }
                Path file = cacheDirectory.resolve(key);
                Files.write(file, value);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        return data;
    }

    @Override
    public final byte[] remove(Object key) {
        byte[] data = get(key);
        try {
            Files.deleteIfExists(cacheDirectory.resolve(key.toString()));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        return data;
    }

    @Override
    public final void putAll(Map<? extends String, ? extends byte[]> m) {
        m.forEach(this::put);
    }

    @Override
    public final void clear() {
        try {
            Files.walkFileTree(cacheDirectory, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.deleteIfExists(file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    // Don't delete the cache directory itself.
                    if (!Files.isSameFile(dir, cacheDirectory)) {
                        Files.deleteIfExists(dir);
                    }
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public final Set<String> keySet() {
        try (final Stream<Path> files = Files.list(cacheDirectory)) {
            return Collections.unmodifiableSet(files.map(Path::getFileName).map(Path::toString).collect(Collectors.toSet()));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public final Collection<byte[]> values() {
        throw new UnsupportedOperationException();
    }

    @Override
    public final Set<Entry<String, byte[]>> entrySet() {
        throw new UnsupportedOperationException();
    }

    @Override
    public final void forEach(final BiConsumer<? super String, ? super byte[]> action) {
        if (Files.exists(cacheDirectory))
            try (final Stream<Path> files = cacheContent().filter(Files::isReadable)) {
                files.forEach(file -> {
                    try {
                        action.accept(file.getFileName().toString(), Files.readAllBytes(file));
                    } catch (final IOException e) {
                        throw new UncheckedIOException(e);
                    }
                });
            }
    }

    @SuppressWarnings("StreamResourceLeak")
    private final Stream<Path> cacheContent() {
        try {
            return Files.walk(cacheDirectory).filter(Files::isRegularFile);
        } catch (final IOException e) {
            throw new UncheckedIOException(e);
        }
    }

}

D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/LruCache.java => libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/LruCache.java +0 -228
@@ 1,228 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package rocks.xmpp.util.cache;

import java.util.Collection;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * A simple concurrent implementation of a least-recently-used cache.
 * <p>
 * This cache is keeps a maximal number of items in memory and removes the least-recently-used item, when new items are added.
 *
 * @param <K> The key.
 * @param <V> The value.
 * @author Christian Schudt
 * @see <a href="http://javadecodedquestions.blogspot.de/2013/02/java-cache-static-data-loading.html">http://javadecodedquestions.blogspot.de/2013/02/java-cache-static-data-loading.html</a>
 * @see <a href="http://stackoverflow.com/a/22891780">http://stackoverflow.com/a/22891780</a>
 */
public final class LruCache<K, V> implements Map<K, V> {
    private final int maxEntries;

    private final Map<K, V> map;

    final Queue<K> queue;

    public LruCache(final int maxEntries) {
        this.maxEntries = maxEntries;
        this.map = new ConcurrentHashMap<>(maxEntries);
        // Don't use a ConcurrentLinkedQueue here.
        // There's a JDK bug, leading to OutOfMemoryError and high CPU usage:
        // https://bugs.openjdk.java.net/browse/JDK-8054446
        this.queue = new ConcurrentLinkedDeque<>();
    }

    @Override
    public final int size() {
        return map.size();
    }

    @Override
    public final boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public final boolean containsKey(final Object key) {
        return map.containsKey(key);
    }

    @Override
    public final boolean containsValue(final Object value) {
        return map.containsValue(value);
    }

    @SuppressWarnings("unchecked")
    @Override
    public final V get(final Object key) {
        final V v = map.get(key);
        if (v != null) {
            // Remove the key from the queue and re-add it to the tail. It is now the most recently used key.
            keyUsed((K) key);
        }
        return v;
    }


    @Override
    public final V put(final K key, final V value) {
        V v = map.put(key, value);
        keyUsed(key);
        limit();
        return v;
    }

    @Override
    public final V remove(final Object key) {
        queue.remove(key);
        return map.remove(key);
    }


    @Override
    public final void putAll(final Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public final void clear() {
        queue.clear();
        map.clear();
    }

    @Override
    public final Set<K> keySet() {
        return map.keySet();
    }

    @Override
    public final Collection<V> values() {
        return map.values();
    }

    @Override
    public final Set<Entry<K, V>> entrySet() {
        return map.entrySet();
    }


    // Default methods

    @Override
    public final V putIfAbsent(final K key, final V value) {
        final V v = map.putIfAbsent(key, value);
        if (v == null) {
            keyUsed(key);
        }
        limit();
        return v;
    }

    @Override
    public final boolean remove(final Object key, final Object value) {
        final boolean removed = map.remove(key, value);
        if (removed) {
            queue.remove(key);
        }
        return removed;
    }

    @Override
    public final boolean replace(final K key, final V oldValue, final V newValue) {
        final boolean replaced = map.replace(key, oldValue, newValue);
        if (replaced) {
            keyUsed(key);
        }
        return replaced;
    }

    @Override
    public final V replace(final K key, final V value) {
        final V v = map.replace(key, value);
        if (v != null) {
            keyUsed(key);
        }
        return v;
    }

    @Override
    public final V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
        return map.computeIfAbsent(key, mappingFunction.<V>andThen(v -> {
            keyUsed(key);
            limit();
            return v;
        }));
    }

    @Override
    public final V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        return map.computeIfPresent(key, remappingFunction.<V>andThen(v -> {
            keyUsed(key);
            limit();
            return v;
        }));
    }

    @Override
    public final V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        return map.compute(key, remappingFunction.<V>andThen(v -> {
            keyUsed(key);
            limit();
            return v;
        }));
    }

    @Override
    public final V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        return map.merge(key, value, remappingFunction.<V>andThen(v -> {
            keyUsed(key);
            limit();
            return v;
        }));
    }

    private void limit() {
        while (queue.size() > maxEntries) {
            final K oldestKey = queue.poll();
            if (oldestKey != null) {
                map.remove(oldestKey);
            }
        }
    }

    private void keyUsed(final K key) {
        // remove it from the queue and re-add it, to make it the most recently used key.
        queue.remove(key);
        queue.offer(key);
    }
}
\ No newline at end of file

D libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/package-info.java => libs/xmpp-addr/src/main/java/rocks/xmpp/util/cache/package-info.java +0 -28
@@ 1,28 0,0 @@
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Christian Schudt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * Provides simple cache implementations.
 */
package rocks.xmpp.util.cache;
\ No newline at end of file

M src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java => src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java +1 -1
@@ 46,7 46,7 @@ import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.services.ExportBackupService.CIPHERMODE;
import static eu.siacs.conversations.services.ExportBackupService.KEYTYPE;

M src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java => src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java +1 -1
@@ 18,7 18,7 @@ import eu.siacs.conversations.databinding.MagicCreateBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.InstallReferrerUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MagicCreateActivity extends XmppActivity implements TextWatcher {


M src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java => src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java +1 -3
@@ 14,9 14,7 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;



@@ 34,7 32,7 @@ import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;

M src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java => src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java +1 -5
@@ 1,10 1,6 @@
package eu.siacs.conversations.ui;

import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

import java.util.ArrayList;


@@ 14,7 10,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ShareViaAccountActivity extends XmppActivity {
    public static final String EXTRA_CONTACT = "contact";

M src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java => src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java +1 -1
@@ 28,7 28,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.InstallReferrerUtils;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;

M src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java => src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java +1 -1
@@ 26,7 26,7 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.ImportBackupService;
import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.BackupFileViewHolder> {


M src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java => src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java +1 -1
@@ 13,7 13,7 @@ import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.PickServerActivity;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.ui.WelcomeActivity;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class SignupUtils {


M src/main/java/eu/siacs/conversations/Config.java => src/main/java/eu/siacs/conversations/Config.java +1 -1
@@ 6,7 6,7 @@ import java.util.Collections;
import java.util.List;

import eu.siacs.conversations.xmpp.chatstate.ChatState;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public final class Config {
    private static final int UNENCRYPTED = 1;

M src/main/java/eu/siacs/conversations/android/JabberIdContact.java => src/main/java/eu/siacs/conversations/android/JabberIdContact.java +1 -1
@@ 13,7 13,7 @@ import java.util.HashMap;
import java.util.Map;

import eu.siacs.conversations.Config;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JabberIdContact extends AbstractPhoneContact {


M src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java => src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +1 -1
@@ 53,7 53,7 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.pep.PublishOptions;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {


M src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java => src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +1 -2
@@ 2,7 2,6 @@ package eu.siacs.conversations.crypto.axolotl;

import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;


@@ 24,7 23,7 @@ import javax.crypto.spec.SecretKeySpec;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.xml.Element;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class XmppAxolotlMessage {
    public static final String CONTAINERTAG = "encrypted";

M src/main/java/eu/siacs/conversations/crypto/sasl/External.java => src/main/java/eu/siacs/conversations/crypto/sasl/External.java +1 -1
@@ 24,6 24,6 @@ public class External extends SaslMechanism {

	@Override
	public String getClientFirstMessage() {
		return Base64.encodeToString(account.getJid().asBareJid().toString().getBytes(),Base64.NO_WRAP);
		return Base64.encodeToString(account.getJid().asBareJid().toEscapedString().getBytes(),Base64.NO_WRAP);
	}
}

M src/main/java/eu/siacs/conversations/crypto/sasl/ScramMechanism.java => src/main/java/eu/siacs/conversations/crypto/sasl/ScramMechanism.java +1 -1
@@ 173,7 173,7 @@ abstract class ScramMechanism extends SaslMechanism {

                // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
                final KeyPair keys = CACHE.get(
                        CryptoHelper.bytesToHex(account.getJid().asBareJid().toString().getBytes()) + ","
                        CryptoHelper.bytesToHex(account.getJid().asBareJid().toEscapedString().getBytes()) + ","
                                + CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
                                + CryptoHelper.bytesToHex(salt.getBytes()) + ","
                                + String.valueOf(iterationCount) + ","

M src/main/java/eu/siacs/conversations/entities/Account.java => src/main/java/eu/siacs/conversations/entities/Account.java +1 -2
@@ 16,7 16,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

import eu.siacs.conversations.Config;


@@ 30,7 29,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Account extends AbstractEntity implements AvatarService.Avatarable {


M src/main/java/eu/siacs/conversations/entities/Blockable.java => src/main/java/eu/siacs/conversations/entities/Blockable.java +1 -1
@@ 1,6 1,6 @@
package eu.siacs.conversations.entities;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public interface Blockable {
	boolean isBlocked();

M src/main/java/eu/siacs/conversations/entities/Bookmark.java => src/main/java/eu/siacs/conversations/entities/Bookmark.java +1 -1
@@ 17,7 17,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Bookmark extends Element implements ListItem {


M src/main/java/eu/siacs/conversations/entities/Contact.java => src/main/java/eu/siacs/conversations/entities/Contact.java +2 -2
@@ 26,7 26,7 @@ import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Contact implements ListItem, Blockable {
	public static final String TABLENAME = "contacts";


@@ 396,7 396,7 @@ public class Contact implements ListItem, Blockable {

	public Element asElement() {
		final Element item = new Element("item");
		item.setAttribute("jid", this.jid.toString());
		item.setAttribute("jid", this.jid);
		if (this.serverName != null) {
			item.setAttribute("name", this.serverName);
		}

M src/main/java/eu/siacs/conversations/entities/Conversation.java => src/main/java/eu/siacs/conversations/entities/Conversation.java +1 -1
@@ 27,7 27,7 @@ import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.entities.Bookmark.printableValue;


M src/main/java/eu/siacs/conversations/entities/Conversational.java => src/main/java/eu/siacs/conversations/entities/Conversational.java +1 -1
@@ 29,7 29,7 @@

package eu.siacs.conversations.entities;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public interface Conversational {


M src/main/java/eu/siacs/conversations/entities/IndividualMessage.java => src/main/java/eu/siacs/conversations/entities/IndividualMessage.java +1 -1
@@ 34,7 34,7 @@ import android.database.Cursor;
import java.util.Set;

import eu.siacs.conversations.ui.adapter.MessageAdapter;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class IndividualMessage extends Message {


M src/main/java/eu/siacs/conversations/entities/ListItem.java => src/main/java/eu/siacs/conversations/entities/ListItem.java +1 -1
@@ 5,7 5,7 @@ import android.content.Context;
import java.util.List;

import eu.siacs.conversations.services.AvatarService;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;


public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable {

M src/main/java/eu/siacs/conversations/entities/Message.java => src/main/java/eu/siacs/conversations/entities/Message.java +1 -1
@@ 27,7 27,7 @@ import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Message extends AbstractEntity implements AvatarService.Avatarable  {


M src/main/java/eu/siacs/conversations/entities/MucOptions.java => src/main/java/eu/siacs/conversations/entities/MucOptions.java +1 -2
@@ 17,12 17,11 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MucOptions {


M src/main/java/eu/siacs/conversations/entities/RawBlockable.java => src/main/java/eu/siacs/conversations/entities/RawBlockable.java +1 -1
@@ 8,7 8,7 @@ import java.util.List;
import java.util.Locale;

import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class RawBlockable implements ListItem, Blockable {


M src/main/java/eu/siacs/conversations/entities/ReadByMarker.java => src/main/java/eu/siacs/conversations/entities/ReadByMarker.java +1 -2
@@ 6,10 6,9 @@ import org.json.JSONObject;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ReadByMarker {


M src/main/java/eu/siacs/conversations/entities/ReceiptRequest.java => src/main/java/eu/siacs/conversations/entities/ReceiptRequest.java +1 -1
@@ 29,7 29,7 @@

package eu.siacs.conversations.entities;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ReceiptRequest {


M src/main/java/eu/siacs/conversations/entities/Room.java => src/main/java/eu/siacs/conversations/entities/Room.java +1 -3
@@ 4,12 4,10 @@ import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;

import java.util.Comparator;

import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.utils.LanguageUtils;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Room implements AvatarService.Avatarable, Comparable<Room> {


M src/main/java/eu/siacs/conversations/entities/Roster.java => src/main/java/eu/siacs/conversations/entities/Roster.java +1 -1
@@ 6,7 6,7 @@ import java.util.Iterator;
import java.util.List;

import eu.siacs.conversations.android.AbstractPhoneContact;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;


public class Roster {

M src/main/java/eu/siacs/conversations/entities/StubConversation.java => src/main/java/eu/siacs/conversations/entities/StubConversation.java +1 -1
@@ 29,7 29,7 @@

package eu.siacs.conversations.entities;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;


public class StubConversation implements Conversational {

M src/main/java/eu/siacs/conversations/generator/IqGenerator.java => src/main/java/eu/siacs/conversations/generator/IqGenerator.java +2 -2
@@ 34,7 34,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class IqGenerator extends AbstractGenerator {



@@ 302,7 302,7 @@ public class IqGenerator extends AbstractGenerator {
        if (mam.muc()) {
            packet.setTo(mam.getWith());
        } else if (mam.getWith() != null) {
            data.put("with", mam.getWith().toString());
            data.put("with", mam.getWith().toEscapedString());
        }
        final long start = mam.getStart();
        final long end = mam.getEnd();

M src/main/java/eu/siacs/conversations/generator/MessageGenerator.java => src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +1 -1
@@ 23,7 23,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MessageGenerator extends AbstractGenerator {
    private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";

M src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java => src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +1 -1
@@ 32,7 32,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.FileWriterException;
import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class HttpDownloadConnection implements Transferable {


M src/main/java/eu/siacs/conversations/http/SlotRequester.java => src/main/java/eu/siacs/conversations/http/SlotRequester.java +1 -2
@@ 31,7 31,6 @@ package eu.siacs.conversations.http;

import android.util.Log;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;


@@ 44,7 43,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class SlotRequester {


M src/main/java/eu/siacs/conversations/parser/AbstractParser.java => src/main/java/eu/siacs/conversations/parser/AbstractParser.java +1 -1
@@ 16,7 16,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public abstract class AbstractParser {


M src/main/java/eu/siacs/conversations/parser/IqParser.java => src/main/java/eu/siacs/conversations/parser/IqParser.java +1 -1
@@ 39,7 39,7 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class IqParser extends AbstractParser implements OnIqPacketReceived {


M src/main/java/eu/siacs/conversations/parser/MessageParser.java => src/main/java/eu/siacs/conversations/parser/MessageParser.java +1 -1
@@ 48,7 48,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MessageParser extends AbstractParser implements OnMessagePacketReceived {


M src/main/java/eu/siacs/conversations/parser/PresenceParser.java => src/main/java/eu/siacs/conversations/parser/PresenceParser.java +1 -1
@@ 25,7 25,7 @@ import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class PresenceParser extends AbstractParser implements
		OnPresencePacketReceived {

M src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java => src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +1 -1
@@ 58,7 58,7 @@ import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class DatabaseBackend extends SQLiteOpenHelper {


M src/main/java/eu/siacs/conversations/services/AvatarService.java => src/main/java/eu/siacs/conversations/services/AvatarService.java +1 -2
@@ 39,11 39,10 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.RawBlockable;
import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class AvatarService implements OnAdvancedStreamFeaturesLoaded {


M src/main/java/eu/siacs/conversations/services/BarcodeProvider.java => src/main/java/eu/siacs/conversations/services/BarcodeProvider.java +1 -2
@@ 14,7 14,6 @@ import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.Log;

import com.google.zxing.BarcodeFormat;


@@ 32,7 31,7 @@ import java.util.Hashtable;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class BarcodeProvider extends ContentProvider implements ServiceConnection {


M src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java => src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java +1 -4
@@ 10,7 10,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;


@@ 23,8 22,6 @@ import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.utils.LanguageUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;


@@ 35,7 32,7 @@ import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ChannelDiscoveryService {


M src/main/java/eu/siacs/conversations/services/MessageArchiveService.java => src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +1 -2
@@ 15,13 15,12 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ReceiptRequest;
import eu.siacs.conversations.generator.AbstractGenerator;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {


M src/main/java/eu/siacs/conversations/services/MessageSearchTask.java => src/main/java/eu/siacs/conversations/services/MessageSearchTask.java +1 -2
@@ 34,7 34,6 @@ import android.os.SystemClock;
import android.util.Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;



@@ 49,7 48,7 @@ import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
import eu.siacs.conversations.utils.Cancellable;
import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MessageSearchTask implements Runnable, Cancellable {


M src/main/java/eu/siacs/conversations/services/ShortcutService.java => src/main/java/eu/siacs/conversations/services/ShortcutService.java +1 -1
@@ 20,7 20,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ShortcutService {


M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +2 -2
@@ 155,7 155,7 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import me.leolin.shortcutbadger.ShortcutBadger;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class XmppConnectionService extends Service {



@@ 3751,7 3751,7 @@ public class XmppConnectionService extends Service {
        if (account.getStatus() == Account.State.ONLINE) {
            IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
            Element item = iq.query(Namespace.ROSTER).addChild("item");
            item.setAttribute("jid", contact.getJid().toString());
            item.setAttribute("jid", contact.getJid());
            item.setAttribute("subscription", "remove");
            account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
        }

M src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java => src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java +1 -4
@@ 3,9 3,6 @@ package eu.siacs.conversations.ui;
import android.databinding.DataBindingUtil;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.TypefaceSpan;
import android.view.View;
import android.widget.Toast;



@@ 14,7 11,7 @@ import eu.siacs.conversations.databinding.DialogBlockContactBinding;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.util.JidDialog;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public final class BlockContactDialog {
	public static void show(final XmppActivity xmppActivity, final Blockable blockable) {

M src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java => src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java +1 -2
@@ 11,12 11,11 @@ import java.util.Collections;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.RawBlockable;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {


M src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java => src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +1 -1
@@ 38,7 38,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {


M src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java => src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java +0 -3
@@ 3,7 3,6 @@ package eu.siacs.conversations.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.widget.ListView;
import android.widget.Toast;



@@ 12,9 11,7 @@ import java.util.List;

import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import rocks.xmpp.addr.Jid;

public class ChooseAccountForProfilePictureActivity extends XmppActivity {


M src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java => src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java +1 -1
@@ 39,7 39,7 @@ import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.ActivityResult;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener, AdapterView.OnItemClickListener {
    public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";

M src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +1 -3
@@ 3,7 3,6 @@ package eu.siacs.conversations.ui;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;


@@ 23,7 22,6 @@ import java.util.concurrent.atomic.AtomicInteger;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;


@@ 51,7 49,7 @@ import eu.siacs.conversations.utils.StringUtils;
import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.XmppUri;
import me.drakeet.support.toast.ToastCompat;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.entities.Bookmark.printableValue;
import static eu.siacs.conversations.utils.StringUtils.changed;

M src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +1 -1
@@ 60,7 60,7 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnMediaLoaded {
    public static final String ACTION_VIEW_CONTACT = "view_contact";

M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +1 -1
@@ 123,7 123,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.ui.XmppActivity.EXTRA_ACCOUNT;
import static eu.siacs.conversations.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION;

M src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java => src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +1 -1
@@ 81,7 81,7 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;


M src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java => src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java +1 -3
@@ 15,7 15,6 @@ import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;

import java.security.SecureRandom;
import java.util.ArrayList;


@@ 23,7 22,6 @@ import java.util.Collection;
import java.util.List;

import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.CreateConferenceDialogBinding;
import eu.siacs.conversations.databinding.CreatePublicChannelDialogBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;


@@ 32,7 30,7 @@ import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class CreatePublicChannelDialog extends DialogFragment implements OnBackendConnected {


M src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java => src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +1 -1
@@ 75,7 75,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.XmppConnection.Features;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist,
        OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {

M src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java => src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java +1 -1
@@ 24,7 24,7 @@ import eu.siacs.conversations.databinding.EnterJidDialogBinding;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {


M src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java => src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java +1 -1
@@ 18,7 18,7 @@ import eu.siacs.conversations.ui.adapter.MediaAdapter;
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.GridManager;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded {


M src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java => src/main/java/eu/siacs/conversations/ui/MucUsersActivity.java +1 -1
@@ 27,7 27,7 @@ import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.UserAdapter;
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MucUsersActivity extends XmppActivity implements XmppConnectionService.OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, MenuItem.OnActionExpandListener, TextWatcher {


M src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java => src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +1 -1
@@ 54,7 54,7 @@ import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
import static java.util.Arrays.asList;

M src/main/java/eu/siacs/conversations/ui/SettingsActivity.java => src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +1 -1
@@ 41,7 41,7 @@ import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class SettingsActivity extends XmppActivity implements
		OnSharedPreferenceChangeListener {

M src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java => src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +1 -4
@@ 12,7 12,6 @@ import android.view.MenuItem;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import eu.siacs.conversations.Config;


@@ 21,9 20,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.ui.service.EmojiService;
import eu.siacs.conversations.utils.GeoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {


M src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java => src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +1 -1
@@ 79,7 79,7 @@ import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreatePrivateGroupChatDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener, SwipeRefreshLayout.OnRefreshListener, CreatePublicChannelDialog.CreatePublicChannelDialogListener {


M src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java => src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +1 -1
@@ 38,7 38,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;


public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {

M src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java => src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java +1 -1
@@ 19,7 19,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class UriHandlerActivity extends AppCompatActivity {


M src/main/java/eu/siacs/conversations/ui/XmppActivity.java => src/main/java/eu/siacs/conversations/ui/XmppActivity.java +1 -1
@@ 79,7 79,7 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public abstract class XmppActivity extends ActionBarActivity {


M src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java +1 -1
@@ 19,7 19,7 @@ import eu.siacs.conversations.databinding.SearchResultItemBinding;
import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchResultAdapter.ViewHolder> implements View.OnCreateContextMenuListener {


M src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +1 -2
@@ 25,9 25,8 @@ import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {


M src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java +1 -10
@@ 1,12 1,7 @@
package eu.siacs.conversations.ui.adapter;

import android.content.SharedPreferences;
import android.content.res.Resources;
import android.databinding.DataBindingUtil;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;


@@ 17,9 12,7 @@ import android.widget.TextView;

import com.wefika.flowlayout.FlowLayout;

import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;

import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ContactBinding;


@@ 30,9 23,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ListItemAdapter extends ArrayAdapter<ListItem> {


M src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +1 -1
@@ 76,7 76,7 @@ import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {


M src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java +1 -1
@@ 24,7 24,7 @@ import eu.siacs.conversations.ui.ConferenceDetailsActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHolder> implements View.OnCreateContextMenuListener {


M src/main/java/eu/siacs/conversations/ui/forms/FormJidSingleFieldWrapper.java => src/main/java/eu/siacs/conversations/ui/forms/FormJidSingleFieldWrapper.java +1 -1
@@ 7,7 7,7 @@ import java.util.List;

import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Field;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class FormJidSingleFieldWrapper extends FormTextFieldWrapper {


M src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java => src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java +1 -1
@@ 24,7 24,7 @@ import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.MucUsersActivity;
import eu.siacs.conversations.ui.XmppActivity;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;


public final class MucDetailsContextMenuHelper {

M src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java => src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java +1 -1
@@ 43,7 43,7 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class PresenceSelector {


M src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java => src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java +1 -1
@@ 43,7 43,7 @@ import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.utils.Patterns;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class ShareUtil {


M src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java => src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java +1 -2
@@ 3,9 3,8 @@ package eu.siacs.conversations.utils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class BackupFileHeader {


M src/main/java/eu/siacs/conversations/utils/CryptoHelper.java => src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +1 -2
@@ 24,7 24,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import eu.siacs.conversations.Config;


@@ 32,7 31,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.http.AesGcmURLStreamHandler;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public final class CryptoHelper {


M src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java => src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java +1 -1
@@ 53,7 53,7 @@ import java.util.regex.Pattern;

import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.util.StyledAttributes;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class IrregularUnicodeDetector {


M src/main/java/eu/siacs/conversations/utils/JidHelper.java => src/main/java/eu/siacs/conversations/utils/JidHelper.java +1 -1
@@ 35,7 35,7 @@ import java.util.List;
import java.util.Locale;

import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JidHelper {


M src/main/java/eu/siacs/conversations/utils/NickValidityChecker.java => src/main/java/eu/siacs/conversations/utils/NickValidityChecker.java +1 -1
@@ 5,7 5,7 @@ import java.util.List;
import java.util.Set;

import eu.siacs.conversations.entities.Conversation;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class NickValidityChecker {


M src/main/java/eu/siacs/conversations/utils/UIHelper.java => src/main/java/eu/siacs/conversations/utils/UIHelper.java +1 -7
@@ 2,19 2,13 @@ package eu.siacs.conversations.utils;

import android.content.Context;
import android.support.annotation.ColorInt;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
import android.widget.PopupMenu;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;


@@ 34,7 28,7 @@ import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.RtpSessionStatus;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.services.ExportBackupService;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class UIHelper {


M src/main/java/eu/siacs/conversations/utils/XmppUri.java => src/main/java/eu/siacs/conversations/utils/XmppUri.java +1 -1
@@ 14,7 14,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class XmppUri {


M src/main/java/eu/siacs/conversations/xml/Element.java => src/main/java/eu/siacs/conversations/xml/Element.java +8 -3
@@ 1,15 1,13 @@
package eu.siacs.conversations.xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;

import eu.siacs.conversations.utils.XmlHelper;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Element {
	private final String name;


@@ 126,6 124,13 @@ public class Element {
		return this;
	}

	public Element setAttribute(String name, Jid value) {
		if (name != null && value != null) {
			this.attributes.put(name, value.toEscapedString());
		}
		return this;
	}

	public Element removeAttribute(String name) {
		this.attributes.remove(name);
		return this;

M src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java => src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java +1 -11
@@ 32,7 32,6 @@ package eu.siacs.conversations.xmpp;
import android.support.annotation.NonNull;

import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
import rocks.xmpp.addr.Jid;

public class InvalidJid implements Jid {



@@ 78,10 77,6 @@ public class InvalidJid implements Jid {
		throw new AssertionError("Not implemented");
	}

	@Override
	public Jid withLocal(CharSequence charSequence) {
		throw new AssertionError("Not implemented");
	}

	@Override
	public Jid withResource(CharSequence charSequence) {


@@ 89,11 84,6 @@ public class InvalidJid implements Jid {
	}

	@Override
	public Jid atSubdomain(CharSequence charSequence) {
		throw new AssertionError("Not implemented");
	}

	@Override
	public String getLocal() {
		throw new AssertionError("Not implemented");
	}


@@ 139,7 129,7 @@ public class InvalidJid implements Jid {
	}

	public static Jid getNullForInvalid(Jid jid) {
		if (jid != null && jid instanceof InvalidJid) {
		if (jid instanceof InvalidJid) {
			return null;
		} else {
			return jid;

A src/main/java/eu/siacs/conversations/xmpp/Jid.java => src/main/java/eu/siacs/conversations/xmpp/Jid.java +224 -0
@@ 0,0 1,224 @@
package eu.siacs.conversations.xmpp;

import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Domainpart;
import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;

import java.io.Serializable;

public interface Jid extends Comparable<Jid>, Serializable, CharSequence {

    static Jid of(CharSequence local, CharSequence domain, CharSequence resource) {
        if (resource == null) {
            return ofLocalAndDomain(local, domain);
        }
        try {
            return new WrappedJid(JidCreate.entityFullFrom(
                    Localpart.fromUnescaped(local.toString()),
                    Domainpart.from(domain.toString()),
                    Resourcepart.from(resource.toString())
            ));
        } catch (XmppStringprepException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Creates a bare JID with only the domain part, e.g. <code>capulet.com</code>
     *
     * @param domain The domain.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain or local part are not valid.
     */
    static Jid ofDomain(CharSequence domain) {
        try {
            return new WrappedJid(JidCreate.domainBareFrom(domain));
        } catch (XmppStringprepException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Creates a bare JID with a local and domain part, e.g. <code>juliet@capulet.com</code>
     *
     * @param local  The local part.
     * @param domain The domain.
     * @return The JID.
     * @throws NullPointerException     If the domain is null.
     * @throws IllegalArgumentException If the domain or local part are not valid.
     */
    static Jid ofLocalAndDomain(CharSequence local, CharSequence domain) {
        try {
            return new WrappedJid(
                    JidCreate.bareFrom(
                            Localpart.fromUnescaped(local.toString()),
                            Domainpart.from(domain.toString())
                    )
            );
        } catch (XmppStringprepException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Creates a JID from an unescaped string. The format must be
     * <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
     * The input string will be escaped.
     *
     * @param jid The JID.
     * @return The JID.
     * @throws NullPointerException     If the jid is null.
     * @throws IllegalArgumentException If the jid could not be parsed or is not valid.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    static Jid of(CharSequence jid) {
        if (jid instanceof Jid) {
            return (Jid) jid;
        }
        try {
            return new WrappedJid(JidCreate.fromUnescaped(jid));
        } catch (XmppStringprepException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Creates a JID from a escaped JID string. The format must be
     * <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
     * This method should be used, when parsing JIDs from the XMPP stream.
     *
     * @param jid The JID.
     * @return The JID.
     * @throws NullPointerException     If the jid is null.
     * @throws IllegalArgumentException If the jid could not be parsed or is not valid.
     * @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
     */
    static Jid ofEscaped(CharSequence jid) {
        try {
            return new WrappedJid(JidCreate.from(jid));
        } catch (XmppStringprepException e) {
            e.printStackTrace();
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Checks if the JID is a full JID.
     * <blockquote>
     * <p>The term "full JID" refers to an XMPP address of the form &lt;localpart@domainpart/resourcepart&gt; (for a particular authorized client or device associated with an account) or of the form &lt;domainpart/resourcepart&gt; (for a particular resource or script associated with a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a full JID; otherwise false.
     */
    boolean isFullJid();

    /**
     * Checks if the JID is a bare JID.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return True, if the JID is a bare JID; otherwise false.
     */
    boolean isBareJid();

    /**
     * Checks if the JID is a domain JID, i.e. if it has no local part.
     *
     * @return True, if the JID is a domain JID, i.e. if it has no local part.
     */
    boolean isDomainJid();

    /**
     * Gets the bare JID representation of this JID, i.e. removes the resource part.
     * <blockquote>
     * <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
     * </blockquote>
     *
     * @return The bare JID.
     * @see #withResource(CharSequence)
     */
    Jid asBareJid();

    /**
     * Creates a new full JID with a resource and the same local and domain part of the current JID.
     *
     * @param resource The resource.
     * @return The full JID with a resource.
     * @throws IllegalArgumentException If the resource is not a valid resource part.
     * @see #asBareJid()
     */
    Jid withResource(CharSequence resource);


    /**
     * Gets the local part of the JID, also known as the name or node.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.3">3.3.  Localpart</a></cite></p>
     * <p>The localpart of a JID is an optional identifier placed before the
     * domainpart and separated from the latter by the '@' character.
     * Typically, a localpart uniquely identifies the entity requesting and
     * using network access provided by a server (i.e., a local account),
     * although it can also represent other kinds of entities (e.g., a
     * chatroom associated with a multi-user chat service [XEP-0045]).  The
     * entity represented by an XMPP localpart is addressed within the
     * context of a specific domain (i.e., &lt;localpart@domainpart&gt;).</p>
     * </blockquote>
     *
     * @return The local part or null.
     * @see #getEscapedLocal()
     */
    String getLocal();

    /**
     * Gets the escaped local part of the JID.
     *
     * @return The escaped local part or null.
     * @see #getLocal()
     * @since 0.8.0
     */
    String getEscapedLocal();

    /**
     * Gets the domain part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.2">3.2.  Domainpart</a></cite></p>
     * <p>The domainpart is the primary identifier and is the only REQUIRED
     * element of a JID (a mere domainpart is a valid JID).  Typically,
     * a domainpart identifies the "home" server to which clients connect
     * for XML routing and data management functionality.</p>
     * </blockquote>
     *
     * @return The domain part.
     */
    String getDomain();

    /**
     * Gets the resource part.
     * <blockquote>
     * <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.4">3.4.  Resourcepart</a></cite></p>
     * <p>The resourcepart of a JID is an optional identifier placed after the
     * domainpart and separated from the latter by the '/' character.  A
     * resourcepart can modify either a &lt;localpart@domainpart&gt; address or a
     * mere &lt;domainpart&gt; address.  Typically, a resourcepart uniquely
     * identifies a specific connection (e.g., a device or location) or
     * object (e.g., an occupant in a multi-user chatroom [XEP-0045])
     * belonging to the entity associated with an XMPP localpart at a domain
     * (i.e., &lt;localpart@domainpart/resourcepart&gt;).</p>
     * </blockquote>
     *
     * @return The resource part or null.
     */
    String getResource();

    /**
     * Returns the JID in escaped form as described in <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>.
     *
     * @return The escaped JID.
     * @see #toString()
     */
    String toEscapedString();
}

A src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java => src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java +126 -0
@@ 0,0 1,126 @@
package eu.siacs.conversations.xmpp;


import android.support.annotation.NonNull;

import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Domainpart;
import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;


public class WrappedJid implements eu.siacs.conversations.xmpp.Jid {
    private final Jid inner;

    WrappedJid(Jid inner) {
        this.inner = inner;
    }

    @Override
    public boolean isFullJid() {
        return inner.isEntityFullJid() || inner.isDomainFullJid();
    }

    @Override
    public boolean isBareJid() {
        return inner.isDomainBareJid() || inner.isEntityBareJid();
    }

    @Override
    public boolean isDomainJid() {
        return inner.isDomainBareJid() || inner.isDomainFullJid();
    }

    @Override
    public eu.siacs.conversations.xmpp.Jid asBareJid() {
        return new WrappedJid(inner.asBareJid());
    }

    @Override
    public eu.siacs.conversations.xmpp.Jid withResource(CharSequence resource) {
        try {
            return new WrappedJid(
                    JidCreate.fullFrom(
                            inner.getLocalpartOrThrow(),
                            inner.getDomain(),
                            Resourcepart.from(resource.toString())
                    ));
        } catch (XmppStringprepException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public String getLocal() {
        final Localpart localpart = inner.getLocalpartOrNull();
        return localpart == null ? null : localpart.asUnescapedString();
    }

    @Override
    public String getEscapedLocal() {
        final Localpart localpart = inner.getLocalpartOrNull();
        return localpart == null ? null : localpart.toString();
    }

    @Override
    public String getDomain() {
        final Domainpart domainpart = inner.getDomain();
        return domainpart == null ? null : domainpart.toString();
    }

    @Override
    public String getResource() {
        final Resourcepart resourcepart = inner.getResourceOrNull();
        return resourcepart == null ? null : resourcepart.toString();
    }

    @Override
    public String toEscapedString() {
        return inner.toString();
    }

    @NonNull
    @Override
    public String toString() {
        return inner.asUnescapedString();
    }

    @Override
    public int length() {
        return inner.length();
    }

    @Override
    public char charAt(int i) {
        return inner.charAt(i);
    }

    @Override
    public CharSequence subSequence(int i, int i1) {
        return inner.subSequence(i,i1);
    }

    @Override
    public int compareTo(eu.siacs.conversations.xmpp.Jid jid) {
        if (jid instanceof WrappedJid) {
            return inner.compareTo(((WrappedJid) jid).inner);
        } else {
            return 0;
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WrappedJid that = (WrappedJid) o;
        return inner.equals(that.inner);
    }

    @Override
    public int hashCode() {
        return inner.hashCode();
    }
}

M src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java => src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +0 -1
@@ 100,7 100,6 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
import rocks.xmpp.addr.Jid;

public class XmppConnection implements Runnable {


M src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java => src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java +1 -2
@@ 1,7 1,6 @@
package eu.siacs.conversations.xmpp.jingle;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;

import eu.siacs.conversations.entities.Account;


@@ 9,7 8,7 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public abstract class AbstractJingleConnection {


M src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java => src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java +1 -1
@@ 11,7 11,7 @@ import java.util.Enumeration;
import java.util.List;
import java.util.UUID;

import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class DirectConnectionUtils {


M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java +2 -2
@@ 5,7 5,7 @@ import java.util.List;

import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JingleCandidate {



@@ 127,7 127,7 @@ public class JingleCandidate {
		element.setAttribute("host", this.getHost());
		element.setAttribute("port", Integer.toString(this.getPort()));
		if (jid != null) {
			element.setAttribute("jid", jid.toEscapedString());
			element.setAttribute("jid", jid);
		}
		element.setAttribute("priority", Integer.toString(this.getPriority()));
		if (this.getType() == TYPE_DIRECT) {

M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +1 -1
@@ 48,7 48,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JingleConnectionManager extends AbstractConnectionManager {
    static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();

M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java +1 -1
@@ 47,7 47,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.S5BTransportInfo;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JingleFileTransferConnection extends AbstractJingleConnection implements Transferable {


M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java +1 -1
@@ 20,7 20,7 @@ import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JingleInBandTransport extends JingleTransport {


M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +1 -1
@@ 47,7 47,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback {


M src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java => src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java +1 -3
@@ 1,9 1,7 @@
package eu.siacs.conversations.xmpp.jingle;

import com.google.common.base.Optional;

import eu.siacs.conversations.entities.Account;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public interface OngoingRtpSession {
    Account getAccount();

M src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java => src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +3 -3
@@ 12,7 12,7 @@ import java.util.Map;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class JinglePacket extends IqPacket {



@@ 96,13 96,13 @@ public class JinglePacket extends IqPacket {
    //RECOMMENDED for session-initiate, NOT RECOMMENDED otherwise
    public void setInitiator(final Jid initiator) {
        Preconditions.checkArgument(initiator.isFullJid(), "initiator should be a full JID");
        findChild("jingle", Namespace.JINGLE).setAttribute("initiator", initiator.toEscapedString());
        findChild("jingle", Namespace.JINGLE).setAttribute("initiator", initiator);
    }

    //RECOMMENDED for session-accept, NOT RECOMMENDED otherwise
    public void setResponder(Jid responder) {
        Preconditions.checkArgument(responder.isFullJid(), "responder should be a full JID");
        findChild("jingle", Namespace.JINGLE).setAttribute("responder", responder.toEscapedString());
        findChild("jingle", Namespace.JINGLE).setAttribute("responder", responder);
    }

    public Element getJingleChild(final String name) {

M src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java => src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java +1 -1
@@ 3,7 3,7 @@ package eu.siacs.conversations.xmpp.pep;
import android.util.Base64;

import eu.siacs.conversations.xml.Element;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class Avatar {


M src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java => src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java +0 -1
@@ 2,7 2,6 @@ package eu.siacs.conversations.xmpp.stanzas;

import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;

abstract public class AbstractAcknowledgeableStanza extends AbstractStanza {


M src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java => src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +3 -3
@@ 2,7 2,7 @@ package eu.siacs.conversations.xmpp.stanzas;

import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xml.Element;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;

public class AbstractStanza extends Element {



@@ 20,13 20,13 @@ public class AbstractStanza extends Element {

	public void setTo(final Jid to) {
		if (to != null) {
			setAttribute("to", to.toEscapedString());
			setAttribute("to", to);
		}
	}

	public void setFrom(final Jid from) {
		if (from != null) {
			setAttribute("from", from.toEscapedString());
			setAttribute("from", from);
		}
	}