Difian/DifianApp/src/main/java/com/sonova/difian/utilities/Contract.java
public final class Contract
{
    private Contract()
    {
    }

    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
    public static void check(boolean condition)
    {
        if (!condition)
        {
            throw new AssertionError();
        }
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/UserInfoRequestMessage.java
public final class UserInfoRequestMessage extends Message
{
    @Override
    public MessageType getType()
    {
        return MessageType.USER_INFO_REQUEST;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageType.java
public enum MessageType
{
    USER_INFO_REQUEST, USER_INFO, SMART, SMART_REPLY, ALERT, ALERT_REPLY, FD_STATUS, HI_STATUS, TYPING
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/YalerSSLServerSocket.java
public final class YalerSSLServerSocket extends YalerServerSocket {
	public YalerSSLServerSocket(String host, int port, String id) {
		super(host, port, id);
	}

	public YalerSSLServerSocket(String host, int port, String id, Proxy proxy) {
		super(host, port, id, proxy);
	}

	@Override
	public YalerSocketFactory getYalerSocketFactory() {
		return YalerSSLSocketFactory.getInstance();
	}

	@Override
	public InetSocketAddress findLocation(InputStream stream) throws IOException {
		return StreamHelper.findLocation(stream, "https", 443);
	}
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/UserInfoMessage.java
public final class UserInfoMessage extends Message
{
    private String _name;
    private String _lang;
    private String _text;
    private String _image;

    UserInfoMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, null, XmlConstants.USER_NAME);
        setName(parser.nextText());
        parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_NAME);
        while (parser.nextTag() == XmlPullParser.START_TAG)
        {
            if (XmlConstants.USER_LANG.equals(parser.getName()))
            {
                setLang(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_LANG);
            }
            else if (XmlConstants.USER_TEXT.equals(parser.getName()))
            {
                setText(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_TEXT);
            }
            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
            {
                setImage(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
            }
            else
            {
                throw new MessageReaderException("Invalid tag in \"user_info\" <message>.");
            }
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.USER_INFO;
    }

    public String getName()
    {
        return _name;
    }

    private void setName(String name)
    {
        _name = name;
    }

    @SuppressWarnings("WeakerAccess")
    public String getLang()
    {
        return _lang;
    }

    private void setLang(String lang)
    {
        _lang = lang;
    }

    @SuppressWarnings("WeakerAccess")
    public String getText()
    {
        return _text;
    }

    private void setText(String text)
    {
        _text = text;
    }

    public String getImage()
    {
        return _image;
    }

    private void setImage(String image)
    {
        _image = image;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/SmartMessage.java
public final class SmartMessage extends Message
{
    private String _text;
    private String _image;
    private SmartMessageOptionsType _optionsType;
    private final List<SmartMessageOption> _options = new ArrayList<SmartMessageOption>();

    SmartMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        while (parser.nextTag() == XmlPullParser.START_TAG)
        {
            if ("text".equals(parser.getName()))
            {
                setText(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, "text");
            }
            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
            {
                setImage(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
            }
            else if (XmlConstants.OPTIONS.equals(parser.getName()))
            {
                String optionsType = parser.getAttributeValue(null, "type");
                if ("button".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.BUTTON);
                }
                else if ("radio".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.RADIO);
                }
                else if ("checkbox".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.CHECKBOX);
                }
                else if ("text".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.TEXT);
                }
                else
                {
                    throw new MessageReaderException("Invalid type in \"smart\" <message> \"options\".");
                }

                while (parser.nextTag() == XmlPullParser.START_TAG)
                {
                    if (XmlConstants.OPTION.equals(parser.getName()))
                    {
                        String optionId = parser.getAttributeValue(null, "id");
                        String optionText = null;
                        String optionImage = null;
                        if ((optionId == null) || !optionId.matches("^[a-z0-9]+$"))
                        {
                            throw new MessageReaderException(String.format("Invalid option id: %s.", optionId));
                        }
                        while (parser.nextTag() == XmlPullParser.START_TAG)
                        {
                            if ("text".equals(parser.getName()))
                            {
                                optionText = parser.nextText();
                                parser.require(XmlPullParser.END_TAG, null, "text");
                            }
                            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
                            {
                                optionImage = parser.nextText();
                                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
                            }
                            else
                            {
                                throw new MessageReaderException("Invalid tag in \"smart\" <message> \"option\".");
                            }
                        }
                        if (optionText == null)
                        {
                            throw new MessageReaderException("No option text in \"smart\" <message>.");
                        }
                        addOption(new SmartMessageOption(optionId, optionText, optionImage));
                        parser.require(XmlPullParser.END_TAG, null, XmlConstants.OPTION);
                    }
                    else
                    {
                        throw new MessageReaderException("Invalid tag in \"smart\" <message> \"options\".");
                    }
                }
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.OPTIONS);
            }
            else
            {
                throw new MessageReaderException("Invalid tag in \"smart\" <message>.");
            }
        }
        if (getText() == null)
        {
            throw new MessageReaderException("No text in \"smart\" <message>.");
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.SMART;
    }

    @SuppressWarnings("WeakerAccess")
    public String getText()
    {
        return _text;
    }

    private void setText(String text)
    {
        _text = text;
    }

    public String getImage()
    {
        return _image;
    }

    private void setImage(String image)
    {
        _image = image;
    }

    public SmartMessageOptionsType getOptionsType()
    {
        return _optionsType;
    }

    private void setOptionsType(SmartMessageOptionsType optionsType)
    {
        _optionsType = optionsType;
    }

    public int getOptionsCount()
    {
        return _options.size();
    }

    public SmartMessageOption getOption(int id)
    {
        return _options.get(id);
    }

    private void addOption(SmartMessageOption option)
    {
        _options.add(option);
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/Message.java
public abstract class Message
{
    private String _guid;

    public abstract MessageType getType();

    public final void setGuid(String guid)
    {
        _guid = guid;
    }

    public final String getGuid()
    {
        return _guid;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/SmartReplyMessage.java
public final class SmartReplyMessage extends Message
{
    private final String _reText;

    private final SmartMessage _reMessage;
    private final List<SmartMessageOption> _selection = new ArrayList<SmartMessageOption>();

    private final ChatMessage _message;

    public SmartReplyMessage(ChatMessage message)
    {
        Contract.check(message != null);
        _message = message;

        if (message.getMessage() == null)
        {
            _reText = message.getText();
            _reMessage = null;
        }
        else
        {
            _reText = null;
            _reMessage = message.getMessage();
            for (SmartMessageOption o : message.getSelection())
            {
                addSelection(o);
            }
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.SMART_REPLY;
    }

    @SuppressWarnings("WeakerAccess")
    public void addSelection(SmartMessageOption selection)
    {
        Contract.check(_reMessage != null);

        _selection.add(selection);
    }

    public String getReText()
    {
        return _reText;
    }

    public SmartMessage getReMessage()
    {
        return _reMessage;
    }

    public int getSelectionCount()
    {
        return _selection.size();
    }

    public SmartMessageOption getSelection(int id)
    {
        return _selection.get(id);
    }

    public void ack()
    {
        _message.ack();
    }

    public void retry()
    {
        _message.retry();
    }

    public boolean isFailed()
    {
        return _message.isFailed();
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/utilities/SocketHelpers.java
public final class SocketHelpers
{
    private SocketHelpers()
    {
    }

    public static Socket createSocket(SocketFactory factory, int soTimeout)
    {
        Socket result = null;
        try
        {
            result = factory.createSocket();
        }
        catch (IOException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while creating socket.", e);
            throw new RuntimeException(e);
        }

        try
        {
            result.setSoTimeout(soTimeout);
        }
        catch (SocketException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while setting socket timeout.", e);
            throw new RuntimeException(e);
        }

        try
        {
            result.setTcpNoDelay(true);
        }
        catch (SocketException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while setting TcpNoDelay option.", e);
            throw new RuntimeException(e);
        }

        return result;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiStatusMessage.java
public final class HiStatusMessage extends Message
{
    private final HiConnectionStatus[] _status = new HiConnectionStatus[2];
    private final HiMuteStatus[] _muteStatus = new HiMuteStatus[2];

    HiStatusMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        for (int i = 0; i < 2; i++)
        {
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, XmlConstants.HI);

            String sideString = parser.getAttributeValue(null, XmlConstants.SIDE);
            HiSide side;
            if (XmlConstants.LEFT.equals(sideString))
            {
                side = HiSide.LEFT;
            }
            else if (XmlConstants.RIGHT.equals(sideString))
            {
                side = HiSide.RIGHT;
            }
            else
            {
                throw new MessageReaderException(String.format("Invalid side in \"hi_status\" <message>: \"%s\".", sideString));
            }

            String statusString = parser.getAttributeValue(null, XmlConstants.STATUS);
            HiConnectionStatus status;
            if (XmlConstants.CONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.CONNECTED;
            }
            else if (XmlConstants.DISCONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.DISCONNECTED;
            }
            else if (XmlConstants.NOT_CONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.NOT_CONNECTED;
            }
            else if (XmlConstants.UNDEFINED.equals(statusString))
            {
                status = HiConnectionStatus.UNDEFINED;
            }
            else
            {
                throw new MessageReaderException("Invalid status in \"hi_status\" <message>.");
            }

            String muteStatusString = parser.getAttributeValue(null, XmlConstants.MUTE_STATUS);
            HiMuteStatus muteStatus;
            if (XmlConstants.MUTED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.MUTED;
            }
            else if (XmlConstants.UNMUTED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.UNMUTED;
            }
            else if (XmlConstants.UNDEFINED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.UNDEFINED;
            }
            else
            {
                throw new MessageReaderException("Invalid mute_status in \"hi_status\" <message>.");
            }

            setStatus(side, status);
            setMuteStatus(side, muteStatus);

            parser.nextTag();
            parser.require(XmlPullParser.END_TAG, null, "hi");
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.HI_STATUS;
    }

    public HiConnectionStatus getStatus(HiSide side)
    {
        HiConnectionStatus result = _status[side.ordinal()];
        return (result != null) ? result : HiConnectionStatus.UNDEFINED;
    }

    private void setStatus(HiSide side, HiConnectionStatus status)
    {
        _status[side.ordinal()] = status;
    }

    public HiMuteStatus getMuteStatus(HiSide side)
    {
        HiMuteStatus result = _muteStatus[side.ordinal()];
        return (result != null) ? result : HiMuteStatus.UNDEFINED;
    }

    private void setMuteStatus(HiSide side, HiMuteStatus muteStatus)
    {
        _muteStatus[side.ordinal()] = muteStatus;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageReader.java
public final class MessageReader
{
    private static final String TAG = MessageReader.class.getName();

    private static final MessageReader _instance = new MessageReader();

    public static MessageReader getInstance()
    {
        return _instance;
    }

    private MessageReader()
    {
    }

    public Message parse(InputStream stream) throws XmlPullParserException, IOException, MessageReaderException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in parse");
        }

        Message result;

        XmlPullParser parser = Xml.newPullParser();
        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
        parser.setInput(stream, null);
        parser.nextTag();

        parser.require(XmlPullParser.START_TAG, null, XmlConstants.MESSAGE);
        String v = parser.getAttributeValue(null, "v");
        if ((v == null) || !"3".equals(v))
        {
            if ((v != null) && "0".equals(v))
            {
                // ignore, treat like v = 3
                Log.w(TAG, "Received <message> with \"v\" = \"0\".");
            }
            else
            {
                throw new MessageReaderException(String.format("Invalid format at <message> attribute \"v\" : %s.", v));
            }
        }
        String guid = parser.getAttributeValue(null, "guid");
        String type = parser.getAttributeValue(null, "type");
        if (type == null)
        {
            throw new MessageReaderException("\"type\" attribute does not exist in <message>.");
        }
        if ("user_info".equals(type))
        {
            result = new UserInfoMessage(parser);
        }
        else if ("hi_status".equals(type))
        {
            result = new HiStatusMessage(parser);
        }
        else if ("smart".equals(type))
        {
            result = new SmartMessage(parser);
        }
        else if ("typing".equals(type))
        {
            result = new TypingMessage(parser);
        }
        else
        {
            Log.w(TAG, String.format("Skipped message of type %s", type));
            throw new MessageReaderException(String.format("Unsupported message type while reading: %s", type));
        }
        result.setGuid(guid);

        return result;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageReaderException.java
@SuppressWarnings("SerializableHasSerializationMethods")
public final class MessageReaderException extends Exception
{
    private static final long serialVersionUID = 1L;

    MessageReaderException()
    {
    }

    MessageReaderException(String message)
    {
        super(message);
    }

    MessageReaderException(String message, Throwable cause)
    {
        super(message, cause);
    }

    MessageReaderException(Throwable cause)
    {
        super(cause);
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/StreamHelpers.java
public final class StreamHelpers
{
    private static final String TAG = StreamHelpers.class.getName();

    private StreamHelpers()
    {
    }

    public static String readLine(InputStream stream) throws IOException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in readLine");
        }
        StringBuilder line = new StringBuilder();
        int b;
        do
        {
            b = stream.read();
            if (b != -1)
            {
                if (line.length() < 4096)
                {
                    line.append((char)b);
                } else {
                    throw new IOException("line too long", null);
                }
            }
            else
            {
                throw new IOException("unexpected end of stream", null);
            }
        } while (b != '\n');
        String result;
        if ((line.length() >= 2) && (line.charAt(line.length() - 2) == '\r')) {
            result = line.substring(0, line.length() - 2).trim();
        } else {
            result = line.substring(0, line.length() - 1).trim();
        }
        return result;
    }

    public static boolean consumeHttpHeaders (InputStream stream) throws IOException
    {
        boolean continueExpected = false;
        String line = readLine(stream);
        while (!line.equals(""))
        {
            Log.v(TAG, line);
            if (line.equals("Expect: 100-continue"))
            {
                continueExpected = true;
            }
            line = readLine(stream);
        }
        return continueExpected;
    }
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/AcceptCallback.java
public interface AcceptCallback {
    void statusChanged(AcceptCallbackState state);
}
Difian/DifianApp/src/main/java/com/sonova/difian/utilities/StringHelpers.java
public final class StringHelpers
{
    private StringHelpers()
    {
    }

    public static byte[] convertToBytes(String source)
    {
        char[] chars = source.toCharArray();
        byte[] result = new byte[chars.length];
        for (int i = 0; i < chars.length; i++)
        {
            result[i] = (byte)chars[i];
        }
        return result;
    }
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/AcceptCallbackState.java
public enum AcceptCallbackState {
    Undefined,
    Accessible,
    Connected
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiSide.java
public enum HiSide
{
    LEFT, RIGHT
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageWriter.java
public final class MessageWriter
{
    private static final String TAG = MessageWriter.class.getName();
    private static final MessageWriter _instance = new MessageWriter();

    private MessageWriter()
    {
    }

    public static MessageWriter getInstance()
    {
        return _instance;
    }

    @SuppressWarnings("UnnecessaryCodeBlock")
    public void transmit(OutputStream stream, String host, String id, Message message) throws IOException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in transmit");
        }
        if (host == null)
        {
            throw new IllegalArgumentException("host == null");
        }
        if (id == null)
        {
            throw new IllegalArgumentException("id == null");
        }
        if (message == null)
        {
            throw new IllegalArgumentException("message == null");
        }
        if ((message.getType() != MessageType.USER_INFO_REQUEST) && (message.getType() != MessageType.SMART_REPLY))
        {
            throw new IllegalArgumentException(String.format("Unsupported message type while writing: %s", message.getType()));
        }

        StringWriter messageString = new StringWriter();

        XmlSerializer x = Xml.newSerializer();
        x.setOutput(messageString);

        // Prepare message.
        x.startDocument("ASCII", true);
        {
            x.startTag("", XmlConstants.MESSAGE);
            {
                x.attribute("", "v", "3");
                //noinspection UnnecessaryToStringCall
                x.attribute("", "guid", UUID.randomUUID().toString());
                x.attribute("", "date", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US).format(new Date()));
                if (message.getType() == MessageType.USER_INFO_REQUEST)
                {
                    x.attribute("", "type", "user_info_request");
                }
                else if (message.getType() == MessageType.SMART_REPLY)
                {
                    x.attribute("", "type", "smart_reply");

                    SmartReplyMessage m = (SmartReplyMessage)message;

                    if (m.getReMessage() != null)
                    {
                        x.attribute("", XmlConstants.RE_MESSAGE_GUID, m.getReMessage().getGuid());
                    }
                    else
                    {
                        x.attribute("", XmlConstants.RE_MESSAGE_GUID, "00000000-0000-0000-0000-000000000000");
                    }

                    String sel;
                    String text;

                    if (m.getSelectionCount() != 0)
                    {
                        StringBuilder selBuilder = new StringBuilder();
                        StringBuilder textBuilder = new StringBuilder();
                        for (int i = 0; i < m.getSelectionCount(); i++)
                        {
                            if (i != 0)
                            {
                                selBuilder.append(';');
                                textBuilder.append(';');
                            }
                            selBuilder.append(m.getSelection(i).getId());
                            textBuilder.append(m.getSelection(i).getText());
                        }
                        sel = selBuilder.toString();
                        text = textBuilder.toString();
                    }
                    else
                    {
                        text = m.getReText();
                        sel = "";
                    }

                    x.startTag("", "re_text");
                    {
                        x.text(text);
                    }
                    x.endTag("", "re_text");
                    x.startTag("", XmlConstants.SELECTION);
                    {
                        x.attribute("", XmlConstants.OPTION_IDS, sel);
                    }
                    x.endTag("", XmlConstants.SELECTION);
                }
            }
            x.endTag("", XmlConstants.MESSAGE);
        }
        x.endDocument();

        String m = messageString.toString();
        Log.v(MessageWriter.class.getSimpleName(), m);

        // Send HTTP header.
        stream.write(String.format("PUT /%s_-1 HTTP/1.1\r\n", id).getBytes());
        stream.write("Content-Type: text/xml; charset=utf-8\r\n".getBytes());
        stream.write(String.format("Host: %s\r\n", host).getBytes());
        stream.write(String.format("Content-Length: %d\r\n", m.length()).getBytes());
        stream.write("\r\n".getBytes());
        stream.write(m.getBytes());
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiMuteStatus.java
public enum HiMuteStatus
{
    MUTED, UNMUTED, UNDEFINED
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/TypingMessage.java
public final class TypingMessage extends Message
{
    private final boolean _typing;

    TypingMessage(XmlPullParser parser) throws MessageReaderException
    {
        String typingString = parser.getAttributeValue(null, "is_saving");
        if (typingString == null)
        {
            throw new MessageReaderException("is_saving attribute not present in typing message");
        }
        _typing = Boolean.parseBoolean(typingString);
    }

    @Override
    public MessageType getType()
    {
        return MessageType.TYPING;
    }

    public boolean isTyping()
    {
        return _typing;
    }
}


Question: import android.util.Log;
import com.sonova.difian.communication.messaging.HiMuteStatus;
import com.sonova.difian.communication.messaging.HiSide;
import com.sonova.difian.communication.messaging.HiStatusMessage;
import com.sonova.difian.communication.messaging.Message;
import com.sonova.difian.communication.messaging.MessageReader;
import com.sonova.difian.communication.messaging.MessageReaderException;
import com.sonova.difian.communication.messaging.MessageType;
import com.sonova.difian.communication.messaging.MessageWriter;
import com.sonova.difian.communication.messaging.SmartMessage;
import com.sonova.difian.communication.messaging.SmartReplyMessage;
import com.sonova.difian.communication.messaging.StreamHelpers;
import com.sonova.difian.communication.messaging.TypingMessage;
import com.sonova.difian.communication.messaging.UserInfoMessage;
import com.sonova.difian.communication.messaging.UserInfoRequestMessage;
import com.sonova.difian.communication.utilities.SocketHelpers;
import com.sonova.difian.utilities.Contract;
import com.sonova.difian.utilities.StringHelpers;
import org.xmlpull.v1.XmlPullParserException;
import org.yaler.AcceptCallback;
import org.yaler.AcceptCallbackState;
import org.yaler.YalerSSLServerSocket;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// Copyright (c) 2014 Phonak, Inc. and Oberon microsystems, Inc. All rights reserved.

package com.sonova.difian.communication.chat;



public final class ChatManager
{
    private static final String TAG = ChatManager.class.getName();
    private static final int RX_SOCKET_COUNT = 2;
    private static final int TX_SOCKET_COUNT = 1;
    private static final int SOCKET_TIMEOUT = 10000;
    private final ChatManagerCallback _delegate;
    private final Object _lockObject = new Object();
    private final Object _txLockObject = new Object();
    private final SocketFactory _sslSocketFactory = SSLSocketFactory.getDefault();
    private final Pattern _locationPattern = Pattern.compile("^Location\\: https://([a-z0-9\\-\\.]+)\\:443/[a-z0-9]+\\_\\-1$", Pattern.CASE_INSENSITIVE);
    private final Pattern _chatMessagePattern = Pattern.compile("^[\u0020-\u7dff\ue000-\ufffd]*$", 0);
    // Guarded by _lockObject.
    private final AbstractList<YalerSSLServerSocket> _rxChatListenerSockets = new ArrayList<YalerSSLServerSocket>(RX_SOCKET_COUNT);
    // Guarded by _lockObject.
    private final AbstractList<Socket> _rxChatSockets = new ArrayList<Socket>();
    // Guarded by _lockObject.
    private final AbstractList<Socket> _txChatSockets = new ArrayList<Socket>(TX_SOCKET_COUNT);
    // Guarded by _lockObject.
    private final ArrayList<ChatMessage> _chatHistory = new ArrayList<ChatMessage>(4);
    // Guarded by _lockObject, _txLockObject.
    private final Queue<ChatMessage> _pendingSends = new ArrayDeque<ChatMessage>(2);
    // Guarded by _lockObject, _txLockObject.
    private boolean _pendingAudiologistInfo;
    // Guarded by _lockObject.
    private String _id;
    // Guarded by _lockObject.
    private String _relayHost;
    // Guarded by _lockObject.
    private AudiologistInfo _audiologistInfo = AudiologistInfo.EMPTY;
    // Guarded by _lockObject.
Answer:     private HiMuteStatus _muteStatusLeft = HiMuteStatus.UNDEFINED;