mirror of
https://github.com/pierre42100/ComunicAndroid
synced 2025-07-07 18:42:53 +00:00
First call on Android device
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user