First call on Android device

This commit is contained in:
2019-02-26 15:31:32 +01:00
parent 797b0ae09b
commit f08f1940fc
23 changed files with 3209 additions and 14 deletions

View File

@ -0,0 +1,84 @@
package org.communiquons.signalexchangerclient;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Signal exchanger client request
*
* @author Pierre HUBERT
*/
class ClientRequest {
/**
* Contains request information
*/
private JSONObject mList;
/**
* Initialize object
*/
ClientRequest(){
this.mList = new JSONObject();
}
/**
* Add a string to the request
*
* @param name The name of the string to add
* @param value The value of the string to add
* @return This object to help to concatenate requests
*/
ClientRequest addString(String name, String value){
try {
mList.put(name, value);
} catch (JSONException e) {
e.printStackTrace();
throw new RuntimeException("Could not add a string to a JSON object!");
}
return this;
}
/**
* Add a boolean to the request
*
* @param name The name of the string to add
* @param value Boolean value
* @return This object
*/
ClientRequest addBoolean(String name, boolean value){
try {
mList.put(name, value);
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
/**
* Add a JSON object to the request
*
* @param name The name of the field to add
* @param value The object
* @return This object
*/
ClientRequest addJSONObject(String name, JSONObject value){
try {
mList.put(name, value);
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
/**
* Get resulting JSON object
*
* @return Get the resulting JSON object
*/
JSONObject get(){
return mList;
}
}

View File

@ -0,0 +1,76 @@
package org.communiquons.signalexchangerclient;
import android.support.annotation.Nullable;
import org.webrtc.IceCandidate;
import org.webrtc.SessionDescription;
/**
* This interface should be implemented by the classes
* that makes use of the {@link SignalExchangerClient}
* in order to get updated about new information
* availability
*
* @author Pierre HUBERT
*/
public interface SignalExchangerCallback {
/**
* Method called when an error occur
*
* @param msg Message associated to the error
* @param t Optional associated throwable
*/
void onSignalServerError(String msg, @Nullable Throwable t);
/**
* Method called once we are connected to the server
*/
void onConnectedToSignalingServer();
/**
* Method called on ready message callback
*
* @param target_id The ID of the target
* @param number_targets The number of peers who received the message
*/
void onReadyMessageCallback(String target_id, int number_targets);
/**
* Method called when this client receive a new ready message signal
*
* @param source_id The source of the message
*/
void onReadyMessage(String source_id);
/**
* Method called when the client received a signal
*
* @param source_id The source of the signal
* @param signal The signal
*/
void onSignal(String source_id, String signal);
/**
* Send signals callback
*
* @param number_targets The number of targets for the signal
*/
void onSendSignalCallback(int number_targets);
/**
* This method is called once we received a remote Ice Candidate
*
* @param source_id The source of the signal
* @param iceCandidate The candidate itself
*/
void gotRemoteIceCandidate(String source_id, IceCandidate iceCandidate);
/**
* This method is called when we have got a new remote session description
*
* @param source_id The source of the signal
* @param sessionDescription The session description
*/
void gotRemoteSessionDescription(String source_id, SessionDescription sessionDescription);
}

View File

@ -0,0 +1,333 @@
package org.communiquons.signalexchangerclient;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.IceCandidate;
import org.webrtc.SessionDescription;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
/**
* Signal exchanger client
*
* @author Pierre HUBERT
*/
public class SignalExchangerClient extends WebSocketListener {
/**
* Debug log
*/
private static final String TAG = SignalExchangerClient.class.getSimpleName();
/**
* Instance configuration
*/
private SignalExchangerInitConfig mConfig;
/**
* Signal exchanger callback
*/
@Nullable
private SignalExchangerCallback mCallback;
/**
* Http Client
*/
private OkHttpClient mClient;
/**
* Current WebSocket connection
*/
private WebSocket mWebSocket;
/**
* Initialize a SignalExchanger client
*
* @param config Configuration of the client
* @param cb Callback function to call when we got information update
*/
public SignalExchangerClient(@NonNull SignalExchangerInitConfig config,
@Nullable SignalExchangerCallback cb){
//Save configuration
this.mConfig = config;
this.mCallback = cb;
//Connect to the WebSocket
String url = (config.isSecure() ? "wss" : "ws")
+ "://" + config.getDomain() + ":" + config.getPort() + "/socket";
mClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
mWebSocket = mClient.newWebSocket(request, this);
}
/**
* Get current client configuration
*
* @return Configuration of the client
*/
public SignalExchangerInitConfig getConfig() {
return mConfig;
}
/**
* Set the callback to use on new updates
*
* @param mCallback Callback to use
*/
public void setCallback(@Nullable SignalExchangerCallback mCallback) {
this.mCallback = mCallback;
}
/**
* Check out whether the current client is connected to a server or not
*
* @return true if the client is connected to a server / false else
*/
public boolean isConnected(){
return mWebSocket != null;
}
/**
* Send ready message to a client
*
* @param target_client_id The ID of the target client
*/
public void sendReadyMessage(String target_client_id){
sendData(new ClientRequest()
.addBoolean("ready_msg", true)
.addString("target_id", target_client_id));
}
/**
* Send a signal to a target
*
* @param target_id The ID of the target
* @param signal The signal to send
*/
public void sendSignal(String target_id, String signal){
sendData(new ClientRequest()
.addString("target_id", target_id)
.addString("signal", signal));
}
/**
* Send a session description to a target
*
* @param target_id The ID of the target
* @param description The description
*/
public void sendSessionDescription(String target_id, SessionDescription description){
try {
JSONObject object = new JSONObject();
object.put("type", description.type.canonicalForm());
object.put("sdp", description.description);
sendSignal(target_id, object.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* Send an Ice Candidate to a remote peer
*
* @param target_id The ID of the target
* @param candidate The candidate to send
*/
public void sendIceCandidate(String target_id, IceCandidate candidate){
try {
JSONObject candidateObj = new JSONObject();
candidateObj.put("sdpMid", candidate.sdpMid);
candidateObj.put("sdpMLineIndex", candidate.sdpMLineIndex);
candidateObj.put("candidate", candidate.sdp);
JSONObject object = new JSONObject();
object.put("candidate", candidateObj);
sendSignal(target_id, object.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* Send data to the server
*
* @param request The data to send to the server
*/
private void sendData(@NonNull ClientRequest request){
//Continues only in case of active connection
if(!isConnected()) {
return;
}
//Send data to the server
Log.v(TAG, "Sending " + request.get().toString());
mWebSocket.send(request.get().toString());
}
/**
* Invoked when a web socket has been accepted by the remote peer and may begin transmitting
* messages.
*/
public void onOpen(WebSocket webSocket, Response response) {
//Save WebSocket object
this.mWebSocket = webSocket;
//Send the ID of current client to the server
sendData(new ClientRequest()
.addString("client_id", mConfig.getClientID()));
//Inform we are connected
if(mCallback != null)
mCallback.onConnectedToSignalingServer();
}
/** Invoked when a text (type {@code 0x1}) message has been received. */
@Override
public void onMessage(WebSocket webSocket, String text) {
Log.v(TAG, "Received new message from server: " + text);
//Decode message
try {
JSONObject message = new JSONObject(text);
//Ready message callback
if(message.has("ready_message_sent")){
if(mCallback != null)
mCallback.onReadyMessageCallback(
message.getString("target_id"),
message.getInt("number_of_targets")
);
}
//Ready message
else if(message.has("ready_msg")){
if(mCallback != null)
mCallback.onReadyMessage(
message.getString("source_id")
);
}
//Signal
else if(message.has("signal")) {
if(mCallback != null)
mCallback.onSignal(
message.getString("source_id"),
message.getString("signal")
);
processReceivedSignal(message.getString("source_id"),
message.getString("signal"));
}
//Send signal callback
else if(message.has("signal_sent")){
if(mCallback != null)
mCallback.onSendSignalCallback(
message.getInt("number_of_targets")
);
}
//Success message
else if(message.has("success"))
Log.v(TAG, "Success: " + message.getString("success"));
//Unrecognized message
else
Log.e(TAG, "Message from server not understood!");
} catch (JSONException e) {
e.printStackTrace();
if(mCallback != null)
mCallback.onSignalServerError("Could not parse response from server!", e);
}
}
/**
* Process a received signal
*
* @param source_id The source of the signal
* @param signal The signal to process
*/
private void processReceivedSignal(String source_id, String signal) throws JSONException {
JSONObject object = new JSONObject(signal);
//Ice candidate
if(object.has("candidate")) {
JSONObject candidate = object.getJSONObject("candidate");
if (mCallback != null)
mCallback.gotRemoteIceCandidate(
source_id, new IceCandidate(
candidate.getString("sdpMid"),
candidate.getInt("sdpMLineIndex"),
candidate.getString("candidate")
)
);
}
//Sdp signal
else if(object.has("sdp") && object.has("type")){
SessionDescription.Type type = SessionDescription.Type.fromCanonicalForm(
object.getString("type"));
String sdp = object.getString("sdp");
if(mCallback != null)
mCallback.gotRemoteSessionDescription(source_id,
new SessionDescription(type, sdp));
}
else
Log.e(TAG, "Could not understand received signal!");
}
/**
* Invoked when both peers have indicated that no more messages will be transmitted and the
* connection has been successfully released. No further calls to this listener will be made.
*/
public void onClosed(WebSocket webSocket, int code, String reason) {
mWebSocket = null;
}
/**
* Invoked when a web socket has been closed due to an error reading from or writing to the
* network. Both outgoing and incoming messages may have been lost. No further calls to this
* listener will be made.
*/
public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
if(mCallback != null)
mCallback.onSignalServerError(t.getMessage(), t);
}
}

View File

@ -0,0 +1,58 @@
package org.communiquons.signalexchangerclient;
/**
* Signal exchanger configuration intialization
*
* @author Pierre HUBERT
*/
public class SignalExchangerInitConfig {
//Private fields
private String domain;
private int port;
private String clientID;
private boolean isSecure;
public SignalExchangerInitConfig() {
}
public SignalExchangerInitConfig(String domain, int port, String clientID, boolean isSecure) {
this.domain = domain;
this.port = port;
this.clientID = clientID;
this.isSecure = isSecure;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getClientID() {
return clientID;
}
public void setClientID(String clientID) {
this.clientID = clientID;
}
public boolean isSecure() {
return isSecure;
}
public void setSecure(boolean secure) {
isSecure = secure;
}
}