//WebSocketChannelClient.java
//For Socket Io
IO .Options opts = new IO .Options ();
//opts.forceNew = false;
opts .reconnection = true ;
opts .transports = new String []{"websocket" };
opts .upgrade = false ;
try {
OkHttpClient client
= SelfSigningClientBuilder .createClient (context );
opts .callFactory = client ;
opts .webSocketFactory = client ;
} catch (Exception e ) {
C .DISCONNECTION_OCCURRED_IN_LAST_CALL = true ;
Lg .d (TAG , e .getMessage ().toString ());
return ;
}
try {
//ws.connect(new URI(wsServerUrl), wsObserver);// connect and add listener
//For socket Io
mSocket = IO .socket (C .CALL_SOCKET_URL , opts );
mSocket .io ().on (Manager .EVENT_TRANSPORT , onTransport );
mSocket .on (Socket .EVENT_CONNECT , onConnect );
mSocket .on (Socket .EVENT_RECONNECT , onReconnect );
mSocket .on (Socket .EVENT_DISCONNECT , onDisconnect );
mSocket .on (Socket .EVENT_CONNECT_ERROR , onConnectionError );
mSocket .on ("connect" , onUserConnect );
mSocket .on ("notify" , onNotify );
//WebSocketChannelClient.java
// region : Socket Events
private Emitter .Listener onConnect = args -> {
Lg .d (TAG_SOCKET_IO , "Socket Connected" );
Lg .d (TAG , "Socket Connected" );
Lg .d (TAG_SOCKET_IO , "Socket ID: " + mSocket .id ());
if (U .isEmpty (mSocket .id ()))
Lg .d (TAG , "isJoinedOnce: " + isJoinedOnce );
mySocketId = mSocket .id ();
//Join Working
state = WebSocketConnectionState .REGISTERED ;
if (!isJoinedOnce ) {
isJoinedOnce = true ;
join (roomID );
} else
rejoin (roomID );
events .onWebSocketConnected ();
};
private Emitter .Listener onDisconnect = args -> {
String socketId = mSocket .id ();
Lg .d (TAG_SOCKET_IO , "Socket Disconnected: SocketId: " +socketId );
events .onWebSocketDisconnected ();
};
private Emitter .Listener onReconnect = args -> {
String socketId = mSocket .id ();
Lg .d (TAG_SOCKET_IO , "Socket Reconnect: socketId: " +socketId );
};
private Emitter .Listener onConnectionError = args -> {
String socketId = mSocket .id ();
Lg .d (TAG_SOCKET_IO , "Socket Connection Error socketId: " +socketId );
};
private Emitter .Listener onUserConnect = args -> {
Lg .d (TAG , "User Joined" );
};
// Do not remove this member variable. If this is removed, the observer gets garbage collected and
// this causes test breakages.
private Emitter .Listener onExchange = args -> {
JSONObject exchangeMsg = (JSONObject ) args [0 ];
//Socket ID from this msg came
try {
String from = exchangeMsg .getString ("from" );
Lg .d (TAG_SOCKET_IO , "OnExchanges get from:" + from );
otherUserSocketId = from ;
String message = exchangeMsg .getString ("message" );
Lg .d (TAG_SOCKET_IO , "Message: " + message );
events .onWebSocketMessage (message );
} catch (JSONException e ) {
e .printStackTrace ();
}
};
private JSONObject cacheMultipleSocketMsg = null ;
private Emitter .Listener onNotify = args -> {
JSONObject jsonStr = (JSONObject ) args [0 ];
String from = U .getStringJ (jsonStr , "from" );
if (jsonStr .equals (cacheMultipleSocketMsg ) || from .equals (mySocketId )) {
Lg .d (TAG , "ignore Socket Msg in Notify" );
return ;
}
cacheMultipleSocketMsg = jsonStr ;
String type = U .getStringJ (jsonStr , "msgType" );
Lg .d (TAG , "onNotify " + jsonStr );
if (type .equals (notifyTypeGeneral )) {
// if(U.isEmpty(otherUserSocketId))
rejoin (roomID );
events .onIncoming (jsonStr .toString ());
} else if (type .equals (notifyTypeExchange )) {
onExchange .call (args );
} else if (type .equals (notifyTypeCallStatus )) {
events .onCallStatus (jsonStr .toString ());
}
};
private Emitter .Listener onTransport = args -> {
Transport transport = (Transport ) args [0 ];
transport .on (Transport .EVENT_REQUEST_HEADERS , args12 -> {
@ SuppressWarnings ("unchecked" )
Map <String , List <String >> headers = (Map <String , List <String >>) args12 [0 ];
// modify request headers
}).on (Transport .EVENT_RESPONSE_HEADERS , args1 -> {
@ SuppressWarnings ("unchecked" )
Map <String , List <String >> headers = (Map <String , List <String >>) args1 [0 ];
});
};
private void join (String roomID ) {
Lg .d (TAG , "joining to roomId: " + roomID );
if (turnServerCredentials == null ) {
Lg .d (TAG , "turnServerCredentials: null" );
return ;
}
mSocket .emit ("join" , roomID , (Ack ) args12 -> {
Lg .d (TAG_SOCKET_IO , "Join Success to roomId: " + roomID );
Lg .d (TAG , "Join Success to roomId: " + roomID );
JSONArray ack = (JSONArray ) args12 [0 ];
Lg .d (TAG_SOCKET_IO , " Room Member " + ack );
Lg .d (TAG , " socket ack: " + ack );
for (int i = 0 ; i < ack .length (); ++i ) {
try {
String socketId = ack .getString (i );
otherUserSocketId = socketId ;
//Other Users Socket id
Lg .d (TAG_SOCKET_IO , i + " socket: " + socketId );
Lg .d (TAG , i + " socket: " + socketId );
//exchangeMsg(socketId, "Hello");
} catch (JSONException e ) {
e .printStackTrace ();
}
}
final boolean b = ack .length () > 0 ;
// if (signalingParameters != null) { // ignoring non-call room-joins, handling only call-related room joins
if (signalingParameters == null ) {
signalingParameters = new AppRTCClient .SignalingParameters (
RoomParametersFetcher .getIceServers (turnServerCredentials ),
false , "" , "" , "" , null , null );
}
signalingParameters .initiator = this .initiator ; //false; //b && initiator;
//initiator = false;
Lg .d (TAG_SOCKET_IO , "before events.onConnectToRoom: signalingParameters!=null" );
events .onConnectToRoom (signalingParameters );
Lg .d (TAG_SOCKET_IO , "Initiator: " + signalingParameters .initiator );
register (roomID , clientID );
});
}
private void rejoin (String roomID ) {
Lg .d (TAG , "re-joining to roomId: " + roomID );
if (turnServerCredentials == null ) {
Lg .d (TAG , "turnServerCredentials: null" );
return ;
}
mSocket .emit ("join" , roomID , (Ack ) args12 -> {
Lg .d (TAG_SOCKET_IO , "rejoin Success to roomId: " + roomID );
Lg .d (TAG , "rejoin Success to roomId: " + roomID );
JSONArray ack = (JSONArray ) args12 [0 ];
Lg .d (TAG_SOCKET_IO , " rejoin Room Member " + ack );
Lg .d (TAG , "rejoin socket ack: " + ack );
for (int i = 0 ; i < ack .length (); ++i ) {
try {
String socketId = ack .getString (i );
otherUserSocketId = socketId ;
//Other Users Socket id
Lg .d (TAG_SOCKET_IO , i + " socket: " + socketId );
Lg .d (TAG , i + " socket: " + socketId );
//exchangeMsg(socketId, "Hello");
} catch (JSONException e ) {
e .printStackTrace ();
}
}
final boolean b = ack .length () > 0 ;
// if (signalingParameters != null) { // ignoring non-call room-joins, handling only call-related room joins
if (signalingParameters == null ) {
signalingParameters = new AppRTCClient .SignalingParameters (
RoomParametersFetcher .getIceServers (turnServerCredentials ),
false , "" , "" , "" , null , null );
}
signalingParameters .initiator = this .initiator ; //false; //b && initiator;
//initiator = false;
Lg .d (TAG_SOCKET_IO , "before events.onConnectToRoom: signalingParameters!=null" );
//events.onConnectToRoom(signalingParameters);
// }
// else Lg.d(TAG_SOCKET_IO, "events.onConnectToRoom not called signalingParameters==null" );
Lg .d (TAG_SOCKET_IO , "rejoin Initiator: " + signalingParameters .initiator );
register (roomID , clientID );
});
}
private void exchangeMsgSend (String socketId , String msg ) {
Lg .d (TAG_SOCKET_IO , " Exchange to: " + socketId + " Msg Sending: " + msg );
JSONObject msgBody = new JSONObject ();
try {
msgBody .put ("to" , roomID );
msgBody .put ("from" , mySocketId );
msgBody .put ("msgType" , notifyTypeExchange );
msgBody .put ("message" , msg );
} catch (JSONException e ) {
e .printStackTrace ();
}
// if(C.IS_APP_DEBUGGABLE)
// D.showToastShort(ge, "OFFER_SDP_SEND");
Lg .d (TAG_SOCKET_IO , " emit notify: msgBody: " + msgBody );
mSocket .emit ("notify" , msgBody , (Ack ) args12 -> {
//JSONArray ack = (JSONArray) args12[0];
Lg .d (TAG_SOCKET_IO , "Exchange Msg Send: " );
});
}
public void notifyOtherParty (Context context ,
String aptId ,
String callId ,
String roomId ,
String doctorId ,
String patientId ) {
Lg .d (TAG , "notifyOtherParty " + mSocket .id ());
if (mSocket != null && mSocket .connected ()) {
rejoin (roomId );
try {
Lg .d (TAG , "try notifyOtherParty" );
JSONObject jo = new JSONObject ();
jo .put ("to" , roomId );
jo .put ("from" , mySocketId );
jo .put ("msgType" , notifyTypeGeneral );
jo .put ("call_id" , callId );
jo .put ("appointment_id" , aptId );
jo .put ("room_id" , roomId );
jo .put ("caller_id" , C .IS_DOCTOR_APP ? doctorId : patientId );
jo .put ("caller" , P .getFullName (context ));
jo .put ("caller_username" , P .getUserName (context ));
jo .put ("caller_image" , P .getProfilePicUrl (context ));
Lg .d (TAG , "try notifyOtherParty: " + jo .toString ());
mSocket .emit ("notify" , jo , (Ack ) res -> {
Lg .e (TAG , "NOTIFY event response: " + Arrays .toString (res ));
});
Lg .i (TAG , "Thrown NOTIFY event by JSONObject: " + jo );
} catch (Exception e ) {
e .printStackTrace ();
Lg .d (TAG , "catch notifyOtherParty" );
}
}
}
public void disconnect (boolean waitForComplete , GenericCallbacks callbacks ) {
checkIfCalledOnValidThread ();
Lg .d (TAG , "Disconnect WebSocket. State: " + state );
isJoinedOnce = false ;
//channelInstance=null;
if (state == WebSocketConnectionState .REGISTERED ) {
// Send "bye" to WebSocket server.
send ("{\" type\" : \" bye\" }" );
state = WebSocketConnectionState .CONNECTED ;
// Send http DELETE to http WebSocket server.
//sendWSSMessage("DELETE", "");
}
// Close WebSocket in CONNECTED or ERROR states only.
if (state == WebSocketConnectionState .CONNECTED || state == WebSocketConnectionState .ERROR ) {
//ws.disconnect();
mSocket .disconnect (); // FIXME Do not disconnect in case of app-internal calls
state = WebSocketConnectionState .CLOSED ;
// Wait for websocket close event to prevent websocket library from
// sending any pending messages to deleted looper thread.
if (waitForComplete ) {
synchronized (closeEventLock ) {
while (!closeEvent ) {
try {
closeEventLock .wait (CLOSE_TIMEOUT );
callbacks .onSuccess ();
break ;
} catch (InterruptedException e ) {
Lg .e (TAG , "Wait error: " + e .toString ());
callbacks .onError ("Wait error" );
}
}
}
}
} else callbacks .onSuccess ();
Lg .d (TAG , "Disconnecting WebSocket done." );
}
Socket Connection and Join to a Room Diagram
_________________________________________________________________________________________
⎪Patient App | | Server | | Dr App ⎟
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Socket Connection ——————————————▶︎ Socket URL ◀︎————————————— Socket Connection
│ │
▼ ▼
Add Event Listener Add Event Listener
"connect", "connect",
"reconnect", "reconnect",
"notify" "notify"
"leave" "leave"
"disconnect" "disconnect"
│ │
▼ ▼
Join(roomId) —————▶︎ emit "join"——▶︎ Server Join ◀︎————— emit "join"◀︎—— Join(roomId)
| |
▼ ▼
Peer Connection After Join Same Room
_______________________________________________________________________
Patient App | Dr App
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
CallActivity ————————————————————————————————▶︎ Incoming Activity
│
▼
Accept Call
|
▼
Call Activity Open
|
▼
PeerConnectionClient <——Send Offer—————————— PeerConnectionClient
(Set Offer) (Set Candidate) (Create Offer) (Candidate)
|
▼
(Create Answer ) ————Send Answer ——————————> (Set Answer)
|
▼
(Candidate) ————— Send Candidate ———————> (Set Candidate)