title: Working with Sinch Instant Messaging System
body: <p>I am using Sinch and Parse for my instant messaging system integrated in our application, and I have two concerns. </p>
  
  <p>1) For some reason, I am receiving the following error when the messaging activity is displayed: The message client did not start". Furthermore, the message does not seem to go through in Sinch and not reflected visually in the application.</p>
  
  <p>Below is the activity code (when the user clicks on the "quick chat" button, it takes them to the messaging activity page.</p>
  
  <p>Below is the activity code for the messaging activity</p>
  
  <pre><code>public class MessagingActivity extends Activity implements ServiceConnection, MessageClientListener {
  
      private String recipientId;
      private Button sendButton;
      private EditText messageBodyField;
      private String messageBody;
      private MessageService.MessageServiceInterface messageService;
      private MessageAdapter messageAdapter;
      private ListView messagesList;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.messaging);
  
          doBind();
  
  
          messagesList = (ListView) findViewById(R.id.listMessages);
          messageAdapter = new MessageAdapter(this);
          messagesList.setAdapter(messageAdapter);
  
          Intent intent = getIntent();
          recipientId = intent.getStringExtra("Name");
  
          messageBodyField = (EditText) findViewById(R.id.messageBodyField);
          sendButton = (Button) findViewById(R.id.sendButton);
  
          sendButton.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  sendMessage();
              }
          });
      }
  
      private void sendMessage() {
          messageBodyField = (EditText) findViewById(R.id.messageBodyField);
          messageBody = messageBodyField.getText().toString();
          if (messageBody.isEmpty()) {
              Toast.makeText(this, "Please enter a message", Toast.LENGTH_LONG).show();
              return;
          }
  
          //Here is where you will actually send the message throught Sinch
          messageService.sendMessage(recipientId, messageBody);
          messageBodyField.setText("");
      }
  
      private void doBind() {
          Intent serviceIntent = new Intent(this, MessageService.class);
          bindService(serviceIntent, this, BIND_AUTO_CREATE);
      }
  
      @Override
      public void onDestroy() {
          unbindService(this);
          super.onDestroy();
      }
  
      @Override
      public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
          //Define the messaging service and add a listener
          messageService = (MessageService.MessageServiceInterface) iBinder;
          messageService.addMessageClientListener(this);
          if (!messageService.isSinchClientStarted()) {
              Toast.makeText(this, "The message client did not start."
                  ,Toast.LENGTH_LONG).show();
          }
      }
  
      @Override
      public void onServiceDisconnected(ComponentName componentName) {
          messageService = null;
      }
  
      @Override
      public void onMessageDelivered(MessageClient client, MessageDeliveryInfo deliveryInfo) {
          //Intentionally  left blank
      }
  
      @Override
      public void onMessageFailed(MessageClient client, Message message,
                                  MessageFailureInfo failureInfo) {
          //Notify the user if message fails to send
          Toast.makeText(this, "Message failed to send.", Toast.LENGTH_LONG).show();
      }
  
      @Override
      public void onIncomingMessage(MessageClient client, Message message) {
          messageAdapter.addMessage(message, MessageAdapter.DIRECTION_INCOMING);
      }
  
      @Override
      public void onMessageSent(MessageClient client, Message message, String recipientId) {
          messageAdapter.addMessage(message, MessageAdapter.DIRECTION_OUTGOING);
      }
  
      @Override
      public void onShouldSendPushData(MessageClient client, Message message, List&lt;PushPair&gt; pushPairs) {
          //Intentionally left blank
      }
  }
  </code></pre>
  
  <p>I have verified that the APP_KEY, the APP_SECRET and ENVIRONMENT matches what was recorded on Sinch.
  I have tried this both on the emulator, and on a physical device.</p>
  
  <p>Thanks in advance</p>
  
  <p>Code service</p>
  
  <pre><code> public class MessageService extends Service implements SinchClientListener {
  
          private static final String APP_KEY = "XXXXX";
          private static final String APP_SECRET = "YYYYY";
          private static final String ENVIRONMENT = "sandbox.sinch.com";
          private final MessageServiceInterface serviceInterface = new MessageServiceInterface();
          private SinchClient sinchClient = null;
          private MessageClient messageClient = null;
          private String currentUserId;
  
          @Override
          public int onStartCommand(Intent intent, int flags, int startId) {
  
              currentUserId = ParseUser.getCurrentUser().getObjectId().toString();
  
              if (currentUserId != null &amp;&amp; !isSinchClientStarted()) {
                  startSinchClient(currentUserId);
              }
  
              return super.onStartCommand(intent, flags, startId);
          }
  
          public void startSinchClient(String username) {
              sinchClient = Sinch.getSinchClientBuilder().context(this).userId(username).applicationKey(APP_KEY)
                      .applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
  
              sinchClient.addSinchClientListener(this);
  
              sinchClient.setSupportMessaging(true);
              sinchClient.setSupportActiveConnectionInBackground(true);
  
              sinchClient.checkManifest();
              sinchClient.start();
          }
  
          private boolean isSinchClientStarted() {
              return sinchClient != null &amp;&amp; sinchClient.isStarted();
          }
  
          @Override
          public void onClientFailed(SinchClient client, SinchError error) {
              sinchClient = null;
          }
  
          @Override
          public void onClientStarted(SinchClient client) {
              client.startListeningOnActiveConnection();
              messageClient = client.getMessageClient();
          }
  
          @Override
          public void onClientStopped(SinchClient client) {
              sinchClient = null;
          }
  
          public void stop() {
              if (isSinchClientStarted()) {
                  sinchClient.stop();
                  sinchClient.removeSinchClientListener(this);
              }
              sinchClient = null;
          }
  
          @Override
          public IBinder onBind(Intent intent) {
              return serviceInterface;
          }
  
          @Override
          public void onLogMessage(int level, String area, String message) {
              //Intentionally left blank
          }
  
          @Override
          public void onRegistrationCredentialsRequired(SinchClient client, ClientRegistration clientRegistration) {
              //Intentionally left blank
          }
  
          public void sendMessage(String recipientUserId, String textBody) {
              if (messageClient != null) {
                  WritableMessage message = new WritableMessage(recipientUserId, textBody);
                  messageClient.send(message);
              }
          }
  
          public void addMessageClientListener(MessageClientListener listener) {
              if (messageClient != null) {
                  messageClient.addMessageClientListener(listener);
              }
          }
  
          public void removeMessageClientListener(MessageClientListener listener) {
              if (messageClient != null) {
                  messageClient.removeMessageClientListener(listener);
              }
          }
  
          public class MessageServiceInterface extends Binder {
  
              public void sendMessage(String recipientUserId, String textBody) {
                  MessageService.this.sendMessage(recipientUserId, textBody);
              }
  
              public void addMessageClientListener(MessageClientListener listener) {
                  MessageService.this.addMessageClientListener(listener);
              }
  
              public void removeMessageClientListener(MessageClientListener listener) {
                  MessageService.this.removeMessageClientListener(listener);
              }
  
              public boolean isSinchClientStarted() {
                  return MessageService.this.isSinchClientStarted();
              }
          }
      }
  </code></pre>
  
  <p>2) Limit: I would like to set a limit, where a only 25 messages would be available between a particular party.</p>
  
  <p>Thanks in advance, and if you need any clarification, let me know.</p>
  
  <p><strong>Update 3</strong>
  When user clicks on this button, he is taking to the MessagingActivity with the person he has been matched to based upon the below criteria</p>
  
  <pre><code> final Button ibutton = (Button) this.findViewById(R.id.btnQuickChat);
          idrinks.setOnClickListener(new OnClickListener() {
  
          @Override
          public void onClick(View v) {
              openConversation();
          }
  
          private void openConversation() {
              // TODO Auto-generated method stub
                ParseQuery&lt;ParseUser&gt; query = ParseUser.getQuery();
                  query.whereNotEqualTo("objectId", ParseUser.getCurrentUser()
                          .getObjectId());
  
                  query.setLimit(1);
  
                  query.findInBackground(new FindCallback&lt;ParseUser&gt;() {
                      public void done(List&lt;ParseUser&gt; user, ParseException e) {
                            if (e == null) {
                                 Intent intent = new Intent(getApplicationContext(), MessagingActivity.class);
                                 intent.putExtra("RECIPIENT_ID", user.get(0).getObjectId());
                                 startActivity(intent);
                             } else {
                                 Toast.makeText(getApplicationContext(),
                                     "Error finding that user",
                                         Toast.LENGTH_SHORT).show();
                             }
                         }
                      });
                  }
      });
  </code></pre>
  
  <p>MessagingActivity (nearly same as one provided in tutorial):</p>
  
  <pre><code>public class MessagingActivity extends Activity {
  
      private String recipientId;
      private EditText messageBodyField;
      private String messageBody;
      private MessageService.MessageServiceInterface messageService;
      private MessageAdapter messageAdapter;
      private ListView messagesList;
      private String currentUserId;
      private ServiceConnection serviceConnection = new MyServiceConnection();
      private MessageClientListener messageClientListener = new MyMessageClientListener();
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.messaging);
  
          bindService(new Intent(this, MessageService.class), serviceConnection, BIND_AUTO_CREATE);
  
          Intent intent = getIntent();
          recipientId = intent.getStringExtra("RECIPIENT_ID");
          currentUserId = ParseUser.getCurrentUser().getObjectId();
  
          messagesList = (ListView) findViewById(R.id.listMessages);
          messageAdapter = new MessageAdapter(this);
          messagesList.setAdapter(messageAdapter);
          populateMessageHistory();
  
          messageBodyField = (EditText) findViewById(R.id.messageBodyField);
  
          findViewById(R.id.sendButton).setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  sendMessage();
              }
          });
      }
  
      //get previous messages from parse &amp; display
      private void populateMessageHistory() {
          String[] userIds = {currentUserId, recipientId};
          ParseQuery&lt;ParseObject&gt; query = ParseQuery.getQuery("ParseMessage");
          query.whereContainedIn("senderId", Arrays.asList(userIds));
          query.whereContainedIn("recipientId", Arrays.asList(userIds));
          query.orderByAscending("createdAt");
          query.findInBackground(new FindCallback&lt;ParseObject&gt;() {
              @Override
              public void done(List&lt;ParseObject&gt; messageList, com.parse.ParseException e) {
                  if (e == null) {
                      for (int i = 0; i &lt; messageList.size(); i++) {
                          WritableMessage message = new WritableMessage(messageList.get(i).get("recipientId").toString(), messageList.get(i).get("messageText").toString());
                          if (messageList.get(i).get("senderId").toString().equals(currentUserId)) {
                              messageAdapter.addMessage(message, MessageAdapter.DIRECTION_OUTGOING);
                          } else {
                              messageAdapter.addMessage(message, MessageAdapter.DIRECTION_INCOMING);
                          }
                      }
                  }
              }
          });
      }
  
      private void sendMessage() {
          messageBody = messageBodyField.getText().toString();
          if (messageBody.isEmpty()) {
              Toast.makeText(this, "Please enter a message", Toast.LENGTH_LONG).show();
              return;
          }
  
          messageService.sendMessage(recipientId, messageBody);
          messageBodyField.setText("");
      }
  
      @Override
      public void onDestroy() {
          messageService.removeMessageClientListener(messageClientListener);
          unbindService(serviceConnection);
          super.onDestroy();
      }
  
      private class MyServiceConnection implements ServiceConnection {
          @Override
          public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
              messageService = (MessageService.MessageServiceInterface) iBinder;
              messageService.addMessageClientListener(messageClientListener);
          }
  
          @Override
          public void onServiceDisconnected(ComponentName componentName) {
              messageService = null;
          }
      }
  
      private class MyMessageClientListener implements MessageClientListener {
          @Override
          public void onMessageFailed(MessageClient client, Message message,
                                      MessageFailureInfo failureInfo) {
              Toast.makeText(MessagingActivity.this, "Message failed to send.", Toast.LENGTH_LONG).show();
          }
  
          @Override
          public void onIncomingMessage(MessageClient client, Message message) {
              if (message.getSenderId().equals(recipientId)) {
                  WritableMessage writableMessage = new WritableMessage(message.getRecipientIds().get(0), message.getTextBody());
                  messageAdapter.addMessage(writableMessage, MessageAdapter.DIRECTION_INCOMING);
              }
          }
  
          @Override
          public void onMessageSent(MessageClient client, Message message, String recipientId) {
  
              final WritableMessage writableMessage = new WritableMessage(message.getRecipientIds().get(0), message.getTextBody());
  
              //only add message to parse database if it doesn't already exist there
              ParseQuery&lt;ParseObject&gt; query = ParseQuery.getQuery("ParseMessage");
              query.whereEqualTo("sinchId", message.getMessageId());
              query.findInBackground(new FindCallback&lt;ParseObject&gt;() {
                  @Override
                  public void done(List&lt;ParseObject&gt; messageList, com.parse.ParseException e) {
                      if (e == null) {
                          if (messageList.size() == 0) {
                              ParseObject parseMessage = new ParseObject("ParseMessage");
                              parseMessage.put("senderId", currentUserId);
                              parseMessage.put("recipientId", writableMessage.getRecipientIds().get(0));
                              parseMessage.put("messageText", writableMessage.getTextBody());
                              parseMessage.put("sinchId", writableMessage.getMessageId());
                              parseMessage.saveInBackground();
  
                              messageAdapter.addMessage(writableMessage, MessageAdapter.DIRECTION_OUTGOING);
                          }
                      }
                  }
              });
          }
  
          @Override
          public void onMessageDelivered(MessageClient client, MessageDeliveryInfo deliveryInfo) {}
  
          @Override
          public void onShouldSendPushData(MessageClient client, Message message, List&lt;PushPair&gt; pushPairs) {}
      }
  }
  </code></pre>
  
  <p>MessageService activity</p>
  
  <pre><code>public class MessageService extends Service implements SinchClientListener {
  
      private static final String APP_KEY = "61b1bfc0-b82a-44f5-ab68-dedca69ead8c";
      private static final String APP_SECRET = "jrFrLr8Adkm0Na4nLdASDw==";
      private static final String ENVIRONMENT = "sandbox.sinch.com";
      private final MessageServiceInterface serviceInterface = new MessageServiceInterface();
      private SinchClient sinchClient = null;
      private MessageClient messageClient = null;
      private String currentUserId;
      private LocalBroadcastManager broadcaster;
  
      private Intent broadcastIntent = new Intent("com.dooba.beta.matchOptionActivity");
  
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
  
          currentUserId = ParseUser.getCurrentUser().getObjectId();
  
          if (currentUserId != null &amp;&amp; !isSinchClientStarted()) {
              startSinchClient(currentUserId);
          }
  
          broadcaster = LocalBroadcastManager.getInstance(this);
  
          return super.onStartCommand(intent, flags, startId);
      }
  
      public void startSinchClient(String username) {
          sinchClient = Sinch.getSinchClientBuilder().context(this).userId(username).applicationKey(APP_KEY)
                  .applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
  
          sinchClient.addSinchClientListener(this);
  
          sinchClient.setSupportMessaging(true);
          sinchClient.setSupportActiveConnectionInBackground(true);
  
          sinchClient.checkManifest();
          sinchClient.start();
      }
  
      private boolean isSinchClientStarted() {
          return sinchClient != null &amp;&amp; sinchClient.isStarted();
      }
  
      @Override
      public void onClientFailed(SinchClient client, SinchError error) {
          broadcastIntent.putExtra("success", false);
          broadcaster.sendBroadcast(broadcastIntent);
  
          sinchClient = null;
      }
  
      @Override
      public void onClientStarted(SinchClient client) {
          broadcastIntent.putExtra("success", true);
          broadcaster.sendBroadcast(broadcastIntent);
  
          client.startListeningOnActiveConnection();
          messageClient = client.getMessageClient();
      }
  
      @Override
      public void onClientStopped(SinchClient client) {
          sinchClient = null;
      }
  
      @Override
      public IBinder onBind(Intent intent) {
          return serviceInterface;
      }
  
      @Override
      public void onLogMessage(int level, String area, String message) {
      }
  
      @Override
      public void onRegistrationCredentialsRequired(SinchClient client, ClientRegistration clientRegistration) {
      }
  
      public void sendMessage(String recipientUserId, String textBody) {
          if (messageClient != null) {
              WritableMessage message = new WritableMessage(recipientUserId, textBody);
              messageClient.send(message);
          }
      }
  
      public void addMessageClientListener(MessageClientListener listener) {
          if (messageClient != null) {
              messageClient.addMessageClientListener(listener);
          }
      }
  
      public void removeMessageClientListener(MessageClientListener listener) {
          if (messageClient != null) {
              messageClient.removeMessageClientListener(listener);
          }
      }
  
      public class MessageServiceInterface extends Binder {
          public void sendMessage(String recipientUserId, String textBody) {
              MessageService.this.sendMessage(recipientUserId, textBody);
          }
  
          public void addMessageClientListener(MessageClientListener listener) {
              MessageService.this.addMessageClientListener(listener);
          }
  
          public void removeMessageClientListener(MessageClientListener listener) {
              MessageService.this.removeMessageClientListener(listener);
          }
  
          public boolean isSinchClientStarted() {
              return MessageService.this.isSinchClientStarted();
          }
      }
  </code></pre>
  
  <p>Message adapter activity:</p>
  
  <pre><code>public class MessageAdapter extends BaseAdapter {
  
      public static final int DIRECTION_INCOMING = 0;
      public static final int DIRECTION_OUTGOING = 1;
  
      private List&lt;Pair&lt;WritableMessage, Integer&gt;&gt; messages;
      private LayoutInflater layoutInflater;
  
      public MessageAdapter(Activity activity) {
          layoutInflater = activity.getLayoutInflater();
          messages = new ArrayList&lt;Pair&lt;WritableMessage, Integer&gt;&gt;();
      }
  
      public void addMessage(WritableMessage message, int direction) {
          messages.add(new Pair(message, direction));
          notifyDataSetChanged();
      }
  
      @Override
      public int getCount() {
          return messages.size();
      }
  
      @Override
      public Object getItem(int i) {
          return messages.get(i);
      }
  
      @Override
      public long getItemId(int i) {
          return i;
      }
  
      @Override
      public int getViewTypeCount() {
          return 2;
      }
  
      @Override
      public int getItemViewType(int i) {
          return messages.get(i).second;
      }
  
      @Override
      public View getView(int i, View convertView, ViewGroup viewGroup) {
          int direction = getItemViewType(i);
  
          //show message on left or right, depending on if
          //it's incoming or outgoing
          if (convertView == null) {
              int res = 0;
              if (direction == DIRECTION_INCOMING) {
                  res = R.layout.message_right;
              } else if (direction == DIRECTION_OUTGOING) {
                  res = R.layout.message_left;
              }
              convertView = layoutInflater.inflate(res, viewGroup, false);
          }
  
          WritableMessage message = messages.get(i).first;
  
          TextView txtMessage = (TextView) convertView.findViewById(R.id.txtMessage);
          txtMessage.setText(message.getTextBody());
  
          return convertView;
      }
  }
  </code></pre>