I am using the following open-source webrtc android application:
https://github.com/pchab/AndroidRTC
I have just modified this application to use my socket.io server instead of using the following one which is given by same author:
https://github.com/pchab/ProjectRTC
To do this, I needed to do some changes in the two classes of the above AndroidRTC Application. After this, when I started the application it did not call the 'createOffer()' or 'createAnswer()' function which is part of libjingle_peerconnection library. I am confused whether these two functions are not getting called or they are not able to use 'sendMessage()' function.
From debugging, I came to know that line which calls 'createAnswer()' function is successfully reached. After this, I expect the 'createAnswer()' function to use my 'sendMessage()' function to send the answer back to other party by using my socket.io server. I am not able to peek inside this 'createAnswer()' function as it is part of the library.
Before changing the above application to use my own server, I had tested it with the server given by auhtor. It ran successfully. I don't know what is wrong when I use my own server to make calls and do handshaking. I just modified few lines to support the way I do signalling on the server.
My server code is already used for webrtc web application. Web Applications are successful in making calls using this server. It should work for this android application too with little modification on the application.
I modified the following two classes in android application:
RTCActivity.java
package fr.pchab.AndroidRTC;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Window;
import android.widget.Toast;
import org.json.JSONException;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.VideoRenderer;
import java.util.List;
public class RTCActivity extends Activity implements WebRtcClient.RTCListener{
private final static int VIDEO_CALL_SENT = 666;
private VideoStreamsView vsv;
private WebRtcClient client;
private String mSocketAddress;
private String callerId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mSocketAddress = "https://" + getResources().getString(R.string.host);
mSocketAddress += (":"+getResources().getString(R.string.port)+"/");
PeerConnectionFactory.initializeAndroidGlobals(this);
// Camera display view
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
vsv = new VideoStreamsView(this, displaySize);
client = new WebRtcClient(this, mSocketAddress);
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
final List<String> segments = intent.getData().getPathSegments();
callerId = segments.get(0);
}
}
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
#Override
public void onPause() {
super.onPause();
vsv.onPause();
}
#Override
public void onResume() {
super.onResume();
vsv.onResume();
}
#Override
public void onCallReady(String callId) {
startCam();
}
public void answer(String callerId) throws JSONException {
client.sendMessage(callerId, "init", null);
startCam();
}
public void call(String callId) {
Intent msg = new Intent(Intent.ACTION_SEND);
msg.putExtra(Intent.EXTRA_TEXT, mSocketAddress + callId);
msg.setType("text/plain");
startActivityForResult(Intent.createChooser(msg, "Call someone :"), VIDEO_CALL_SENT);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == VIDEO_CALL_SENT) {
startCam();
}
}
public void startCam() {
setContentView(vsv);
// Camera settings
client.setCamera("front", "640", "480");
client.start("android_test", true);
}
#Override
public void onStatusChanged(final String newStatus) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), newStatus, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onLocalStream(MediaStream localStream) {
localStream.videoTracks.get(0).addRenderer(new VideoRenderer(new VideoCallbacks(vsv, 0)));
}
#Override
public void onAddRemoteStream(MediaStream remoteStream, int endPoint) {
remoteStream.videoTracks.get(0).addRenderer(new VideoRenderer(new VideoCallbacks(vsv, endPoint)));
vsv.shouldDraw[endPoint] = true;
}
#Override
public void onRemoveRemoteStream(MediaStream remoteStream, int endPoint) {
remoteStream.videoTracks.get(0).dispose();
vsv.shouldDraw[endPoint] = false;
}
// Implementation detail: bridge the VideoRenderer.Callbacks interface to the
// VideoStreamsView implementation.
private class VideoCallbacks implements VideoRenderer.Callbacks {
private final VideoStreamsView view;
private final int stream;
public VideoCallbacks(VideoStreamsView view, int stream) {
this.view = view;
this.stream = stream;
}
#Override
public void setSize(final int width, final int height) {
view.queueEvent(new Runnable() {
public void run() {
view.setSize(stream, width, height);
}
});
}
#Override
public void renderFrame(VideoRenderer.I420Frame frame) {
view.queueFrame(stream, frame);
}
}
}
WebRTCClient.java
package fr.pchab.AndroidRTC;
import java.util.HashMap;
import java.util.LinkedList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import android.os.Handler;
import android.util.Log;
import com.koushikdutta.async.http.socketio.Acknowledge;
import com.koushikdutta.async.http.socketio.ConnectCallback;
import com.koushikdutta.async.http.socketio.EventCallback;
import com.koushikdutta.async.http.socketio.SocketIOClient;
class WebRtcClient {
private final static int MAX_PEER = 2;
private boolean[] endPoints = new boolean[MAX_PEER];
private PeerConnectionFactory factory;
private HashMap<String, Peer> peers = new HashMap<String, Peer>();
private LinkedList<PeerConnection.IceServer> iceServers = new LinkedList<PeerConnection.IceServer>();
private MediaConstraints pcConstraints = new MediaConstraints();
private MediaStream lMS;
private RTCListener mListener;
private SocketIOClient client;
private final MessageHandler messageHandler = new MessageHandler();
private final static String TAG = WebRtcClient.class.getCanonicalName();
public interface RTCListener{
void onCallReady(String callId);
void onStatusChanged(String newStatus);
void onLocalStream(MediaStream localStream);
void onAddRemoteStream(MediaStream remoteStream, int endPoint);
void onRemoveRemoteStream(MediaStream remoteStream, int endPoint);
}
private interface Command{
void execute(String peerId, JSONObject payload) throws JSONException;
}
private class CreateOfferCommand implements Command{
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.d(TAG,"CreateOfferCommand");
Peer peer = peers.get(peerId);
peer.pc.createOffer(peer, pcConstraints);
}
}
private class CreateAnswerCommand implements Command{
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.d(TAG,"CreateAnswerCommand");
Peer peer = peers.get(peerId);
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
peer.pc.createAnswer(peer, pcConstraints);
}
}
private class SetRemoteSDPCommand implements Command{
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.d(TAG,"SetRemoteSDPCommand");
Peer peer = peers.get(peerId);
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
}
}
private class AddIceCandidateCommand implements Command{
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.d(TAG,"AddIceCandidateCommand");
PeerConnection pc = peers.get(peerId).pc;
if (pc.getRemoteDescription() != null) {
IceCandidate candidate = new IceCandidate(
payload.getString("id"),
payload.getInt("label"),
payload.getString("candidate")
);
pc.addIceCandidate(candidate);
}
}
}
public void sendMessage(String to, String type, JSONObject payload) throws JSONException {
JSONObject message = new JSONObject();
//message.put("room", to);
message.put("type", type);
message.put("msg", payload);
message.put("room", "sojharo");
client.emit("message", new JSONArray().put(message));
}
private class MessageHandler implements EventCallback {
private HashMap<String, Command> commandMap;
public MessageHandler() {
this.commandMap = new HashMap<String, Command>();
commandMap.put("init", new CreateOfferCommand());
commandMap.put("offer", new CreateAnswerCommand());
commandMap.put("answer", new SetRemoteSDPCommand());
commandMap.put("candidate", new AddIceCandidateCommand());
}
#Override
public void onEvent(String s, JSONArray jsonArray, Acknowledge acknowledge) {
try {
Log.d(TAG,"MessageHandler.onEvent() "+ (s == null ? "nil" : s));
if(s.equals("id")) {
JSONObject message = new JSONObject();
message.put("room", "sojharo");
message.put("username", "android");
client.emit("create or join livehelp",
new JSONArray().put(message));
} else if (s.equals("joined")) {
mListener.onCallReady("Not Initiator");
} else {
JSONObject json = jsonArray.getJSONObject(0);
try{
if(json.getString("msg").equals("got user media"))
return ;
}catch(JSONException e){}
String from = json.getString("from");
String type = null;
try{
type = json.getString("type");
}catch(JSONException e){}
// if peer is unknown, try to add him
if(!peers.containsKey(from)) {
// if MAX_PEER is reach, ignore the call
int endPoint = findEndPoint();
if(endPoint != MAX_PEER) {
addPeer(from, endPoint);
commandMap.get(type).execute(from, json);
}
} else {
commandMap.get(type).execute(from, json);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private class Peer implements SdpObserver, PeerConnection.Observer{
private PeerConnection pc;
private String id;
private int endPoint;
#Override
public void onCreateSuccess(final SessionDescription sdp) {
try {
JSONObject payload = new JSONObject();
payload.put("type", sdp.type.canonicalForm());
payload.put("sdp", sdp.description);
sendMessage(id, sdp.type.canonicalForm(), payload);
pc.setLocalDescription(Peer.this, sdp);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onSetSuccess() {}
#Override
public void onCreateFailure(String s) {}
#Override
public void onSetFailure(String s) {}
#Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {}
#Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
if(iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
removePeer(id);
mListener.onStatusChanged("DISCONNECTED");
}
}
#Override
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {}
#Override
public void onIceCandidate(final IceCandidate candidate) {
try {
JSONObject payload = new JSONObject();
payload.put("label", candidate.sdpMLineIndex);
payload.put("id", candidate.sdpMid);
payload.put("candidate", candidate.sdp);
sendMessage(id, "candidate", payload);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onError() {}
#Override
public void onAddStream(MediaStream mediaStream) {
Log.d(TAG,"onAddStream "+mediaStream.label());
// remote streams are displayed from 1 to MAX_PEER (0 is localStream)
mListener.onAddRemoteStream(mediaStream, endPoint+1);
}
#Override
public void onRemoveStream(MediaStream mediaStream) {
mListener.onRemoveRemoteStream(mediaStream, endPoint);
removePeer(id);
}
#Override
public void onDataChannel(DataChannel dataChannel) {}
public Peer(String id, int endPoint) {
Log.d(TAG,"new Peer: "+id + " " + endPoint);
this.pc = factory.createPeerConnection(iceServers, pcConstraints, this);
this.id = id;
this.endPoint = endPoint;
pc.addStream(lMS, new MediaConstraints());
mListener.onStatusChanged("CONNECTING");
}
}
public WebRtcClient(RTCListener listener, String host) {
mListener = listener;
factory = new PeerConnectionFactory();
SocketIOClient.connect(host, new ConnectCallback() {
#Override
public void onConnectCompleted(Exception ex, SocketIOClient socket) {
if (ex != null) {
Log.e(TAG,"WebRtcClient connect failed: "+ex.getMessage());
return;
}
Log.d(TAG,"WebRtcClient connected.");
client = socket;
// specify which events you are interested in receiving
client.addListener("id", messageHandler);
client.addListener("message", messageHandler);
client.addListener("joined", messageHandler);
}
}, new Handler());
iceServers.add(new PeerConnection.IceServer("stun:23.21.150.121"));
iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
}
public void setCamera(String cameraFacing, String height, String width){
MediaConstraints videoConstraints = new MediaConstraints();
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", height));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", width));
VideoSource videoSource = factory.createVideoSource(getVideoCapturer(cameraFacing), videoConstraints);
lMS = factory.createLocalMediaStream("ARDAMS");
lMS.addTrack(factory.createVideoTrack("ARDAMSv0", videoSource));
lMS.addTrack(factory.createAudioTrack("ARDAMSa0"));
mListener.onLocalStream(lMS);
}
private int findEndPoint() {
for(int i = 0; i < MAX_PEER; i++) {
if(!endPoints[i]) return i;
}
return MAX_PEER;
}
public void start(String name, boolean privacy){
try {
JSONObject message = new JSONObject();
message.put("msg", new JSONObject().put("msg", "got user media"));
message.put("room", "sojharo");
client.emit("message", new JSONArray().put(message));
} catch (JSONException e) {
e.printStackTrace();
}
}
/*
Cycle through likely device names for the camera and return the first
capturer that works, or crash if none do.
*/
private VideoCapturer getVideoCapturer(String cameraFacing) {
int[] cameraIndex = { 0, 1 };
int[] cameraOrientation = { 0, 90, 180, 270 };
for (int index : cameraIndex) {
for (int orientation : cameraOrientation) {
String name = "Camera " + index + ", Facing " + cameraFacing +
", Orientation " + orientation;
VideoCapturer capturer = VideoCapturer.create(name);
if (capturer != null) {
return capturer;
}
}
}
throw new RuntimeException("Failed to open capturer");
}
private void addPeer(String id, int endPoint) {
Peer peer = new Peer(id, endPoint);
peers.put(id, peer);
endPoints[endPoint] = true;
}
private void removePeer(String id) {
Peer peer = peers.get(id);
peer.pc.close();
peer.pc.dispose();
peers.remove(peer.id);
endPoints[peer.endPoint] = false;
}
}
The code is able to receive the offer and candidates from other party. It is not able to send the answer or candidates to that party in return.
I have not modified other two classes which can be found on the above link for android application.
Here is snippet of my socket.io server code written in nodejs:
socket.on('create or join livehelp', function (room) {
var numClients = socketio.sockets.clients(room.room).length;
if (numClients === 0){
socket.join(room.room);
socket.set('nickname', room.username);
socket.emit('created', room);
} else if (numClients < 2) {
socket.join(room.room);
socket.set('nickname', room.username);
socket.emit('joined', room);
socket.broadcast.to(room.room).emit('join', room);
} else { // max three clients
socket.emit('full', room.room);
}
console.log(socketio.sockets.manager.rooms)
console.log(room)
});
socket.on('message', function (message) {
//console.log('Got message:', message);
//socket.broadcast.emit('message', message);
message.msg.from = socket.id;
//socketio.sockets.in(message.room).emit('message', message.msg);
socket.broadcast.to(message.room).emit('message', message.msg);
//console.log('Got message:', message.msg);
//console.log(socketio.sockets.manager.rooms)
});
I am confused if there is any error why I am not able to find it in debugging. Log for this is very difficult to read as it runs very fast and I am not able to catch each and every line. But apparently, it looked fine at a glance.
Please help. Thanks.
I think you are not able to generate answer but you are able to generate offer?. If this is the case try adding
pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
to your pc constraints.
Hope this will help..
client.on('message', function (details) {
console.log('message',details.to);
console.log(details.type);
if(details.type !== 'init'){
var otherClient = io.sockets.connected[details.to];
if (!otherClient) {
return;
}
delete details.to;
details.from = client.id;
otherClient.emit('message', details);
}
else
{
if (io.sockets.adapter.rooms[client.room] !== undefined ) {
for(var member in io.sockets.adapter.rooms[client.room]){
console.log(member);
if(member !== client.id){
var otherClient = io.sockets.connected[member];
if (!otherClient) {
return;
}
delete details.to;
details.from = client.id;
otherClient.emit('message', details);
}
else{
console.log("no need to send self again!");
}
}
} else {
client.emit("update", "Please connect to a room.");
}
}
});
Please download latest libjingle from here
http://repo.spring.io/libs-release-remote/io/pristine/libjingle/
Related
I am trying to send messages through the UDP communication of an android phone a UDP host, according to the buttons on the screen send a specific message, I have a UDP_Service class where I instantiate the datagramsocket and do the corresponding sending:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDP_Service {
private int server_port;
DatagramSocket skt;
private InetAddress local;
private String msge;
private String st;
private int msge_lenght;
private byte[] byteMsge;
private byte[] resp;
private int j;
public UDP_Service(){
try {
skt = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
// st = null;
}
}
public void setIP(String ip){
try {
local = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
// st = null;
}
}
public void setPort(int np){
server_port = np;
}
public void setMsge(String msj){
msge = msj;
msge_lenght = msge.length();
byteMsge = msge.getBytes();
resp = new byte[1024];
}
public void Enviar(){
try {
skt = new DatagramSocket();
DatagramPacket pqtEnvio = new DatagramPacket(byteMsge, msge_lenght,
local, server_port);
skt.send(pqtEnvio);
DatagramPacket pqtResp = new DatagramPacket(resp, resp.length);
skt.receive(pqtResp);
st = new String(pqtResp.getData(),0,pqtResp.getLength());
//skt.close();
} catch (Exception e) {
// st = null;
}
}
public void close() {
skt.close();
}
public String getRespuesta(){
return st;
}
public int getCont() {
return j;
}
public void setCont(int x) {
j = x;
}
}
The method "Enviar()" I invoke it from an AsyncTask, however it only works once, I open the application from the phone and press any button and effectively the corresponding message is sent, but when I press another button no longer nothing happens. The onPostExecute () method of the AsyncTask is not running,I can not see the corresponding toast.
Here the AsyncTask
public class MainActivity extends AppCompatActivity {
String msje[];
String resp[];
int port;
UDP_Service UDP_Serv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UDP_Serv = new UDP_Service();
//UDP_A = new UDP_Async();
msje = new String[9];
resp = new String[8];
}
public void UDP_Client(String mje, int pt, String ip, int c)
{
//UDP_A = new UDP_Async();
UDP_Serv.setMsge(mje);
UDP_Serv.setPort(pt);
UDP_Serv.setIP(ip);
UDP_Serv.setCont(c);
new UDP_Async().execute();
Toast.makeText(MainActivity.this, mje, Toast.LENGTH_SHORT).show();
}
public class UDP_Async extends AsyncTask < Void, Void, Boolean > {
private String st;
private int j;
#Override
protected Boolean doInBackground(Void...params) {
UDP_Serv.Enviar();
st = UDP_Serv.getRespuesta();
j = UDP_Serv.getCont();
//return st;
return true;
}
#Override
protected void onPostExecute(Boolean result) {
//super.onPostExecute(result);
if (result) {
if (!st.equals(null)) {
resp[j] = st;
actualizarUI(j);
}
Toast.makeText(MainActivity.this, "TAREA FINALIZADA!", Toast.LENGTH_SHORT).show();
}
}
}
}
Hi I'm trying to save the remote stream received from the webrtc, I followed some code sample from git, tried various approach to get remote stream but,not able to get stream from socket or any other way, if some one have any idea please suggest here is snippet of my Class of WebRTCClient
here is my class:
package fr.pchab.webrtcclient;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast;
import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoCapturerAndroid;
import org.webrtc.VideoSource;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
public class WebRtcClient {
private final static String TAG = "WebRtcClient";
private final static int MAX_PEER = 2;
private boolean[] endPoints = new boolean[MAX_PEER];
private PeerConnectionFactory factory;
private HashMap<String, Peer> peers = new HashMap<>();
private LinkedList<PeerConnection.IceServer> iceServers = new LinkedList<>();
private PeerConnectionParameters pcParams;
private MediaConstraints pcConstraints = new MediaConstraints() {
};
private MediaStream localMS;
private VideoSource videoSource;
private RtcListener mListener;
private Socket client;
/**
* Implement this interface to be notified of events.
*/
public interface RtcListener {
void onCallReady(String callId);
void onStatusChanged(String newStatus);
void onLocalStream(MediaStream localStream);
void onAddRemoteStream(MediaStream remoteStream, int endPoint);
void onRemoveRemoteStream(int endPoint);
}
private interface Command {
void execute(String peerId, JSONObject payload) throws JSONException;
}
private class CreateOfferCommand implements Command {
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.e(TAG, "CreateOfferCommand");
Peer peer = peers.get(peerId);
peer.pc.createOffer(peer, pcConstraints);
}
}
private class CreateAnswerCommand implements Command {
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.e(TAG, "CreateAnswerCommand");
Peer peer = peers.get(peerId);
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
peer.pc.createAnswer(peer, pcConstraints);
}
}
private class SetRemoteSDPCommand implements Command {
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.e(TAG, "SetRemoteSDPCommand");
Peer peer = peers.get(peerId);
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
}
}
private class AddIceCandidateCommand implements Command {
public void execute(String peerId, JSONObject payload) throws JSONException {
Log.e(TAG, "AddIceCandidateCommand");
PeerConnection pc = peers.get(peerId).pc;
if (pc.getRemoteDescription() != null) {
IceCandidate candidate = new IceCandidate(
payload.getString("id"),
payload.getInt("label"),
payload.getString("candidate")
);
pc.addIceCandidate(candidate);
}
}
}
/**
* Send a message through the signaling server
*
* #param to id of recipient
* #param type type of message
* #param payload payload of message
* #throws JSONException
*/
public void sendMessage(String to, String type, JSONObject payload) throws JSONException {
JSONObject message = new JSONObject();
message.put("to", to);
message.put("type", type);
message.put("payload", payload);
client.emit("message", message);
}
private class MessageHandler {
private HashMap<String, Command> commandMap;
private MessageHandler() {
this.commandMap = new HashMap<>();
commandMap.put("init", new CreateOfferCommand());
commandMap.put("offer", new CreateAnswerCommand());
commandMap.put("answer", new SetRemoteSDPCommand());
commandMap.put("candidate", new AddIceCandidateCommand());
}
private Emitter.Listener onMessage = new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
try {
String from = data.getString("from");
String type = data.getString("type");
JSONObject payload = null;
if (!type.equals("init")) {
payload = data.getJSONObject("payload");
}
// if peer is unknown, try to add him
if (!peers.containsKey(from)) {
// if MAX_PEER is reach, ignore the call
int endPoint = findEndPoint();
if (endPoint != MAX_PEER) {
Peer peer = addPeer(from, endPoint);
peer.pc.addStream(localMS);
commandMap.get(type).execute(from, payload);
}
} else {
commandMap.get(type).execute(from, payload);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
private Emitter.Listener onId = new Emitter.Listener() {
#Override
public void call(Object... args) {
String id = (String) args[0];
mListener.onCallReady(id);
}
};
}
private class Peer implements SdpObserver, PeerConnection.Observer, DataChannel.Observer {
private PeerConnection pc;
private String id;
private int endPoint;
#Override
public void onCreateSuccess(final SessionDescription sdp) {
// TODO: modify sdp to use pcParams prefered codecs
JSONObject payload = null;
try {
payload = new JSONObject();
payload.put("type", sdp.type.canonicalForm());
payload.put("sdp", sdp.description);
sendMessage(id, sdp.type.canonicalForm(), payload);
pc.setLocalDescription(Peer.this, sdp);
} catch (JSONException e) {
e.printStackTrace();
}
Log.e("WebRtcClient", "WebRtcClient onCreateSuccess:" + payload);
}
#Override
public void onSetSuccess() {
Log.e("WebRtcClient", "WebRtcClient onSetSuccess:");
}
#Override
public void onCreateFailure(String s) {
Log.e("WebRtcClient", "WebRtcClient onCreateFailure:" + s);
}
#Override
public void onSetFailure(String s) {
Log.e("WebRtcClient", "WebRtcClient onSetFailure:" + s);
}
#Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
Log.e("WebRtcClient", "WebRtcClient onSignalingChange:" + signalingState.name());
}
#Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
removePeer(id);
mListener.onStatusChanged("DISCONNECTED");
} else if (iceConnectionState == PeerConnection.IceConnectionState.CONNECTED) {
}
Log.e("WebRtcClient", "WebRtcClient onIceConnectionChange:" + iceConnectionState);
}
#Override
public void onIceConnectionReceivingChange(boolean b) {
Log.e("WebRtcClient", "WebRtcClient onIceConnectionReceivingChange:" + b);
}
#Override
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
Log.e("WebRtcClient", "WebRtcClient onIceGatheringChange:" + iceGatheringState);
}
#Override
public void onIceCandidate(final IceCandidate candidate) {
JSONObject payload = null;
try {
payload = new JSONObject();
payload.put("label", candidate.sdpMLineIndex);
payload.put("id", candidate.sdpMid);
payload.put("candidate", candidate.sdp);
sendMessage(id, "candidate", payload);
} catch (JSONException e) {
e.printStackTrace();
}
Log.e("WebRtcClient", "WebRtcClient onIceCandidate:" + payload);
}
#Override
public void onAddStream(MediaStream mediaStream) {
Log.e(TAG, "onAddStream " + mediaStream.label());
// remote streams are displayed from 1 to MAX_PEER (0 is localStream)
mListener.onAddRemoteStream(mediaStream, endPoint + 1);
Log.e("WebRtcClient", "WebRtcClient onAddStream:" + mediaStream.label());
}
#Override
public void onRemoveStream(MediaStream mediaStream) {
Log.e(TAG, "onRemoveStream " + mediaStream.label());
Log.e("WebRtcClient", "WebRtcClient onRemoveStream:" + mediaStream.label());
removePeer(id);
}
#Override
public void onRenegotiationNeeded() {
Log.e("WebRtcClient", "WebRtcClient onRenegotiationNeeded:");
}
#Override
public void onDataChannel(final DataChannel dataChannel) {
Log.e("onDataChannel", "onDataChannel:" + dataChannel.label());
dataChannel.registerObserver(this);
}
#Override
public void onMessage(DataChannel.Buffer buffer) {
ByteBuffer data = buffer.data;
byte[] bytes = new byte[data.remaining()];
data.get(bytes);
String command = new String(bytes);
Log.e(TAG, " onDataChannel-DcObserver " + command);
}
#Override
public void onStateChange() {
Log.e(TAG, "onDataChannel -DcObserver " + "onStateChange");
}
#Override
public void onBufferedAmountChange(long arg0) {
Log.e(TAG, " DcObserver " + arg0);
}
public Peer(String id, int endPoint) {
Log.e(TAG, "WebRtcClient new Peer: " + id + " " + endPoint);
this.pc = factory.createPeerConnection(iceServers, pcConstraints, this);
createDataChannel();
this.id = id;
this.endPoint = endPoint;
pc.addStream(localMS); //, new MediaConstraints()
mListener.onStatusChanged("CONNECTING");
}
private void createDataChannel() {
DataChannel.Init dcInit = new DataChannel.Init();
dcInit.id = 1;
dataChannel = pc.createDataChannel("sendDataChannel", dcInit);
dataChannel.registerObserver(this);
}
}
DataChannel dataChannel;
private Peer addPeer(String id, int endPoint) {
Peer peer = new Peer(id, endPoint);
peers.put(id, peer);
endPoints[endPoint] = true;
return peer;
}
private void removePeer(String id) {
Peer peer = peers.get(id);
mListener.onRemoveRemoteStream(peer.endPoint);
peer.pc.close();
peers.remove(peer.id);
endPoints[peer.endPoint] = false;
}
private Activity mContext;
public WebRtcClient(RtcListener listener, String host, PeerConnectionParameters params, Activity mContext) {
mListener = listener;
pcParams = params;
this.mContext = mContext;
PeerConnectionFactory.initializeAndroidGlobals(listener, true, true, true
/*params.videoCodecHwAcceleration, mEGLcontext*/);
factory = new PeerConnectionFactory();
MessageHandler messageHandler = new MessageHandler();
try {
client = IO.socket(host);
} catch (URISyntaxException e) {
e.printStackTrace();
}
client.on("id", messageHandler.onId);
client.on("message", messageHandler.onMessage);
client.connect();
iceServers.add(new PeerConnection.IceServer("stun:23.21.150.121"));
iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
// pcConstraints.optional.add(new MediaConstraints.KeyValuePair("RtpDataChannels", "false"));
}
/**
* Call this method in Activity.onPause()
*/
public void onPause() {
if (videoSource != null) videoSource.stop();
}
/**
* Call this method in Activity.onResume()
*/
public void onResume() {
if (videoSource != null) videoSource.restart();
}
/**
* Call this method in Activity.onDestroy()
*/
public void onDestroy() {
for (Peer peer : peers.values()) {
peer.pc.dispose();
}
videoSource.dispose();
factory.dispose();
client.disconnect();
client.close();
}
private int findEndPoint() {
for (int i = 0; i < MAX_PEER; i++) if (!endPoints[i]) return i;
return MAX_PEER;
}
/**
* Start the client.
* <p>
* Set up the local stream and notify the signaling server.
* Call this method after onCallReady.
*
* #param name client name
*/
public void start(String name) {
setCamera();
try {
JSONObject message = new JSONObject();
message.put("name", name);
client.emit("readyToStream", message);
} catch (JSONException e) {
e.printStackTrace();
}
}
private void setCamera() {
localMS = factory.createLocalMediaStream("ARDAMS");
if (pcParams.videoCallEnabled) {
MediaConstraints videoConstraints = new MediaConstraints();
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", Integer.toString(pcParams.videoHeight)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", Integer.toString(pcParams.videoWidth)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxFrameRate", Integer.toString(pcParams.videoFps)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minFrameRate", Integer.toString(pcParams.videoFps)));
VideoCapturer videoCapturer = getVideoCapturer();
if (videoCapturer != null) {
videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
localMS.addTrack(factory.createVideoTrack("ARDAMSv0", videoSource));
} else {
}
}
AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
localMS.addTrack(factory.createAudioTrack("ARDAMSa0", audioSource));
mListener.onLocalStream(localMS);
}
private void showMessage(final String msg) {
mContext.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
});
}
private VideoCapturer getVideoCapturer() {
//Camera name empty will call the back cam bydefualt
return VideoCapturerAndroid.create("", new VideoCapturerAndroid.CameraEventsHandler() {
#Override
public void onCameraError(String s) {
Log.e("WebRtcClient", "WebRtcClient onCameraError:" + s);
}
#Override
public void onCameraFreezed(String s) {
Log.e("WebRtcClient", "WebRtcClient onCameraFreezed:" + s);
}
#Override
public void onCameraOpening(int i) {
Log.e("WebRtcClient", "WebRtcClient onCameraOpening:" + i);
// showMessage("Opening Camera id " + i);
}
#Override
public void onFirstFrameAvailable() {
Log.e("WebRtcClient", "WebRtcClient onFirstFrameAvailable:");
// showMessage("Camera onFirstFrameAvailable:");
}
#Override
public void onCameraClosed() {
Log.e("WebRtcClient", "WebRtcClient onCameraClosed:");
showMessage("Camera onCameraClosed");
}
});
}
}
Referenced from link
Currently there is no option to save remote media stream on android.You will have to implement a media server which would save media stream and pass stream between two devices. Kurento is an open source media server which I know provides the functionality but I haven't used it
Version 4.2 of the Smack library seems to drop the message body, as sent by FCM.
When I set the debugger on, I can see the following incoming message:
<message><data:gcm xmlns:data="google:mobile:data">{"message_type":"nack","from":"xxx","message_id":"aaaa1","error":"BAD_REGISTRATION","error_description":""}</data:gcm></message>
However, when Smack 4.2 parses this message, it drops the JSON body inside the message and gives me the following in my packet listener:
<message><gcm xmlns="google:mobile:data"></gcm></message>
Here's my test class:
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
import javax.net.ssl.SSLSocketFactory;
public class CcsClient {
private static final String HOST = "fcm-xmpp.googleapis.com";
private static final int PORT = 5235;
private final XMPPConnection conn;
public CcsClient(String senderId, String serverKey) {
SASLAuthentication.supportSASLMechanism("PLAIN", 0);
ConnectionConfiguration conf = new ConnectionConfiguration(HOST, PORT);
conf.setSASLAuthenticationEnabled(true);
conf.setSocketFactory(SSLSocketFactory.getDefault());
conf.setServiceName(HOST);
conf.setSendPresence(false);
conf.setCompressionEnabled(false);
conf.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
conf.setDebuggerEnabled(true);
this.conn = new XMPPConnection(conf);
conn.addConnectionListener(new AbstractConnectionListener() {
#Override
public void connectionClosed() {
super.connectionClosed();
}
#Override
public void connectionClosedOnError(Exception e) {
super.connectionClosedOnError(e);
}
#Override
public void reconnectingIn(int seconds) {
super.reconnectingIn(seconds);
}
#Override
public void reconnectionFailed(Exception e) {
super.reconnectionFailed(e);
}
#Override
public void reconnectionSuccessful() {
super.reconnectionSuccessful();
}
});
try {
conn.connect();
conn.login(senderId + "#gcm.googleapis.com", serverKey);
System.out.println("connected!");
} catch (XMPPException e) {
e.printStackTrace();
}
}
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super("gcm", "google:mobile:data");
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return "<gcm xmlns=\"google:mobile:data\">" + StringUtils.escapeForXML(json) + "</gcm>";
}
public Message toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
public static void main(String[] args) throws InterruptedException {
final CcsClient c = new CcsClient("xxx", "xxx");
c.conn.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
System.out.println("incoming!" + packet.toString());
}
}, new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
});
for (int i = 0; i < 1; i++) {
c.conn.sendPacket(new GcmPacketExtension("{\"to\":\"xxx\", \"message_id\":\"aaaa"+i+"\", \"delivery_receipt_requested\":true}").toPacket());
}
Thread.sleep(1000000);
}
}
What am I doing wrong?
So I finally got it working. An example client is here: https://gist.github.com/judepereira/fd8dc0a5321179b699f5c5e54812770c
When you first open the app I want a screen where you can enter the broker information and click try and save.
When clicking try it should just show a Snackbar saying if the information makes for a successful connection.
This is the code I call when the try button is pressed:
private void tryConnection(View v){
if(verifyInputs()){
Snackbar.make(v, getString(R.string.trying_connection), Snackbar.LENGTH_LONG).show();
String clientId = MqttClient.generateClientId();
MqttAndroidClient client =
new MqttAndroidClient(this.getApplicationContext(), getServerAddress(),
clientId);
try {
IMqttToken token = client.connect();
final View vinner = v;
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// We are connected
Snackbar.make(vinner, "Success", Snackbar.LENGTH_LONG).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// Something went wrong e.g. connection timeout or firewall problems
Snackbar.make(vinner, "Fail", Snackbar.LENGTH_LONG).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}
}
The problem is, onFailure doesn't seem to be called when it cannot connect to the server, but when a connection to a server is lost.
How do I just test the connection, so I can store it and go back to the main activity?
Ok, so I can't see your full service, any other implementation or how/where you are using this so I'm providing a sample of my MQTT service.
Maybe you can compare it, find any issue and fix it.
Or you can just use my implementation. Up to you. Hope it helps.
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.util.ArrayList;
public class MyMqttService extends Service implements MqttCallback, IMqttActionListener {
private final IBinder binder = new MyBinder();
private MqttAndroidClient mqttClient;
private MqttConnectOptions mqttConnectOptions;
private static final MemoryPersistence persistence = new MemoryPersistence();
private ArrayList<MqttAndroidClient> lostConnectionClients;
private String clientId = "";
private boolean isReady = false;
private boolean doConnectTask = true;
private boolean isConnectInvoked = false;
private Handler handler = new Handler();
private final int RECONNECT_INTERVAL = 10000; // 10 seconds
private final int DISCONNECT_INTERVAL = 20000; // 20 seconds
private final int CONNECTION_TIMEOUT = 60;
private final int KEEP_ALIVE_INTERVAL = 200;
private String broker_url = "my_broker";
public MyMqttService() {}
public class MyBinder extends Binder {
public MyMqttService getService() {
return MyMqttService.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public void onCreate() {
super.onCreate();
initMqttClient();
}
#Override
public void onDestroy() {
super.onDestroy();
disconnectClients();
if (isConnectInvoked && mqttClient != null && mqttClient.isConnected()) {
try {
// unsubscribe here
unsubscribe("¯\\_(ツ)_/¯");
mqttClient.disconnect();
} catch (MqttException e) {
Log.e("TAG", e.toString());
}
}
handler.removeCallbacks(connect);
handler.removeCallbacks(disconnect);
}
private void initMqttClient() {
if(mqttClient != null) {
mqttClient = null;
}
lostConnectionClients = new ArrayList<>();
mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setCleanSession(true);
mqttConnectOptions.setConnectionTimeout(CONNECTION_TIMEOUT);
mqttConnectOptions.setKeepAliveInterval(KEEP_ALIVE_INTERVAL);
setNewMqttClient();
handler.post(connect);
handler.postDelayed(disconnect, DISCONNECT_INTERVAL);
}
private void setNewMqttClient() {
mqttClient = new MqttAndroidClient(MyMqttService.this, broker_url, clientId, persistence);
mqttClient.setCallback(this);
}
public Runnable connect = new Runnable() {
public void run() {
connectClient();
handler.postDelayed(connect, RECONNECT_INTERVAL);
}
};
public Runnable disconnect = new Runnable() {
public void run() {
disconnectClients();
handler.postDelayed(disconnect, DISCONNECT_INTERVAL);
}
};
private void connectClient() {
if(doConnectTask) {
doConnectTask = false;
try {
isConnectInvoked = true;
mqttClient.connect(mqttConnectOptions, null, this);
} catch (MqttException ex) {
doConnectTask = true;
Log.e("TAG", ex.toString());
}
}
}
private void disconnectClients() {
if (lostConnectionClients.size() > 0) {
// Disconnect lost connection clients
for (MqttAndroidClient client : lostConnectionClients) {
if (client.isConnected()) {
try {
client.disconnect();
} catch (MqttException e) {
Log.e("TAG", e.toString());
}
}
}
// Close already disconnected clients
for (int i = lostConnectionClients.size() - 1; i >= 0; i--) {
try {
if (!lostConnectionClients.get(i).isConnected()) {
MqttAndroidClient client = lostConnectionClients.get(i);
client.close();
lostConnectionClients.remove(i);
}
} catch (IndexOutOfBoundsException e) {
Log.e("TAG", e.toString());
}
}
}
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.e("TAG", "deliveryComplete()");
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String payload = new String(message.getPayload());
// do something
}
#Override
public void connectionLost(Throwable cause) {
Log.e("TAG", cause.getMessage());
}
#Override
public void onSuccess(IMqttToken iMqttToken) {
isReady = true;
// subscribe here
subscribe("¯\\_(ツ)_/¯");
}
#Override
public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
setNewMqttClient();
isReady = false;
doConnectTask = true;
isConnectInvoked = false;
}
private void subscribe(String topic) {
try {
mqttClient.subscribe(topic, 0);
isReady = true;
} catch (MqttSecurityException mqttSexEx) {
isReady = false;
} catch (MqttException mqttEx) {
isReady = false;
}
}
private void unsubscribe(String topic) {
try {
mqttClient.unsubscribe(topic);
} catch (MqttSecurityException mqttSecEx) {
Log.e("TAG", mqttSecEx.getMessage());
} catch (MqttException mqttEx) {
Log.e("TAG", mqttEx.getMessage());
}
}
private void publish(String topic, String jsonPayload) {
if(!isReady) {
return;
}
try {
MqttMessage msg = new MqttMessage();
msg.setQos(0);
msg.setPayload(jsonPayload.getBytes("UTF-8"));
mqttClient.publish(topic, msg);
} catch (Exception ex) {
Log.e("TAG", ex.toString());
}
}
}
My other suggestion would be to just setup local broadcast so when your activity loads and you start the service, if MQTT service is able to connect, you send a broadcast saying connected and you show a Snackbar. If connection failed, you send a different broadcast and show a different message.
I am having an app that has multiple activities and uses MQTT.
I am using the paho client in gradle dependencies as follows
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.0.3-SNAPSHOT'
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.0.3-SNAPSHOT'
I would be using username and password to connect to the broker and some activities would be using diffrent user name and password.
currently I am handling the connect,subscribeand publish tasks in each activity as in the code that I will include below. As the activities get more and more I am having issues. Please suggest me how I can port the code to a service or singleton so that it can be reused and become efficient.
Here is one of the activities
package net.kindows.chitchat;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.pixplicity.easyprefs.library.Prefs;
import net.kindows.SplashScreen;
import net.kindows.common.ApplicationLoader;
import net.kindows.common.utils;
import net.kindows.intlPhone.IntlPhoneInput;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import butterknife.Bind;
import butterknife.ButterKnife;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import static net.kindows.common.ApplicationLoader._toast;
public class LoginActivity extends AppCompatActivity implements MqttCallback {
private static final String TAG = "LoginActivity";
private static final int REQUEST_SIGNUP = 0;
private static final int REQUEST_PAS_RESET = 1;
private static final Integer LOGGED_OUT = 0;
private static final Integer LOGGING_IN = 1;
private static final Integer WAITING_FOR_SING_IN_ACK = 2;
private static final Integer LOGGED_IN = 3;
private static final Integer VERIFICATION_FAILED = 4;
#Bind(R.id.input_password)
EditText _passwordText;
#Bind(R.id.btn_login)
Button _loginButton;
#Bind(R.id.link_signup)
TextView _signupLink;
#Bind(R.id.my_phone_input)
IntlPhoneInput _phoneInputView;
String sUserName = null;
String sPassword = null;
String sDestination = null;
String sMessage = null;
private Integer state;
private Handler han = new Handler();
private MqttConnectOptions connOpt;
private ProgressDialog _progressDialog;
/*
MQTT mqtt = null;
FutureConnection connection = null;*/
private boolean isMinimized = false;
private String clientId;
private Handler loginAgain = new Handler();
private Handler timeout;
private MqttAndroidClient client;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//connect();
_loginButton.setEnabled(false);
// _phoneInputView.setNumber(ApplicationLoader.getSim1number(LoginActivity.this));
_phoneInputView.setOnValidityChange(new IntlPhoneInput.IntlPhoneInputListener() {
#Override
public void done(View view, boolean isValid) {
if (isValid) {
_loginButton.setEnabled(true);
} else _loginButton.setEnabled(false);
}
});
_loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!ApplicationLoader.isConnected(LoginActivity.this, true)) {
} else login();
}
});
_signupLink.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Start the Signup activity
Prefs.putInt(getString(R.string.key_reset_pass), 2);
Intent intent = new Intent(getApplicationContext(), SignUpActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
state = LOGGED_OUT;
connOpt = new MqttConnectOptions();
connOpt.setCleanSession(true);
connOpt.setKeepAliveInterval(30);
connOpt.setCleanSession(true);
clientId = ApplicationLoader.getClientId(LoginActivity.this);
client = new MqttAndroidClient(this, "tcp://104.131.50.64:1883", clientId, MqttAndroidClient.Ack.AUTO_ACK);//this,"tcp://104.131.50.64:1883", "app1", null);
}
#Override
protected void onStop() {
super.onStop();
isMinimized = true;
// super.onDestroy();
try {
client.close();
} catch (Exception e) {
// client.unregisterResources();
e.printStackTrace();
}
Crouton.cancelAllCroutons();
loginAgain.removeCallbacks(null);
han.removeCallbacks(null);
}
#Override
protected void onStart() {
super.onStart();
// Do not go to splash screen if came from signup activity
if (isMinimized && Prefs.getBoolean(getString(R.string.show_splash), true)) {
isMinimized = false;
han.removeCallbacks(null);
startActivity(new Intent(this, SplashScreen.class));
finish();
}
Prefs.putBoolean(getString(R.string.show_splash), true);
if (utils.getLoginState_login()) {
han.removeCallbacks(null);
utils._startActivity(this, MainActivity.class);
finish();
}
}
#Override
protected void onResume() {
super.onResume();
if (utils.getLoginState_login()) {
han.removeCallbacks(null);
utils._startActivity(this, MainActivity.class);
finish();
}
ApplicationLoader.isConnected(this, true);
}
public void login() {
Log.d(TAG, getString(R.string.login));
_toast(getString(R.string.logging_in), LoginActivity.this);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
// Disable login button for 5 secs
final boolean lastLoginState = _loginButton.isEnabled();
_loginButton.setEnabled(false);
loginAgain.postDelayed(new Runnable() {
#Override
public void run() {
_loginButton.setEnabled(lastLoginState);
}
}, 5000);
_progressDialog = new ProgressDialog(LoginActivity.this,
R.style.AppTheme_Dark_Dialog);
// String phone = _phoneText.getText().toString();
String password = _passwordText.getText().toString();
String numb = _phoneInputView.getNumber().replace("+", "");
connectMQTT(numb, password);
_progressDialog.setIndeterminate(true);
_progressDialog.setMessage("Authenticating...");
_progressDialog.show();
han.postDelayed(
new Runnable() {
public void run() { // On complete call either onLoginSuccess or onLoginFailed
//onLoginSuccess();
onLoginFailed();
_progressDialog.dismiss();
}
}, ApplicationLoader.timeOUT);
}
private void publish2MQQT(MqttAndroidClient client1, String topic, String msg) throws MqttException {
if (client1 != null) {
MqttMessage msg2 = new MqttMessage();
msg2.setPayload(msg.getBytes());
client1.publish(topic, msg2, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// _sendErrorLog("on sucess of publish");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// _sendErrorLog("on fail of publish e= " + exception.getMessage());
_progressDialog.dismiss();
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
// Log.e("mqtt ", "published " + msg);
}
}
private void _sendErrorLog(String s) {
Log.e("LOG", s);
}
private void connectMQTT(final String user, final String pass) {
Log.e("connectMQTT", "1");
try {
connOpt.setUserName(user);
connOpt.setPassword(pass.toCharArray());
_sendErrorLog("on connteing with " + user + " " + pass);
client.connect(connOpt, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
_sendErrorLog("on success of connect");
try {
client.subscribe("astr/app/iremote/" + user.replace("+", ""), 0, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
_sendErrorLog("on sucess of subscribe");
// TODO: Implement your own authentication logic here.
JsonObject msg = new JsonObject();
msg.addProperty("u", user);
msg.addProperty("P", pass);
sUserName = user;
sPassword = pass;
sDestination = "astr/admin/signin";
sMessage = msg.toString();
state = LOGGING_IN;
try {
Log.e("register", "publishing signin message");
if (client == null) {
Log.e("register", "publishing register message client is null");
}
publish2MQQT(client, sDestination, sMessage);
state = WAITING_FOR_SING_IN_ACK;
} catch (MqttException e) {
e.printStackTrace();
Log.e("register", "got exception in publish " + e.toString());
_progressDialog.dismiss();
_toast(e.getMessage(), LoginActivity.this);
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
_sendErrorLog("on failure of subscribe " + exception.getMessage());
_progressDialog.dismiss();
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
// client.subscribe("astr/app/iremote/" + _num_2b_verified.replace("+", ""));
} catch (MqttException | NullPointerException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
_sendErrorLog("on failure of connect" + exception.getMessage());
han.removeCallbacks(null);
try {
_progressDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
client.setCallback(this);
} catch (MqttException e) {
e.printStackTrace();
Log.e("connectMQTT", "got exception :: " + e.toString());
}
}
#Override
public void connectionLost(Throwable throwable) {
Log.e("connection", "lost");
//connectMQTT();
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
String msgRecived = new String(mqttMessage.getPayload());
/* Log.e("message arrived", "-------------------------------------------------");
Log.e("message arrived", "| Topic:" + s);*/
Log.e("message arrived", "| Message: " + msgRecived);
/*Log.e("message arrived" , "-------------------------------------------------");*/
if (state.equals(WAITING_FOR_SING_IN_ACK)) {
han.removeCallbacks(null);
JsonParser jp = new JsonParser();
JsonObject reply = (JsonObject) jp.parse(msgRecived);
if (reply.get("s").getAsInt() == 200) {
_toast(getString(R.string.logged_in), LoginActivity.this);
_progressDialog.dismiss();
_phoneInputView.setVisibility(View.GONE);
_passwordText.setVisibility(View.VISIBLE);
_loginButton.setText(R.string.logged_in);
_loginButton.setEnabled(true);
state = LOGGED_IN;
utils.storeLoginState(true, sUserName, sPassword);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
state = LOGGED_IN;
han.removeCallbacks(null);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
startActivity(new Intent(this, MainActivity.class));
//finish();
} else {
state = VERIFICATION_FAILED;
utils.storeLoginState(false, "", "");
onLoginFailed();
}
}
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SIGNUP) {
if (resultCode == RESULT_OK) {
// TODO: Implement successful signup logic here
// By default we just finish the Activity and log them in automatically
this.finish();
}
}
}
#Override
public void onBackPressed() {
// Disable going back to the MainActivity
moveTaskToBack(true);
}
public void onLoginFailed() {
// ApplicationLoader._toast("Login failed",LoginActivity.this);
_toast(getString(R.string.log_in_failed), LoginActivity.this);
_passwordText.setVisibility(View.VISIBLE);
_phoneInputView.setVisibility(View.VISIBLE);
_loginButton.setEnabled(false);
// Enable Login after 5000 ms with editing the number
loginAgain.postDelayed(new Runnable() {
#Override
public void run() {
_loginButton.setEnabled(_phoneInputView.isValid());
}
}, 5000);
}
#Override
public void onPause() {
super.onPause();
//disconnect();
}
public void resetPass(View view) {
// Start the Signup activity
Prefs.putInt(getString(R.string.key_reset_pass), 1);
Intent intent = new Intent(getApplicationContext(), SignUpActivity.class);
startActivityForResult(intent, REQUEST_PAS_RESET);
}
}
I believe the best option for you would be to implement those same functionalities in a Fragment. I won't read through your whole source code because it's enormous and I ain't got time for that, but I'll give you some ideas and directions and you can migrate it yourself.
Step 1: Create a MQTTFragment:
public class MQTTFragment extends Fragment implements MqttCallback {
public static final String TAG = "MQTTFragment.tag";
#Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// avoid this frag getting destroyed during rotation
setRetainInstance(true);
}
// DO NOT #Override onCreateView, this fragment have no views
// Put here ALL the code related to MQTT,
// any functionality you want to be able to call from Activity, make public, all the rest is private
// this fragment should also remember the current state of the connection
// this fragment can also have interface and listener pattern in case to past result back to activity.
... your mqtt code
}
Step 2: Every activity u want to use MQTT functionality includes the MQTTFragment
public class MyActivity extends AppCompatActivity {
private MQTTFragment mqttFragment;
// during onCreate you get or create the fragment
#Override public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
mqttFragment = new MQTTFragment();
getSupportFragmentManager()
.beginTransaction()
.add(mqttFragment, MQTTFragment.TAG)
.commit();
} else {
mqttFragment =
(MQTTFragment) getSupportFragmentManager()
.findFragmentByTag(MQTTFragment.TAG);
}
}
// now on this activity you can call anything MQTT related functionality from the Fragment
}