package com.badoo.chateau.example; import android.os.Handler; import android.os.Looper; import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.parse.ParseUser; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; /** * Client for connecting to the socket notification proxy server which send notifications about new messages. */ public class SocketNotificationClient { private static final String TAG = SocketNotificationClient.class.getSimpleName(); private static final String TYPE_NEW_MESSAGE = "newMessageInChat"; private static final String TYPE_IMAGE_UPLOADED = "imageUploaded"; private static final String TYPE_USER_TYPING = "userTyping"; private static final long RETRY_TIMEOUT = 10 * 1000; @NonNull private final Handler mRetryHandler = new Handler(Looper.getMainLooper()); @NonNull private final String mHost; private final int mPort; @NonNull private final Broadcaster mBroadcaster; @Nullable private SocketThread mThread; boolean mRestartOnDisconnect = false; public SocketNotificationClient(@NonNull Broadcaster broadcaster, @NonNull String host, int port) { mBroadcaster = broadcaster; mHost = host; mPort = port; } /** * Starts and connects the client. After this call any received socket push will be broadcast as a local broadcast. */ @MainThread public void start() { mRestartOnDisconnect = true; if (mThread != null) { return; } Log.d(TAG, "Starting socket notification client"); mThread = new SocketThread(); mThread.start(); } /** * Stops and disconnects the client. */ @MainThread public void stop() { mRestartOnDisconnect = false; // Close the connection Log.d(TAG, "Stopping socket notification client"); if (mThread != null) { mThread.tryToStop(); } cleanup(); } /** * Restarts the client, making sure that the registration is updated. */ public void restart() { stop(); start(); } private void cleanup() { mThread = null; } private void scheduleRestart() { if (mRestartOnDisconnect) { mRetryHandler.postDelayed(this::start, RETRY_TIMEOUT); } } private void onNotification(JSONObject payload) throws JSONException { final String type = payload.getString("type"); if (TYPE_NEW_MESSAGE.equals(type)) { final String chatId = payload.getString("chatId"); final long timestamp = payload.getLong("timestamp"); mBroadcaster.conversationUpdated(chatId, timestamp); } else if (TYPE_IMAGE_UPLOADED.equals(type)) { final String chatId = payload.getString("chatId"); final String messageId = payload.getString("messageId"); mBroadcaster.imageUploaded(chatId, messageId); } else if (TYPE_USER_TYPING.equals(type)) { final String chatId = payload.getString("chatId"); final String userId = payload.getString("userId"); mBroadcaster.otherUserTyping(userId, chatId); } } private class SocketThread extends Thread { private OutputStreamWriter mOut; private BufferedReader mIn; @Nullable private Socket mSocket; public SocketThread() { super("SocketThread"); } public void tryToStop() { interrupt(); if (mSocket != null) { try { mSocket.close(); } catch (IOException e) { //Ignored } } } @Override public void run() { try { mSocket = new Socket(mHost, mPort); mOut = new OutputStreamWriter(mSocket.getOutputStream()); mIn = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); Log.d(TAG, "Connected to socket notification server"); // Send subscription request if (ParseUser.getCurrentUser() != null) { final String id = ParseUser.getCurrentUser().getObjectId(); mOut.write("{\"type\":\"register\", \"value\":\"" + id + "\"}"); mOut.flush(); } // Start listening for notifications while (true) { final String data = mIn.readLine(); if (data == null) { throw new IOException("Socket dropped?"); } JSONObject json = new JSONObject(data); onNotification(json); Log.d(TAG, "Received data from notification server: " + data); } } catch (Exception e) { Log.e(TAG, "Exception on socket thread", e); cleanup(); scheduleRestart(); } Log.d(TAG, "Socket notification thread stopping"); } } }