content: package com.adafruit.bluefruit.le.connect.mqtt;
  
  import android.content.Context;
  import android.util.Log;
  import android.widget.Toast;
  
  import com.adafruit.bluefruit.le.connect.R;
  
  import org.eclipse.paho.android.service.MqttAndroidClient;
  import org.eclipse.paho.android.service.MqttTraceHandler;
  import org.eclipse.paho.client.mqttv3.IMqttActionListener;
  import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  import org.eclipse.paho.client.mqttv3.IMqttToken;
  import org.eclipse.paho.client.mqttv3.MqttCallback;
  import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  import org.eclipse.paho.client.mqttv3.MqttException;
  import org.eclipse.paho.client.mqttv3.MqttMessage;
  
  import java.util.UUID;
  
  public class MqttManager implements IMqttActionListener, MqttCallback, MqttTraceHandler {
      // Log
      private final static String TAG = MqttManager.class.getSimpleName();
  
      // Singleton
      private static MqttManager mInstance = null;
  
      // Types
      public enum MqqtConnectionStatus {
          CONNECTING,
          CONNECTED,
          DISCONNECTING,
          DISCONNECTED,
          ERROR,
          NONE
      }
  
      public static int MqqtQos_AtMostOnce = 0;
      public static int MqqtQos_AtLeastOnce = 1;
      public static int MqqtQos_ExactlyOnce = 2;
  
      // Data
      private MqttAndroidClient mMqttClient;
      private MqttManagerListener mListener;
      private MqqtConnectionStatus mMqqtClientStatus = MqqtConnectionStatus.NONE;
      private Context mContext;
  
      public static MqttManager getInstance(Context context) {
          if (mInstance == null) {
              mInstance = new MqttManager(context);
          }
          return mInstance;
      }
  
      public MqttManager(Context context) {
          mContext = context.getApplicationContext();
      }
  
      @Override
      public void finalize() throws Throwable {
  
          try {
              if (mMqttClient != null) {
                  mMqttClient.unregisterResources();
              }
          } finally {
              super.finalize();
          }
      }
  
  
      public MqqtConnectionStatus getClientStatus() {
          return mMqqtClientStatus;
      }
  
      public void setListener(MqttManagerListener listener) {
          mListener = listener;
      }
  
      // region MQTT
      public void subscribe(String topic, int qos) {
          if (mMqttClient != null && mMqqtClientStatus == MqqtConnectionStatus.CONNECTED && topic != null) {
              try {
                  Log.d(TAG, "Mqtt: subscribe to " + topic + " qos:" + qos);
                  mMqttClient.subscribe(topic, qos);
              } catch (MqttException e) {
                  Log.e(TAG, "Mqtt:x subscribe error: ", e);
              }
          }
      }
  
      public void unsubscribe(String topic) {
          if (mMqttClient != null && mMqqtClientStatus == MqqtConnectionStatus.CONNECTED && topic != null) {
              try {
                  Log.d(TAG, "Mqtt: unsubscribe from " + topic);
                  mMqttClient.unsubscribe(topic);
              } catch (MqttException e) {
                  Log.e(TAG, "Mqtt:x unsubscribe error: ", e);
              }
          }
  
      }
  
  
      public void publish(String topic, String payload, int qos) {
          if (mMqttClient != null && mMqqtClientStatus == MqqtConnectionStatus.CONNECTED && topic != null) {
              boolean retained = false;
  
              try {
                  Log.d(TAG, "Mqtt: publish " + payload + " for topic " + topic + " qos:" + qos);
                  mMqttClient.publish(topic, payload.getBytes(), qos, retained, null, null);
              } catch (MqttException e) {
                  Log.e(TAG, "Mqtt:x publish error: ", e);
              }
          }
      }
  
      public void disconnect() {
          if (mMqttClient != null && mMqqtClientStatus == MqqtConnectionStatus.CONNECTED) {
              try {
                  Log.d(TAG, "Mqtt: disconnect");
  //                mMqqtClientStatus = MqqtConnectionStatus.DISCONNECTING;
                  mMqqtClientStatus = MqqtConnectionStatus.DISCONNECTED;      // Note: it seems that the disconnected callback is never invoked. So we fake here that the final state is disconnected
                  mMqttClient.disconnect(null, this);
  
                  mMqttClient.unregisterResources();
                  mMqttClient = null;
              } catch (MqttException e) {
                  Log.e(TAG, "Mqtt:x disconnection error: ", e);
              }
          }
  
      }
  
      public void connectFromSavedSettings(Context context) {
          MqttSettings settings = MqttSettings.getInstance(context);
          String host = settings.getServerAddress();
          int port = settings.getServerPort();
  
          String username = settings.getUsername();
          String password = settings.getPassword();
          boolean cleanSession = settings.isCleanSession();
          boolean sslConnection = settings.isSslConnection();
  
          connect(context, host, port, username, password, cleanSession, sslConnection);
      }
  
      public void connect(Context context, String host, int port, String username, String password, boolean cleanSession, boolean sslConnection) {
          String clientId = "Bluefruit_"+ UUID.randomUUID().toString();
          final int timeout = MqttConnectOptions.CONNECTION_TIMEOUT_DEFAULT;
          final int keepalive = MqttConnectOptions.KEEP_ALIVE_INTERVAL_DEFAULT;
  
          String message = null;
          String topic = null;
          int qos = 0;
          boolean retained = false;
  
          String uri;
          if (sslConnection) {
              uri = "ssl://" + host + ":" + port;
  
          } else {
              uri = "tcp://" + host + ":" + port;
          }
  
          Log.d(TAG, "Mqtt: Create client: "+clientId);
          mMqttClient = new MqttAndroidClient(context, uri, clientId);
          mMqttClient.registerResources(mContext);
  
          MqttConnectOptions conOpt = new MqttConnectOptions();
          Log.d(TAG, "Mqtt: clean session:" +(cleanSession?"yes":"no"));
          conOpt.setCleanSession(cleanSession);
          conOpt.setConnectionTimeout(timeout);
          conOpt.setKeepAliveInterval(keepalive);
          if (username != null && username.length() > 0) {
              Log.d(TAG, "Mqtt: username: " + username);
              conOpt.setUserName(username);
          }
          if (password != null && password.length() > 0) {
              Log.d(TAG, "Mqtt: password: " + password);
              conOpt.setPassword(password.toCharArray());
          }
  
          boolean doConnect = true;
          if ((message != null && message.length() > 0) || (topic != null && topic.length() > 0)) {
              // need to make a message since last will is set
              Log.d(TAG, "Mqtt: setwill");
              try {
                  conOpt.setWill(topic, message.getBytes(), qos, retained);
              } catch (Exception e) {
                  Log.e(TAG, "Mqtt: Can't set will", e);
                  doConnect = false;
                  //callback.onFailure(null, e);
              }
          }
          mMqttClient.setCallback(this);
          mMqttClient.setTraceCallback(this);
  
          if (doConnect) {
              MqttSettings.getInstance(mContext).setConnectedEnabled(true);
  
              try {
                  Log.d(TAG, "Mqtt: connect to " + uri);
                  mMqqtClientStatus = MqqtConnectionStatus.CONNECTING;
                  mMqttClient.connect(conOpt, null, this);
              } catch (MqttException e) {
                  Log.e(TAG, "Mqtt: connection error: ", e);
              }
          }
      }
  
      // endregion
  
      // region IMqttActionListener
      @Override
      public void onSuccess(IMqttToken iMqttToken) {
          if (mMqqtClientStatus == MqqtConnectionStatus.CONNECTING) {
              Log.d(TAG, "Mqtt connect onSuccess");
              mMqqtClientStatus = MqqtConnectionStatus.CONNECTED;
              if (mListener != null) mListener.onMqttConnected();
  
              MqttSettings settings = MqttSettings.getInstance(mContext);
              String topic = settings.getSubscribeTopic();
              int topicQos = settings.getSubscribeQos();
              if (settings.isSubscribeEnabled() && topic != null) {
                  subscribe(topic, topicQos);
              }
          } else if (mMqqtClientStatus == MqqtConnectionStatus.DISCONNECTING) {
              Log.d(TAG, "Mqtt disconnect onSuccess");
              mMqqtClientStatus = MqqtConnectionStatus.DISCONNECTED;
              if (mListener != null) mListener.onMqttDisconnected();
          } else {
              Log.d(TAG, "Mqtt unknown onSuccess");
          }
      }
  
      @Override
      public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
          Log.d(TAG, "Mqtt onFailure. " + throwable);
  
          // Remove the auto-connect till the failure is solved
          if (mMqqtClientStatus == MqqtConnectionStatus.CONNECTING) {
              MqttSettings.getInstance(mContext).setConnectedEnabled(false);
          }
  
          // Set as an error
          mMqqtClientStatus = MqqtConnectionStatus.ERROR;
          String errorText = mContext.getString(R.string.mqtt_connection_failed)+". "+throwable.getLocalizedMessage();
          Toast.makeText(mContext, errorText, Toast.LENGTH_LONG).show();
  
          // Call listener
          if (mListener != null) mListener.onMqttDisconnected();
      }
      // endregion
  
      // region MqttCallback
      @Override
      public void connectionLost(Throwable throwable) {
          Log.d(TAG, "Mqtt connectionLost. " + throwable);
  
          if (throwable != null) {        // if disconnected because a reason show toast. Standard disconnect will have a null throwable
              Toast.makeText(mContext, R.string.mqtt_connection_lost, Toast.LENGTH_LONG).show();
          }
  
          mMqqtClientStatus = MqqtConnectionStatus.DISCONNECTED;
  
          if (mListener != null) {
              mListener.onMqttDisconnected();
          }
      }
  
      @Override
      public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
          String message = new String(mqttMessage.getPayload());
  
          if (message.length() > 0) {      // filter cleared messages (to avoid duplicates)
  
              Log.d(TAG, "Mqtt messageArrived from topic: " + topic + " message: " + message + " isDuplicate: " + (mqttMessage.isDuplicate() ? "yes" : "no"));
              if (mListener != null) {
                  mListener.onMqttMessageArrived(topic, mqttMessage);
              }
  
              // Fix duplicated messages clearing the received payload and processing only non null messages
              mqttMessage.clearPayload();
          }
      }
  
      @Override
      public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
          Log.d(TAG, "Mqtt deliveryComplete");
  
      }
  
      // endregion
  
      // region MqttTraceHandler
      @Override
      public void traceDebug(String source, String message) {
          Log.d(TAG, "Mqtt traceDebug");
  
      }
  
      @Override
      public void traceError(String source, String message) {
          Log.d(TAG, "Mqtt traceError");
  
      }
  
      @Override
      public void traceException(String source, String message, Exception e) {
          Log.d(TAG, "Mqtt traceException");
  
      }
  
      // endregion
  
  
      public interface MqttManagerListener {
          void onMqttConnected();
  
          void onMqttDisconnected();
  
          void onMqttMessageArrived(String topic, MqttMessage mqttMessage);
      }
  }
  