I have designed a code which leads to application not responding.
I have used okhttp3.WebSocket for continuous input stream of data on which i am deciding to start an IntentService which will be fetching data from server.
I have an IntentService; in onHandleIntent i am giving an service call for fetching data from the server(roughly 3 calls).
For Service call i am using AsyncTask of android inside which my WebConnectionManger class runs on different thread.
Inside websocket i am getting details of a particular record for which i am going to fetch the details from the service call.
For 5-6 such records my application runs fine, but if records get 80-100 my application do not respond at all and i get ANR.
I am using simple plain TCP request for this.
can any on tells me what is the actual issue and how i can get rid of it?
any help is appreciated.
i am pasting the code of WebSocket, AsyncTask(rest two have same implementation), WebConnectionManger class and IntentService class.
WebSocket.class
public class WebSocket {
public static boolean isConnected;
public static String TO_UPDATE = "toOrderUpdate";
#SuppressLint("StaticFieldLeak")
private static WebSocket _instance;
private static OkHttpClient client;
private static WebSocket webSocket = null;
private Context mContext;
private AppPreferences preferences;
private WebSocket() {
client = new OkHttpClient();
}
public WebSocket(Context context) {
client = new OkHttpClient();
mContext = context;
preferences = new AppPreferences(mContext);
init(mContext);
}
public static WebSocket getInstance(Context mContext) {
if (_instance == null) {
_instance = new WebSocket(mContext);
}
return _instance;
}
public static void closeWebSocket() {
if (isConnected) {
webSocket.close(1001, LOGOUT);
_instance = null;
webSocket = null;
isConnected = false;
}
}
public void init(Context context) {
if (webSocket == null) {
preferences = new AppPreferences(context);
Request request = new Request.Builder()
.url(preferences.getWSUrl() + ":" + preferences.getWSPort() + "/" + preferences.getUserID())
.build();
WebSocketMessageListener messageListener = new WebSocketMessageListener();
webSocket = client.newWebSocket(request, messageListener);
isConnected = true;
}
}
private class WebSocketMessageListener extends WebSocketListener {
// private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
Log.i("******", "Socket Open");
}
#Override
public void onMessage(WebSocket webSocket, String response) {
try {
super.onMessage(webSocket, response);
Log.i("******", "Message Received " + response);
// Logger.log("OnMessage : " + response);
ModelAdvertisements modelAdvertisements = DecoderJSONWebSocket.decode(response);
Intent intentForService = new Intent(mContext, WebSocketService.class);
int transCode = Integer.parseInt(modelAdvertisements.getTC());
Intent mwBroadcastIntent = new Intent();
switch (transCode) {
case 1005:
mwBroadcastIntent.setAction(Constants.IntentKeys.KEY_LOGICAL_SESSION_START_END);
mContext.sendBroadcast(mwBroadcastIntent);
break;
case 1004:
case 1006:
case 1007:
intentForService.putExtra(TO_UPDATE, true);
mContext.startService(intentForService);
break;
case 1008:
try {
mwBroadcastIntent.putExtra(KEY_AUCTION_FLOOR_SNAPSHOT, modelAdvertisements);
mwBroadcastIntent.setAction(Constants.IntentKeys.KEY_MARKET_DATASNAPSHOT);
mContext.sendBroadcast(mwBroadcastIntent);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
} catch (Exception e) {
// e.printStackTrace();
}
}
#Override
public void onClosing(WebSocket webSockett, int code, String reason) {
super.onClosing(webSockett, code, reason);
Log.i("******", "Socket Closing Reason: " + reason);
}
#Override
public void onClosed(WebSocket webSockett, int code, String reason) {
super.onClosed(webSockett, code, reason);
Log.i("******", "Socket closed reason: " + reason);
webSocket = null;
isConnected = false;
}
#Override
public void onFailure(WebSocket webSockett, Throwable t, Response response) {
super.onFailure(webSockett, t, response);
isConnected = false;
webSocket = null;
Logger.log(e);
}
}
}
WebSocketService.class
public class WebSocketService extends IntentService {
String securityId;
private Context mContext;
private String tokenId;
private String contractCode;
private int transCode;
private boolean toUpdate;
private String mktCode;
public WebSocketService() {
super("WebSocketService");
}
public WebSocketService(String name) {
super(name);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
if (intent != null) {
tokenId = intent.getStringExtra(KEY_TOKEN_ID);
transCode = intent.getIntExtra(KEY_TRANSCODE, 0);
toUpdate = intent.getBooleanExtra(NeMLWebSocket.TO_UPDATE, false);
contractCode = intent.getStringExtra(KEY_SYMBOL);
mktCode = intent.getStringExtra(KEY_ADV_REF_ID);
}
securityId = DatabaseUtils.getSecurityIdFromFOOrders(mContext, tokenId);
performTokenMasterTask();
}
#Override
public void onCreate() {
super.onCreate();
mContext = this;
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
protected void performTokenMasterTask() {
synchronized (this) {
TokenMasterTask tokenMasterTask = new TokenMasterTask(mContext, new RequestCallback() {
#Override
public void onStart() {
}
#Override
public void onComplete(Object object) {
if (transCode == TC_1004_WEB_SOCKET) {
Intent mwBroadcastIntent = new Intent();
mwBroadcastIntent.setAction(Constants.IntentKeys.KEY_TOKEN_SESSION_START_END);
mContext.sendBroadcast(mwBroadcastIntent);
// stopSelf();
} else if (transCode == TC_TIME_WEB_SOCKET || transCode == TC_AUCTION_WEB_SOCKET) {
performTimeSessionTask();
}
}
#Override
public void onProgress(int current, int total) {
}
#Override
public void onError(int transCode, String msg) {
try {
Logger.log(transCode + "--->" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}, mktCode);
tokenMasterTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, tokenId);
if (transCode == TC_TIME_WEB_SOCKET || transCode == TC_AUCTION_WEB_SOCKET) {
performTimeSessionTask();
}
}
}
public void performTimeSessionTask() {
synchronized (this) {
TimeSessionMapTask timeSessionMapTask = new TimeSessionMapTask(mContext, new RequestCallback() {
ProgressDialog progressDialog;
private boolean m_ConnectionErr = false;
#Override
public void onStart() {
}
#Override
public void onComplete(Object object) {
if (!m_ConnectionErr) {
if (transCode == TC_AUCTION_WEB_SOCKET) {
performFoOrderTask();
}
}
}
#Override
public void onProgress(int current, int total) {
}
#Override
public void onError(int transCode, String msg) {
try {
Logger.log("Received ErrorMessage :" + msg + " \n ErrorCode :" + transCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}, modelMarket);
timeSessionMapTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, TIME_SESSION);
if (transCode == TC_AUCTION_WEB_SOCKET) {
performFoOrderTask();
}
}
}
private synchronized void performFoOrderTask() {
synchronized (mContext) {
FOOrdersTask foOrdersTask = new FOOrdersTask(mContext, new RequestCallback() {
#Override
public void onStart() {
}
#Override
public void onComplete(Object object) {
}
#Override
public void onProgress(int current, int total) {
}
#Override
public void onError(int transCode, String msg) {
}
});
foOrdersTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, tokenId);
}
}
}
TokenMasterTask.class
public class TokenMasterTask extends AsyncTask<Object, Void, ModelToken> {
private final String mktCode;
private RequestCallback _callback;
#SuppressLint("StaticFieldLeak")
private Context context;
private boolean isConnectionError;
private ModelToken modelToken;
private boolean isServerDown;
public TokenMasterTask(Context context, RequestCallback requestCallback, String mktCode) {
this.context = context;
this.mktCode = mktCode;
if (requestCallback == null) {
requestCallback = new RequestCallback() {
#Override
public void onStart() {
}
#Override
public void onComplete(Object object) {
}
#Override
public void onProgress(int current, int total) {
}
#Override
public void onError(int transCode, String msg) {
}
};
}
this._callback = requestCallback;
}
#Override
protected ModelToken doInBackground(Object... voids) {
if (voids != null && voids.length > 0) {
String tokenId = String.valueOf(voids[0]);
isConnectionError = false;
transactionCall(tokenId);
}
return modelToken;
}
private void transactionCall(String tokenId) {
try {
WebConnectionManager connectionManager = new WebConnectionManager(context, new ConnectionListener() {
#Override
public void notifyReadCompleted(String f_Response) {
modelToken = DecoderTokenRequest.decode(f_Response);
synchronized (TokenMasterTask.this) {
TokenMasterTask.this.notify();
}
}
#Override
public void notifySocketError(boolean isServerDown) {
if (!isServerDown) {
isConnectionError = true;
}
TokenMasterTask.this.isServerDown = isServerDown;
synchronized (TokenMasterTask.this) {
TokenMasterTask.this.notify();
}
}
#Override
public void onReceivePacket(int total, int current) {
_callback.onProgress(current, total);
}
});
connectionManager.modifiedHandleRequest(EncoderTokenRequest.encode(context, tokenId,mktCode).getBytes());
} catch (Exception e) {
e.printStackTrace();
Logger.log(e);
}
synchronized( TokenMasterTask.this) {
try {
TokenMasterTask.this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
protected void onPostExecute(ModelToken modelToken) {
if (isServerDown) {
_callback.onError(Constants.ErrorCode.TC_ERROR_SERVER_DOWN, "");
} else if (isConnectionError) {
_callback.onError(0, "Connection Error.");
} else if (modelToken!=null && modelToken.getErrorCode() != null && !TextUtils.isEmpty(modelToken.getErrorCode()) && !modelToken.getErrorCode().equalsIgnoreCase("200")) {
_callback.onError(Integer.parseInt(modelToken.getErrorCode()), modelToken.getError());
} else {
_callback.onComplete(modelToken);
}
super.onPostExecute(modelToken);
}
}
WebConnectionManager.class
public class WebConnectionManager {
private String m_Response = "";
byte[] m_RequestData;
boolean m_Read_Response_Completed = false;
Thread l_WorkerThread;
ConnectionListener m_ConnectionListener;
boolean m_IsFetchCompleted;
Context context;
AppPreferences preferences;
Socket mWebSocket;
public WebConnectionManager(Context mcontext, ConnectionListener f_LoginListener) {
m_ConnectionListener = f_LoginListener;
m_IsFetchCompleted = false;
context = mcontext;
preferences = new AppPreferences(context);
}
public String modifiedHandleRequest(byte[] f_RequestData) {
m_RequestData = f_RequestData;
Logger.log("" + Constants.TIME_OUT);
l_WorkerThread = new Thread(new Runnable() {
#Override
public void run() {
String encodedIP = null;
try {
if (mWebSocket == null || !mWebSocket.isBound()
|| mWebSocket.isClosed() ) {
mWebSocket = new Socket(ip, port);
mWebSocket.setKeepAlive(true);
mWebSocket.setSoTimeout(Constants.TIME_OUT);
}
if (m_RequestData == null) {
m_Read_Response_Completed = true;
if (!mWebSocket.isClosed()) {
m_ConnectionListener.notifyReadCompleted("Connected");
return;
} else {
m_ConnectionListener.notifyReadCompleted("Disconnected");
return;
}
} else {
String request = new String(m_RequestData);
Logger.log(Utils.encodePackets(request));
}
InputStream inputStream = mWebSocket.getInputStream();
try {
mWebSocket.getOutputStream().write(m_RequestData);
} catch (Exception e) {
Logger.log(e);
}
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream(1048576);
byte[] buffer = new byte[1048576];
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
m_Response = byteArrayOutputStream.toString();
}
inputStream.close();
byteArrayOutputStream.close();
mWebSocket.close();
if (TextUtils.isEmpty(m_Response.toString().trim())) {
throw new IOException("Empty Response");
} else {
m_ConnectionListener.notifyReadCompleted(m_Response.toString());
}
} catch (UnknownHostException e) {
Logger.log(e);
m_ConnectionListener.notifySocketError(true);
mWebSocket = null;
} catch (SocketTimeoutException e) {
Logger.log(e);
m_ConnectionListener.notifySocketError(false);
mWebSocket = null;
e.printStackTrace();
} catch (SocketException e) {
Logger.log(e);
m_ConnectionListener.notifySocketError(true);
mWebSocket = null;
e.printStackTrace();
} catch (IOException e) {
Logger.log(e);
m_ConnectionListener.notifySocketError(true);
mWebSocket = null;
e.printStackTrace();
} catch (Exception e) {
Logger.log(e);
m_ConnectionListener.notifySocketError(true);
mWebSocket = null;
e.printStackTrace();
}
}
});
l_WorkerThread.start();
return m_Response;
}
}
And the interfaces.
public interface ConnectionListener {
void notifyReadCompleted(String f_Response);
void notifySocketError(boolean isServerDown);
void onReceivePacket(int total, int current);
}
public interface RequestCallback {
void onStart();
void onComplete(Object object);
void onProgress(int current, int total);
void onError(int transCode, String msg);
}
You may want to check what is blocking the main thread for more than 6 seconds.
Usually ANR happens when main thread is blocked for some time. 6-10 seconds.
Related
I am trying to integrate the Sinch API in android .
My VOIPClient. Java is like this
public class VOIPClient {
private static final String TAG = "VOIPClient";
private SinchClient mSinch;
private TTSHelper mTTS;
private Call mCurrentCall;
private BootService mContext;
private SinchClientBuilder mBuilder;
private NsdController mNsdController;
private final CallListener mCallListener = new CallListener() {
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call established at " + " Thusee");
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established at " + call.getDetails().getEstablishedTime());
mTTS.speak("Call started", TTSHelper.UTTERANCE_VOIP_START);
JsonObject payload = new JsonObject();
payload.addProperty("Status", 0);
payload.addProperty("Call_status", 1);
if (mNsdController != null) {
mNsdController.sendCommand(20, payload);
}
}
#Override
public void onCallEnded(Call call) {
Log.d(TAG, "Call ended at " + call.getDetails().getEndedTime() + "caused by " + call.getDetails().getEndCause().toString());
mTTS.speak("Call ended", TTSHelper.UTTERANCE_VOIP_END);
mCurrentCall.hangup();
JsonObject payload = new JsonObject();
payload.addProperty("Status", 0);
payload.addProperty("Call_status", 1);
if (mNsdController != null) {
mNsdController.sendCommand(21, payload);
}
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> list) {
}
};
public VOIPClient(BootService context) {
mContext = context;
mTTS = TTSHelper.getInstance(context);
mBuilder = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext())
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT);
if (mNsdController != null)
mNsdController.initialize();
}
public void start() {
SharedPreferences prefs = mContext.getPreferences();
int userId = prefs.getInt(MerryClient.PREF_USER_ID, 0);
String mUserId;
if (userId > 0) {
mUserId = String.valueOf(userId);
mSinch = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext()).userId(mUserId)
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT).build();
mSinch.setSupportCalling(true);
mSinch.setSupportManagedPush(false);
SinchClientListener sinchClientListener = new SinchClientListener() {
#Override
public void onClientStarted(SinchClient sinchClient) {
Log.d(TAG, "Sinch Client starts: " + sinchClient.getLocalUserId());
mTTS.speak("Call ready", TTSHelper.UTTERANCE_VOIP_READY);
}
#Override
public void onClientStopped(SinchClient sinchClient) {
Log.d(TAG, "Sinch Client stops");
}
#Override
public void onClientFailed(SinchClient sinchClient, SinchError sinchError) {
Log.e(TAG, String.format("Sinch Client error %d: %s", sinchError.getCode(), sinchError.getMessage()));
mSinch.terminate();
mTTS.speak("Voice Over IP failed", TTSHelper.UTTERANCE_VOIP_FAIL);
}
#Override
public void onRegistrationCredentialsRequired(SinchClient sinchClient, ClientRegistration clientRegistration) {
Log.d(TAG, "Sinch Client requires registration");
}
#Override
public void onLogMessage(int i, String s, String s1) {
Log.d(TAG, s1);
}
};
mSinch.addSinchClientListener(sinchClientListener);
mSinch.getCallClient().setRespectNativeCalls(false);
mSinch.getCallClient().addCallClientListener(new SinchCallClientListener());
mCurrentCall = null;
mSinch.startListeningOnActiveConnection();
mSinch.start();
}
}
public void tearDown() {
if (mSinch != null) {
mSinch.stopListeningOnActiveConnection();
mSinch.terminate();
mSinch = null;
}
}
public void restart() {
tearDown();
start();
}
public void initiateCall(final String targetUserName) {
new Thread(new Runnable() {
public void run() {
Looper.prepare();
if (targetUserName != null) {
try {
Call call = callUser(targetUserName);
call.addCallListener(mCallListener);
mCurrentCall = call;
} catch (Exception e) {
Log.e(TAG, "Initiate VOIP call failed", e);
}
}
Looper.loop();
}
}).start();
}
public void answerCall() {
if (mCurrentCall != null) {
mCurrentCall.answer();
}
}
public void hangUpCall() {
if (mCurrentCall != null) {
mCurrentCall.hangup();
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "Incoming call");
mTTS.speak("Incoming call from " + call.getRemoteUserId(), TTSHelper.UTTERANCE_VOIP_INCOMING);
call.addCallListener(mCallListener);
mCurrentCall = call;
// For testing only
answerCall();
}
}
public Call callUser(String userId) {
if (mSinch != null && mSinch.isStarted()) {
start();
}
if (mSinch == null) {
return null;
}
return mSinch.getCallClient().callUser(userId);
}
class CallerThread implements Runnable {
public String mtargetUserName;
CallerThread(String targetUserName) {
this.mtargetUserName = targetUserName;
}
#Override
public void run() {
Looper.prepare();
if (mtargetUserName != null) {
try {
Call call = callUser(mtargetUserName);
call.addCallListener(mCallListener);
mCurrentCall = call;
} catch (Exception e) {
Log.e(TAG, "Initiate VOIP call failed", e);
mContext.getAlexa().start();
}
}
Looper.loop();
}
}
}
When I try to call to other device then I am getting these kind of exceptions
Initiate VOIP call failed
java.lang.IllegalStateException: SinchClient not started
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.throwUnlessStarted(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.call(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(Unknown Source)
at tw.com.test.cloud.VOIPClient.callUser(VOIPClient.java:272)
at tw.com.test.cloud.VOIPClient$CallerThread.run(VOIPClient.java:293)
at java.lang.Thread.run(Thread.java:818)
Also some times I am getting this exception
FATAL EXCEPTION: Thread-75
Process: tw.com.test.wear, PID: 1123
java.lang.IllegalThreadStateException: A Looper must be associated with this thread.
at com.sinch.android.rtc.internal.AndroidLooperCallbackHandler.<init>(Unknown Source)
at com.sinch.android.rtc.internal.client.InternalSinchClientFactory.createSinchClient(Unknown Source)
at com.sinch.android.rtc.DefaultSinchClientBuilder.build(Unknown Source)
at tw.com.test.cloud.VOIPClient.start(VOIPClient.java:109)
at tw.com.test.cloud.VOIPClient$2.onClientStopped(VOIPClient.java:124)
at com.sinch.android.rtc.internal.client.DefaultSinchClient.shutdown(Unknown Source)
at com.sinch.android.rtc.internal.client.DefaultSinchClient.terminate(Unknown Source)
at tw.com.test.cloud.VOIPClient.tearDown(VOIPClient.java:160)
at tw.com.test.nsd.NsdController.messageReceived(NsdController.java:570)
at tw.com.test.nsd.NsdConnection.run(NsdConnection.java:115)
at java.lang.Thread.run(Thread.java:818)
2 Days I tried my self, I can't able to solve this yet,
I am always getting these exception. Sometimes it will work for one time then I need to restart the app.
You have to start SinchClient sinchClient.start(); and take NOTE during production mode do not place in a plain text form your SINCH_APP_SECRET, because its a secret key, hackers will easily read or decompile your code.
public VOIPClient(BootService context) {
mContext = context;
mTTS = TTSHelper.getInstance(context);
mBuilder = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext())
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT);
sinchClient.setSupportCalling(true);
sinchClient.start();
if (mNsdController != null)
mNsdController.initialize();
}
Here I have a query related to connection between android socket and server. I'm following https://socket.io/blog/native-socket-io-and-android/ and found that it is working fine, so I replaced my local server URL with the URL in the tutorial. But here I'm always getting a connection failed or disconnection error. Here is my application class for further clarification.
public class ChatApplication extends Application {
private Socket mSocket;
{
try {
IO.Options options = new IO.Options();
options.forceNew = true;
options.reconnection = true;
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("uid", 1);
jsonObject.put("usid", 6847);
options.query = jsonObject.toString();
} catch (JSONException e) {
e.printStackTrace();
}
Log.e("JSON", jsonObject.toString());
Log.e("OPTIONS", options.toString());
mSocket = IO.socket(Constants.CHAT_SERVER_URL, options);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Socket getSocket() {
return mSocket;
}
}
And the code of the fragment is below. It keeps calling onDisconnect() whenever I use my local server for it.
public class MainFragment extends Fragment {
private static final String TAG = "MainFragment";
private static final int REQUEST_LOGIN = 0;
private static final int TYPING_TIMER_LENGTH = 600;
private RecyclerView mMessagesView;
private EditText mInputMessageView;
private List<Message> mMessages = new ArrayList<Message>();
private RecyclerView.Adapter mAdapter;
private boolean mTyping = false;
private Handler mTypingHandler = new Handler();
private String mUsername;
private Socket mSocket;
private Boolean isConnected = true;
private Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (!isConnected) {
if (null != mUsername)
mSocket.emit("add user", mUsername);
Toast.makeText(getActivity().getApplicationContext(),
R.string.connect, Toast.LENGTH_LONG).show();
isConnected = true;
}
}
});
}
};
private Emitter.Listener onDisconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "diconnected");
isConnected = false;
Toast.makeText(getActivity().getApplicationContext(),
R.string.disconnect, Toast.LENGTH_LONG).show();
}
});
}
};
private Emitter.Listener onConnectError = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e(TAG, "Error connecting");
Toast.makeText(getActivity().getApplicationContext(),
R.string.error_connect, Toast.LENGTH_LONG).show();
}
});
}
};
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
String message;
try {
username = data.getString("username");
message = data.getString("message");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
removeTyping(username);
addMessage(username, message);
}
});
}
};
private Emitter.Listener onUserJoined = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
int numUsers;
try {
username = data.getString("username");
numUsers = data.getInt("numUsers");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addLog(getResources().getString(R.string.message_user_joined, username));
addParticipantsLog(numUsers);
}
});
}
};
private Emitter.Listener onUserLeft = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
int numUsers;
try {
username = data.getString("username");
numUsers = data.getInt("numUsers");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addLog(getResources().getString(R.string.message_user_left, username));
addParticipantsLog(numUsers);
removeTyping(username);
}
});
}
};
private Emitter.Listener onTyping = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
try {
username = data.getString("username");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addTyping(username);
}
});
}
};
private Emitter.Listener onStopTyping = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
try {
username = data.getString("username");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
removeTyping(username);
}
});
}
};
private Runnable onTypingTimeout = new Runnable() {
#Override
public void run() {
if (!mTyping) return;
mTyping = false;
mSocket.emit("stop typing");
}
};
public MainFragment() {
super();
}
// This event fires 1st, before creation of fragment or any views
// The onAttach method is called when the Fragment instance is associated with an Activity.
// This does not mean the Activity is fully initialized.
#Override
public void onAttach(Context context) {
super.onAttach(context);
mAdapter = new MessageAdapter(context, mMessages);
if (context instanceof Activity) {
//this.listener = (MainActivity) context;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
ChatApplication app = (ChatApplication) getActivity().getApplication();
mSocket = app.getSocket();
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.on("new message", onNewMessage);
mSocket.on("user joined", onUserJoined);
mSocket.on("user left", onUserLeft);
mSocket.on("typing", onTyping);
mSocket.on("stop typing", onStopTyping);
mSocket.connect();
startSignIn();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onDestroy() {
super.onDestroy();
mSocket.disconnect();
mSocket.off(Socket.EVENT_CONNECT, onConnect);
mSocket.off(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.off("new message", onNewMessage);
mSocket.off("user joined", onUserJoined);
mSocket.off("user left", onUserLeft);
mSocket.off("typing", onTyping);
mSocket.off("stop typing", onStopTyping);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMessagesView = (RecyclerView) view.findViewById(R.id.messages);
mMessagesView.setLayoutManager(new LinearLayoutManager(getActivity()));
mMessagesView.setAdapter(mAdapter);
mInputMessageView = (EditText) view.findViewById(R.id.message_input);
mInputMessageView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int id, KeyEvent event) {
if (id == R.id.send || id == EditorInfo.IME_NULL) {
attemptSend();
return true;
}
return false;
}
});
mInputMessageView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (null == mUsername) return;
if (!mSocket.connected()) return;
if (!mTyping) {
mTyping = true;
mSocket.emit("typing");
}
mTypingHandler.removeCallbacks(onTypingTimeout);
mTypingHandler.postDelayed(onTypingTimeout, TYPING_TIMER_LENGTH);
}
#Override
public void afterTextChanged(Editable s) {
}
});
ImageButton sendButton = (ImageButton) view.findViewById(R.id.send_button);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
attemptSend();
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Activity.RESULT_OK != resultCode) {
getActivity().finish();
return;
}
mUsername = data.getStringExtra("username");
int numUsers = data.getIntExtra("numUsers", 1);
addLog(getResources().getString(R.string.message_welcome));
addParticipantsLog(numUsers);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Inflate the menu; this adds items to the action bar if it is present.
inflater.inflate(R.menu.menu_main, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_leave) {
leave();
return true;
}
return super.onOptionsItemSelected(item);
}
private void addLog(String message) {
mMessages.add(new Message.Builder(Message.TYPE_LOG)
.message(message).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void addParticipantsLog(int numUsers) {
addLog(getResources().getQuantityString(R.plurals.message_participants, numUsers, numUsers));
}
private void addMessage(String username, String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.username(username).message(message).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void addTyping(String username) {
mMessages.add(new Message.Builder(Message.TYPE_ACTION)
.username(username).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void removeTyping(String username) {
for (int i = mMessages.size() - 1; i >= 0; i--) {
Message message = mMessages.get(i);
if (message.getType() == Message.TYPE_ACTION && message.getUsername().equals(username)) {
mMessages.remove(i);
mAdapter.notifyItemRemoved(i);
}
}
}
private void attemptSend() {
if (null == mUsername) return;
if (!mSocket.connected()) return;
mTyping = false;
String message = mInputMessageView.getText().toString().trim();
if (TextUtils.isEmpty(message)) {
mInputMessageView.requestFocus();
return;
}
mInputMessageView.setText("");
addMessage(mUsername, message);
// perform the sending message attempt.
mSocket.emit("new message", message);
}
private void startSignIn() {
// mUsername = null;
Intent intent = new Intent(getActivity(), LoginActivity.class);
startActivityForResult(intent, REQUEST_LOGIN);
}
private void leave() {
mUsername = null;
mSocket.disconnect();
mSocket.connect();
startSignIn();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
}
I'm using CHAT_SERVER_URL = "http://192.168.1.14:3000/" to make connection with server but it's not working for me. It works fine when we try to make a web connection through emulator or computer system. Can anyone please give me an idea if I'm doing anything wrong.
Thanks.
After debugging at the server end too, I got to know how to hit my server to make a connection and I got that we don't require IO.Options and Options.query in every case and now it is working fine completely. So for more clarification I'm posting an answer here:
//connect to server
public void startRunning() {
try {
if (baseUrl != null) {
socket = IO.socket("http://" + baseUrl + ":3000?uid=" + userId + "&usid=" + userSessionId);
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
if (socket == null) return;
socket
.on(Socket.EVENT_CONNECT, CONNECTED)
.on(Socket.EVENT_DISCONNECT, DISCONNECTED)
.on("chat-message", CHAT_MESSAGE)
.on("chats-active", CHATS_ACTIVE)
.on("chat-logout", LOGOUT)
.on("messages-read", MESSAGE_READ)
.on("chat-login", CHAT_LOGIN);
socket.connect();
}
I am facing the problem to create a new user on ejabberd server but sign in is working fine. i used the github repository (https://github.com/dilicode/LetsChat) for register the new user and chat between two and more users. I search on intenet, i found some way to register those are:
1.add
%% In-band registration
{access, register, [{allow, all}]}.
in access rules in ejabberd server and
2. also add
{mod_register, [
{access_from, register},
...
] ...
it in access rules of ejabberd server.
my sign up activity as follows:
public class SignupActivity extends AppCompatActivity implements OnClickListener, Listener<Boolean> {
private static final int REQUEST_CODE_SELECT_PICTURE = 1;
private static final int REQUEST_CODE_CROP_IMAGE = 2;
private static final String RAW_PHOTO_FILE_NAME = "camera.png";
private static final String AVATAR_FILE_NAME = "avatar.png";
private EditText nameText;
private EditText phoneNumberText;
private EditText passwordText;
private Button submitButton;
private ImageButton uploadAvatarButton;
private File rawImageFile;
private File avatarImageFile;
private SignupTask signupTask;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signup);
nameText = (EditText)findViewById(R.id.et_name);
phoneNumberText = (EditText)findViewById(R.id.et_phone_number);
passwordText = (EditText)findViewById(R.id.et_password);
uploadAvatarButton = (ImageButton)findViewById(R.id.btn_upload_avatar);
submitButton = (Button)findViewById(R.id.btn_submit);
submitButton.setOnClickListener(this);
uploadAvatarButton.setOnClickListener(this);
File dir = FileUtils.getDiskCacheDir(this, "temp");
if (!dir.exists()) {
dir.mkdirs();
}
rawImageFile = new File(dir, RAW_PHOTO_FILE_NAME);
avatarImageFile = new File(dir, AVATAR_FILE_NAME);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
if (v == submitButton) {
String phoneNumber = phoneNumberText.getText().toString();
String password = passwordText.getText().toString();
String name = nameText.getText().toString();
if (phoneNumber.trim().length() == 0 || password.trim().length() == 0 ||
name.trim().length() == 0) {
Toast.makeText(this, R.string.incomplete_signup_info, Toast.LENGTH_SHORT).show();
return;
}
signupTask = new SignupTask(this, this, phoneNumber, password, name, getAvatarBytes());
signupTask.execute();
} else if(v == uploadAvatarButton) {
chooseAction();
}
}
private void chooseAction() {
Intent captureImageIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(rawImageFile));
Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT);
pickIntent.setType("image/*");
Intent chooserIntent = Intent.createChooser(pickIntent, getString(R.string.profile_photo));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {captureImageIntent});
startActivityForResult(chooserIntent, REQUEST_CODE_SELECT_PICTURE);
}
#Override
public void onResponse(Boolean result) {
if (result) {
Toast.makeText(this, R.string.login_success, Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
setResult(RESULT_OK);
finish();
}
}
#Override
public void onErrorResponse(Exception exception) {
Toast.makeText(this, R.string.create_account_error, Toast.LENGTH_SHORT).show();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_CODE_SELECT_PICTURE:
boolean isCamera;
if (data == null) {
isCamera = true;
} else {
String action = data.getAction();
if (action == null) {
isCamera = false;
} else {
isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
}
}
if (isCamera) {
startCropImage(Uri.fromFile(rawImageFile));
} else {
startCropImage(data == null ? null : data.getData());
}
break;
case REQUEST_CODE_CROP_IMAGE:
Bitmap bitmap = BitmapFactory.decodeFile(avatarImageFile.getAbsolutePath());
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap);
drawable.setCircular(true);
uploadAvatarButton.setImageDrawable(drawable);
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void startCropImage(Uri source) {
if (source != null) {
int size = getResources().getDimensionPixelSize(R.dimen.default_avatar_size);
CropImageIntentBuilder cropImage = new CropImageIntentBuilder(size, size, Uri.fromFile(avatarImageFile));
cropImage.setSourceImage(source);
startActivityForResult(cropImage.getIntent(this), REQUEST_CODE_CROP_IMAGE);
}
}
private byte[] getAvatarBytes() {
if (!avatarImageFile.exists()) return null;
InputStream inputStream = null;
try {
inputStream = new FileInputStream(avatarImageFile);
} catch (FileNotFoundException e) {
AppLog.e("avatar file not found", e);
}
byte[] buffer = new byte[1024];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
return output.toByteArray();
}
#Override
protected void onDestroy() {
super.onDestroy();
if (signupTask != null) {
signupTask.dismissDialogAndCancel();
}
}
}
SignupTaskActivity as follows:
public class SignupTask extends BaseAsyncTask<Void, Void, Boolean> {
private String user;
private String name;
private String password;
private byte[] avatar;
private ProgressDialog dialog;
public SignupTask(Listener<Boolean> listener, Context context, String user, String password, String name, byte[] avatar) {
super(listener, context);
this.user = user;
this.name = name;
this.password = password;
this.avatar = avatar;
dialog = ProgressDialog.show(context, null, context.getResources().getString(R.string.signup));
}
#Override
public Response<Boolean> doInBackground(Void... params) {
Context context = getContext();
if (context != null) {
try {
SmackHelper.getInstance(context).signupAndLogin(user, password, name, avatar);
if (avatar != null) {
ImageCache.addAvatarToFile(context, user, BitmapFactory.decodeByteArray(avatar, 0, avatar.length));
}
PreferenceUtils.setLoginUser(context, user, password, name);
return Response.success(true);
} catch(SmackInvocationException e) {
AppLog.e(String.format("sign up error %s", e.toString()), e);
return Response.error(e);
}
}
return null;
}
#Override
protected void onPostExecute(Response<Boolean> response) {
dismissDialog();
super.onPostExecute(response);
}
#Override
protected void onCancelled() {
super.onCancelled();
dismissDialog();
}
public void dismissDialog() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
public void dismissDialogAndCancel() {
dismissDialog();
cancel(false);
}
}
SmackHelper class as follows:
public class SmackHelper {
private static final String LOG_TAG = "SmackHelper";
private static final int PORT = 5222;
public static final String RESOURCE_PART = "Smack";
private XMPPConnection con;
private ConnectionListener connectionListener;
private Context context;
private State state;
private PacketListener messagePacketListener;
private PacketListener presencePacketListener;
private SmackAndroid smackAndroid;
private static SmackHelper instance;
private SmackContactHelper contactHelper;
private SmackVCardHelper vCardHelper;
private FileTransferManager fileTransferManager;
private PingManager pingManager;
private long lastPing = new Date().getTime();
public static final String ACTION_CONNECTION_CHANGED = "com.mstr.letschat.intent.action.CONNECTION_CHANGED";
public static final String EXTRA_NAME_STATE = "com.mstr.letschat.State";
private SmackHelper(Context context) {
this.context = context;
smackAndroid = SmackAndroid.init(context);
messagePacketListener = new MessagePacketListener(context);
presencePacketListener = new PresencePacketListener(context);
SmackConfiguration.setDefaultPacketReplyTimeout(20 * 1000);
Roster.setDefaultSubscriptionMode(SubscriptionMode.manual);
ProviderManager.addExtensionProvider(UserLocation.ELEMENT_NAME, UserLocation.NAMESPACE, new LocationMessageProvider());
}
public static synchronized SmackHelper getInstance(Context context) {
if (instance == null) {
instance = new SmackHelper(context.getApplicationContext());
}
return instance;
}
public void setState(State state) {
if (this.state != state) {
Log.d(LOG_TAG, "enter state: " + state.name());
this.state = state;
}
}
public void signupAndLogin(String user, String password, String nickname, byte[] avatar) throws SmackInvocationException {
connect();
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("name", nickname);
try {
AccountManager.getInstance(con).createAccount(user, password, attributes);
} catch (Exception e) {
throw new SmackInvocationException(e);
}
login(user, password);
vCardHelper.save(nickname, avatar);
}
public void sendChatMessage(String to, String body, PacketExtension packetExtension) throws SmackInvocationException {
Message message = new Message(to, Message.Type.chat);
message.setBody(body);
if (packetExtension != null) {
message.addExtension(packetExtension);
}
try {
con.sendPacket(message);
} catch (NotConnectedException e) {
throw new SmackInvocationException(e);
}
}
public List<RosterEntry> getRosterEntries() {
List<RosterEntry> result = new ArrayList<RosterEntry>();
Roster roster = con.getRoster();
Collection<RosterGroup> groups = roster.getGroups();
for (RosterGroup group : groups) {
result.addAll(group.getEntries());
}
return result;
}
and finally my menifest file
public UserProfile search(String username) throws SmackInvocationException {
String name = StringUtils.parseName(username);
String jid = null;
if (name == null || name.trim().length() == 0) {
jid = username + "#" + con.getServiceName();
} else {
jid = StringUtils.parseBareAddress(username);
}
if (vCardHelper == null) {
return null;
}
VCard vCard = vCardHelper.loadVCard(jid);
String nickname = vCard.getNickName();
return nickname == null ? null : new UserProfile(jid, vCard);
}
public String getNickname(String jid) throws SmackInvocationException {
VCard vCard = vCardHelper.loadVCard(jid);
return vCard.getNickName();
}
private void connect() throws SmackInvocationException {
if (!isConnected()) {
setState(State.CONNECTING);
if (con == null) {
con = createConnection();
}
try {
con.connect();
}catch (SmackException.NoResponseException er){
Log.e(LOG_TAG,"Norespponse exception");
}
catch(Exception e) {
Log.e(LOG_TAG, String.format("Unhandled exception %s", e.toString()), e);
startReconnectIfNecessary();
throw new SmackInvocationException(e);
}
}
}
#SuppressLint("TrulyRandom")
private XMPPConnection createConnection() {
ConnectionConfiguration config = new ConnectionConfiguration(PreferenceUtils.getServerHost(context), PORT);
SSLContext sc = null;
MemorizingTrustManager mtm = null;
try {
mtm = new MemorizingTrustManager(context);
sc = SSLContext.getInstance("TLS");
sc.init(null, new X509TrustManager[] { mtm }, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
} catch (KeyManagementException e) {
throw new IllegalStateException(e);
}
config.setCustomSSLContext(sc);
config.setHostnameVerifier(mtm.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()));
config.setSecurityMode(SecurityMode.required);
config.setReconnectionAllowed(false);
config.setSendPresence(false);
config.setSecurityMode(SecurityMode.disabled);
List<HostAddress> list = config.getHostAddresses();
boolean data = config.isSendPresence();
return new XMPPTCPConnection(config);
}
public void cleanupConnection() {
if (con != null) {
con.removePacketListener(messagePacketListener);
con.removePacketListener(presencePacketListener);
if (connectionListener != null) {
con.removeConnectionListener(connectionListener);
}
}
if (isConnected()) {
try {
con.disconnect();
} catch (NotConnectedException e) {}
}
}
private void onConnectionEstablished() {
if (state != State.CONNECTED) {
//processOfflineMessages();
try {
con.sendPacket(new Presence(Presence.Type.available));
} catch (NotConnectedException e) {}
contactHelper = new SmackContactHelper(context, con);
vCardHelper = new SmackVCardHelper(context, con);
fileTransferManager = new FileTransferManager(con);
OutgoingFileTransfer.setResponseTimeout(30000);
addFileTransferListener();
pingManager = PingManager.getInstanceFor(con);
pingManager.registerPingFailedListener(new PingFailedListener() {
#Override
public void pingFailed() {
// Note: remember that maybeStartReconnect is called from a different thread (the PingTask) here, it may causes synchronization problems
long now = new Date().getTime();
if (now - lastPing > 30000) {
Log.e(LOG_TAG, "Ping failure, reconnect");
startReconnectIfNecessary();
lastPing = now;
} else {
Log.e(LOG_TAG, "Ping failure reported too early. Skipping this occurrence.");
}
}
});
con.addPacketListener(messagePacketListener, new MessageTypeFilter(Message.Type.chat));
con.addPacketListener(presencePacketListener, new PacketTypeFilter(Presence.class));
con.addConnectionListener(createConnectionListener());
setState(State.CONNECTED);
broadcastState(State.CONNECTED);
MessageService.reconnectCount = 0;
}
}
private void broadcastState(State state) {
Intent intent = new Intent(ACTION_CONNECTION_CHANGED);
intent.putExtra(EXTRA_NAME_STATE, state.toString());
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
public void login(String username, String password) throws SmackInvocationException {
connect();
try {
if (!con.isAuthenticated()) {
con.login(username, password, RESOURCE_PART);
}
onConnectionEstablished();
} catch(Exception e) {
SmackInvocationException exception = new SmackInvocationException(e);
// this is caused by wrong username/password, do not reconnect
if (exception.isCausedBySASLError()) {
cleanupConnection();
} else {
startReconnectIfNecessary();
}
throw exception;
}
}
public String getLoginUserNickname() throws SmackInvocationException {
try {
return AccountManager.getInstance(con).getAccountAttribute("name");
} catch (Exception e) {
throw new SmackInvocationException(e);
}
}
private void processOfflineMessages() {
Log.i(LOG_TAG, "Begin retrieval of offline messages from server");
OfflineMessageManager offlineMessageManager = new OfflineMessageManager(con);
try {
if (!offlineMessageManager.supportsFlexibleRetrieval()) {
Log.d(LOG_TAG, "Offline messages not supported");
return;
}
List<Message> msgs = offlineMessageManager.getMessages();
for (Message msg : msgs) {
Intent intent = new Intent(MessageService.ACTION_MESSAGE_RECEIVED, null, context, MessageService.class);
intent.putExtra(MessageService.EXTRA_DATA_NAME_FROM, StringUtils.parseBareAddress(msg.getFrom()));
intent.putExtra(MessageService.EXTRA_DATA_NAME_MESSAGE_BODY, msg.getBody());
context.startService(intent);
}
offlineMessageManager.deleteMessages();
} catch (Exception e) {
Log.e(LOG_TAG, "handle offline messages error ", e);
}
Log.i(LOG_TAG, "End of retrieval of offline messages from server");
}
private ConnectionListener createConnectionListener() {
connectionListener = new ConnectionListener() {
#Override
public void authenticated(XMPPConnection arg0) {}
#Override
public void connected(XMPPConnection arg0) {}
#Override
public void connectionClosed() {
Log.e(LOG_TAG, "connection closed");
}
#Override
public void connectionClosedOnError(Exception arg0) {
// it may be due to network is not available or server is down, update state to WAITING_TO_CONNECT
// and schedule an automatic reconnect
Log.e(LOG_TAG, "connection closed due to error ", arg0);
startReconnectIfNecessary();
}
#Override
public void reconnectingIn(int arg0) {}
#Override
public void reconnectionFailed(Exception arg0) {}
#Override
public void reconnectionSuccessful() {}
};
return connectionListener;
}
private void startReconnectIfNecessary() {
cleanupConnection();
setState(State.WAITING_TO_CONNECT);
if (NetworkUtils.isNetworkConnected(context)) {
context.startService(new Intent(MessageService.ACTION_RECONNECT, null, context, MessageService.class));
}
}
private boolean isConnected() {
return con != null && con.isConnected();
}
public void onNetworkDisconnected() {
setState(State.WAITING_FOR_NETWORK);
}
public void requestSubscription(String to, String nickname) throws SmackInvocationException {
contactHelper.requestSubscription(to, nickname);
}
public void approveSubscription(String to, String nickname, boolean shouldRequest) throws SmackInvocationException {
contactHelper.approveSubscription(to);
if (shouldRequest) {
requestSubscription(to, nickname);
}
}
public void delete(String jid) throws SmackInvocationException {
contactHelper.delete(jid);
}
public String loadStatus() throws SmackInvocationException {
if (vCardHelper == null) {
throw new SmackInvocationException("server not connected");
}
return vCardHelper.loadStatus();
}
public VCard loadVCard(String jid) throws SmackInvocationException {
if (vCardHelper == null) {
throw new SmackInvocationException("server not connected");
}
return vCardHelper.loadVCard(jid);
}
public VCard loadVCard() throws SmackInvocationException {
if (vCardHelper == null) {
throw new SmackInvocationException("server not connected");
}
return vCardHelper.loadVCard();
}
public void saveStatus(String status) throws SmackInvocationException {
if (vCardHelper == null) {
throw new SmackInvocationException("server not connected");
}
vCardHelper.saveStatus(status);
contactHelper.broadcastStatus(status);
}
public SubscribeInfo processSubscribe(String from) throws SmackInvocationException {
SubscribeInfo result = new SubscribeInfo();
RosterEntry rosterEntry = contactHelper.getRosterEntry(from);
ItemType rosterType = rosterEntry != null ? rosterEntry.getType() : null;
if (rosterEntry == null || rosterType == ItemType.none) {
result.setType(SubscribeInfo.TYPE_WAIT_FOR_APPROVAL);
result.setNickname(getNickname(from));
} else if (rosterType == ItemType.to) {
result.setType(SubscribeInfo.TYPE_APPROVED);
result.setNickname(rosterEntry.getName());
approveSubscription(from, null, false);
}
result.setFrom(from);
return result;
}
public void sendImage(File file, String to) throws SmackInvocationException {
if (fileTransferManager == null || !isConnected()) {
throw new SmackInvocationException("server not connected");
}
String fullJid = to + "/" + RESOURCE_PART;
OutgoingFileTransfer transfer = fileTransferManager.createOutgoingFileTransfer(fullJid);
try {
transfer.sendFile(file, file.getName());
} catch (SmackException e) {
Log.e(LOG_TAG, "send file error");
throw new SmackInvocationException(e);
}
while(!transfer.isDone()) {
if(transfer.getStatus().equals(Status.refused) || transfer.getStatus().equals(Status.error)
|| transfer.getStatus().equals(Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
Log.d(LOG_TAG, "send file status: " + transfer.getStatus());
if(transfer.getStatus().equals(Status.refused) || transfer.getStatus().equals(Status.error)
|| transfer.getStatus().equals(Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
private void addFileTransferListener() {
fileTransferManager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(final FileTransferRequest request) {
new Thread() {
#Override
public void run() {
IncomingFileTransfer transfer = request.accept();
String fileName = String.valueOf(System.currentTimeMillis());
File file = new File(FileUtils.getReceivedImagesDir(context), fileName + FileUtils.IMAGE_EXTENSION);
try {
transfer.recieveFile(file);
} catch (SmackException e) {
Log.e(LOG_TAG, "receive file error", e);
return;
}
while (!transfer.isDone()) {
if(transfer.getStatus().equals(Status.refused) || transfer.getStatus().equals(Status.error)
|| transfer.getStatus().equals(Status.cancelled)){
Log.e(LOG_TAG, "receive file error, " + transfer.getError());
return;
}
}
// start service to save the image to sqlite
if (transfer.getStatus().equals(Status.complete)) {
Intent intent = new Intent(MessageService.ACTION_MESSAGE_RECEIVED, null, context, MessageService.class);
intent.putExtra(MessageService.EXTRA_DATA_NAME_FROM, StringUtils.parseBareAddress(request.getRequestor()));
intent.putExtra(MessageService.EXTRA_DATA_NAME_MESSAGE_BODY, context.getString(R.string.image_message_body));
intent.putExtra(MessageService.EXTRA_DATA_NAME_FILE_PATH, file.getAbsolutePath());
intent.putExtra(MessageService.EXTRA_DATA_NAME_TYPE, ChatMessageTableHelper.TYPE_INCOMING_IMAGE);
context.startService(intent);
}
}
}.start();
}
});
}
public void onDestroy() {
cleanupConnection();
smackAndroid.onDestroy();
}
public static enum State {
CONNECTING,
CONNECTED,
DISCONNECTED,
// this is a state that client is trying to reconnect to server
WAITING_TO_CONNECT,
WAITING_FOR_NETWORK;
}
}
but i could not found any progress.please help me out. thanks in advance.
After struggle i found solution of this kind of problem. we need to do server side changes like:
step1:
step2:
step3:
after doing all these steps we will able to register new user on ejabberd server.
So I'm badly stuck in creating a datachannel between android app client and Web App client running on Chrome.
Following is my complete code for which Audio and Video is streaming fine. But When I try to send the message using DataChannel it returns False and State as CONNECTING. I'm trying to resolve since 1 week but couldn't make it run.
EDIT: Issue resolved. Here is the final Working Code. Android Do not support RTPDataChannel anymore
Home.java
public class Home extends Activity {
public List<PeerConnection.IceServer> iceServers;
private GLSurfaceView videoView;
public static SocketIO socket;
ArrayList<String> userIDs = new ArrayList<>();
private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
String RoomId = "";
String sreverURL = "http://xx.xx.xx.xx:xxxx/";
private EditText roomid;
private VideoRenderer.Callbacks remote_view;
private VideoRenderer.Callbacks local_view;
protected PeerConnectionFactory factory;
PeerConnectionFactory.Options options = null;
Events pc_events;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
videoView = (GLSurfaceView) findViewById(R.id.glview_call_remote);
VideoRendererGui.setView(videoView, new Runnable() {
#Override
public void run() {
createPeerConnectionFactory();
}
});
remote_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FIT, false);
local_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, true);
iceServers = new ArrayList<>();
IceServer icc = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(icc);
roomid = (EditText) findViewById(R.id.roomId);
Random rand = new Random();
roomid.setText("" + rand.nextInt(9999));
pc_events = new peerEventHandler();
}
private void createPeerConnectionFactory() {
runOnUiThread(new Runnable() {
#Override
public void run() {
PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
PeerConnectionFactory.initializeAndroidGlobals(Home.this, true, true, true, VideoRendererGui.getEGLContext());
try {
factory = new PeerConnectionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void ondail(View view) {
try {
try {
SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
socket = new SocketIO();
//startCall();
socket.connect(sreverURL, new IOCallback() {
#Override
public void onMessage(JSONObject json, IOAcknowledge ack) {
}
#Override
public void onMessage(String data, IOAcknowledge ack) {
}
#Override
public void onError(SocketIOException socketIOException) {
socketIOException.printStackTrace();
}
#Override
public void onDisconnect() {
}
#Override
public void onConnect() {
showToast("Connected to " + sreverURL);
}
#Override
public void on(final String event, IOAcknowledge ack, final Object... args) {
String argument = "";
try {
if (args instanceof Object[]) {
argument = args[0].toString();
} else {
argument = args.toString();
}
} catch (Exception e) {
argument = args.toString();
}
Log.e("Socked.on", event + ", " + argument);
switch (getEvent(event)) {
case LOG:
break;
case MESSAGE:
if (args instanceof Object[]) {
pc_events.setMessage(args[0].toString());
} else {
pc_events.setMessage(args.toString());
}
break;
case CREATED:
runOnUiThread(new Runnable() {
public void run() {
showToast("Room Created " + args[0]);
}
});
break;
case BROADCAST:
break;
case JOIN:
break;
case EMIT:
Log.e("Socked.onEMIT", argument);
startCall();
pc_events.createOffer();
break;
case ERROR:
Log.e("Socked.onERROR", argument);
break;
default:
break;
}
}
});
try {
RoomId = roomid.getText().toString();
} catch (Exception e) {
}
socket.emit("create or join", RoomId);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public void oncancel(View view) {
}
public SocketEvent getEvent(String eventString) {
SocketEvent eventType;
try {
if (eventString.contains("log")) {
eventType = SocketEvent.LOG;
} else if (eventString.contains("created")) {
eventType = SocketEvent.CREATED;
} else if (eventString.contains("emit():")) {
eventType = SocketEvent.EMIT;
} else if (eventString.contains("broadcast():")) {
eventType = SocketEvent.BROADCAST;
} else if (eventString.contains("message")) {
eventType = SocketEvent.MESSAGE;
} else if (eventString.toLowerCase().substring(0, 20).contains("join")) {
eventType = SocketEvent.JOIN;
} else {
eventType = SocketEvent.ERROR;
}
} catch (Exception e) {
eventType = SocketEvent.ERROR;
}
return eventType;
}
public static interface Events {
public void peerConnectionEvent(VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender);
public void setFactory(PeerConnectionFactory factory);
public void setMessage(String message);
public void createOffer();
public void sendMessage(String msg);
}
private void startCall() {
pc_events.setFactory(factory);
pc_events.peerConnectionEvent(remote_view, local_view);
}
public void showToast(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(Home.this, message, Toast.LENGTH_SHORT).show();
}
});
}
public void sendMessage(View v) {
pc_events.sendMessage("Hello");
}
}
peerEventHandler.java
public class peerEventHandler implements Events {
private PeerConnection peerConnection;
private PeerConnectionFactory factory;
PCObserver pcObserver = new PCObserver();
public LooperExecutor executor;
private MediaStream mediaStream;
private VideoSource videoSource;
private DcObserver dc_observer;
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
private VideoCapturerAndroid videoCapturer;
private VideoTrack localVideoTrack;
private VideoTrack remoteVideoTrack;
public boolean preferIsac = false;
public boolean videoCallEnabled = true;
public boolean preferH264 = false;
private SessionDescription localSdp;
private final SDPObserver sdpObserver = new SDPObserver();
public boolean isInitiator = false;
private MediaConstraints sdpMediaConstraints;
private VideoRenderer.Callbacks remote_view;
private VideoRenderer.Callbacks local_view;
private DataChannel dataChannel;
#Override
public void peerConnectionEvent(Callbacks remoteRender, Callbacks localRender) {
this.remote_view = remoteRender;
this.local_view = localRender;
creatPeerConnection();
}
public void creatPeerConnection() {
executor = new LooperExecutor();
executor.requestStart();
MediaConstraints pcConstraints = new MediaConstraints();
MediaConstraints videoConstraints = new MediaConstraints();
MediaConstraints audioConstraints = new MediaConstraints();
sdpMediaConstraints = new MediaConstraints();
creatvideoConstraints(videoConstraints);
creataudioConstraints(audioConstraints);
creatsdpMediaConstraints(sdpMediaConstraints);
List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>();
IceServer iceServer = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(iceServer);
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false"));
pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);
Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT), Logging.Severity.LS_WARNING);
mediaStream = factory.createLocalMediaStream("ARDAMS");
Log.e("DC Add", "Adding Data Channel");
dataChannel = peerConnection.createDataChannel("sendDataChannel", new DataChannel.Init());
dc_observer = new DcObserver();
dataChannel.registerObserver(dc_observer);
Log.e("DC Add", "Data Channel added");
String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
String frontCameraDeviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice();
cameraDeviceName = frontCameraDeviceName;
videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);
videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
localVideoTrack.setEnabled(true);
localVideoTrack.addRenderer(new VideoRenderer(local_view));
mediaStream.addTrack(factory.createAudioTrack(AUDIO_TRACK_ID, factory.createAudioSource(audioConstraints)));
mediaStream.addTrack(localVideoTrack);
peerConnection.addStream(mediaStream);
}
#Override
public void createOffer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = true;
peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
}
}
});
}
public void createAnswer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = false;
peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
}
}
});
}
private class PCObserver implements PeerConnection.Observer {
#Override
public void onAddStream(final MediaStream stream) {
Log.e("onAddStream", "onAddStream");
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
// /reportError("Weird-looking stream: " + stream);
return;
}
if (stream.videoTracks.size() == 1) {
remoteVideoTrack = stream.videoTracks.get(0);
remoteVideoTrack.setEnabled(true);
remoteVideoTrack.addRenderer(new VideoRenderer(remote_view));
VideoRendererGui.update(local_view, 75, 70, 60, 60, ScalingType.SCALE_ASPECT_FIT, true);
VideoRendererGui.update(remote_view, 0, 0, 200, 200, ScalingType.SCALE_ASPECT_FILL, false);
}
}
});
}
#Override
public void onDataChannel(final DataChannel dc) {
executor.execute(new Runnable() {
#Override
public void run() {
dataChannel = dc;
String channelName = dataChannel.label();
dc_observer = new DcObserver();
dataChannel.registerObserver(dc_observer);
}
});
}
#Override
public void onIceCandidate(IceCandidate candidate) {
SocketIO socket = Home.socket;
JSONObject json;
try {
/*
json.putOpt("label", candidate.sdpMLineIndex);
json.putOpt("id", candidate.sdpMid);
json.putOpt("candidate", candidate.sdp);*/
Gson gson = new Gson();
String json_str = gson.toJson(candidate);
json = new JSONObject(json_str);
json.putOpt("type", "candidate");
socket.emit("message", json);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onIceConnectionChange(IceConnectionState state) {
}
#Override
public void onIceConnectionReceivingChange(boolean arg0) {
}
#Override
public void onIceGatheringChange(IceGatheringState arg0) {
}
#Override
public void onRemoveStream(MediaStream arg0) {
}
#Override
public void onRenegotiationNeeded() {
}
#Override
public void onSignalingChange(SignalingState arg0) {
}
}
public void creatPcConstrains(MediaConstraints pcConstraints) {
}
public void creatvideoConstraints(MediaConstraints videoConstraints) {
String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
int videoWidth = 0;
int videoHeight = 0;
if ((videoWidth == 0 || videoHeight == 0) && true && MediaCodecVideoEncoder.isVp8HwSupported()) {
videoWidth = 1280;
videoHeight = 1280;
}
if (videoWidth > 0 && videoHeight > 0) {
videoWidth = Math.min(videoWidth, 1280);
videoHeight = Math.min(videoHeight, 1280);
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
}
int videoFps = 30;
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
}
public void creataudioConstraints(MediaConstraints audioConstraints) {
audioConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
pcConstraints.optional.add(new KeyValuePair("internalSctpDataChannels", "true"));
}
public void creatsdpMediaConstraints(MediaConstraints sdpMediaConstraints) {
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", "true"));
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", "true"));
}
private class SDPObserver implements SdpObserver {
#Override
public void onCreateFailure(String arg0) {
System.out.print(arg0);
}
#Override
public void onCreateSuccess(SessionDescription origSdp) {
if (localSdp != null) {
return;
}
localSdp = origSdp;
setLocalDescription(origSdp);
}
#Override
public void onSetFailure(String arg0) {
}
#Override
public void onSetSuccess() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (isInitiator) {
if (peerConnection != null) {
JSONObject json = new JSONObject();
try {
json.putOpt("type", localSdp.type.toString().toLowerCase());
json.putOpt("sdp", localSdp.description);
} catch (JSONException e) {
e.printStackTrace();
}
Home.socket.emit("message", json);
}
} else {
// createAnswer();
}
}
});
}
}
public void addRemoteIceCandidate(final IceCandidate candidate) {
executor.execute(new Runnable() {
#Override
public void run() {
peerConnection.addIceCandidate(candidate);
}
});
}
public void setLocalDescription(final SessionDescription sdp) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
peerConnection.setLocalDescription(sdpObserver, sdp);
}
});
}
public void setRemoteDescription(final SessionDescription sdp) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
peerConnection.setRemoteDescription(sdpObserver, sdp);
}
});
}
#Override
public void setFactory(PeerConnectionFactory factory) {
this.factory = factory;
}
public void onWebSocketMessage(final String msg) {
try {
Log.e("onWebSocketMessage", msg);
JSONObject json = new JSONObject(msg);
json = new JSONObject(msg);
String type = json.optString("type");
if (type.equals("candidate")) {
IceCandidate candidate = new IceCandidate(json.getString("id"), json.getInt("label"), json.getString("candidate"));
addRemoteIceCandidate(candidate);
} else if (type.equals("answer")) {
isInitiator = false;
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else if (type.equals("offer")) {
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else if (type.equals("bye")) {
} else {
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void setMessage(String message) {
if (message.toString().contains("got user media") || message.toString().contains("bye")) {
} else
onWebSocketMessage(message);
}
private class DcObserver implements DataChannel.Observer {
#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("onMessage ", command);
}
#Override
public void onStateChange() {
Log.e("onStateChange ", "onStateChange");
}
#Override
public void onBufferedAmountChange(long arg0) {
Log.e("onMessage ", "" + arg0);
}
}
#Override
public void sendMessage(String msg) {
Log.e("DataChannel State", "" + dataChannel.state());
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
boolean sent = dataChannel.send(new DataChannel.Buffer(buffer, false));
Log.e("Message sent", "" + sent);
}
}
Edit: There is no issue at web client as similar app in ios is working fine in similar scenario.
So I'm trying to connect Android to Browser via webRTC via socket.io and libjingle and server is running on Node.js .
Issue I'm facing is weired.
When 1 client is at Android(native app) and other is at Ipad(native app), Everything works fine.
When 1 client is at iPad(Native app) and other is WebApp, Everyting works fine.
But When 1 Client is at Android(native app) and other is WebPage, everyting works fine except the audio and video is not streaming to that end.
Following are the two major classes i've used for the purpose:
PS. The method makeOffer(View v) called by the button.
MainActivity.java
public List<PeerConnection.IceServer> iceServers;
private GLSurfaceView videoView;
public static SocketIO socket;
ArrayList<String> userIDs = new ArrayList<>();
private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
String RoomId = "";
String sreverURL = "http://xx.xx.xx.xx:xxxx/";
private EditText roomid;
private VideoRenderer.Callbacks localRender;
private VideoRenderer.Callbacks remoteRender;
protected PeerConnectionFactory factory;
PeerConnectionFactory.Options options = null;
Events pc_events;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
videoView = (GLSurfaceView) findViewById(R.id.glview_call);
VideoRendererGui.setView(videoView, new Runnable() {
#Override
public void run() {
createPeerConnectionFactory();
}
});
remoteRender = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, false);
localRender = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, true);
iceServers = new ArrayList<>();
IceServer icc = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(icc);
roomid = (EditText) findViewById(R.id.roomId);
Random rand = new Random();
roomid.setText("" + rand.nextInt(9999));
pc_events = new peerEventHandler();
}
private void createPeerConnectionFactory() {
runOnUiThread(new Runnable() {
#Override
public void run() {
PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
PeerConnectionFactory.initializeAndroidGlobals(Home.this, true, true, true, VideoRendererGui.getEGLContext());
try {
factory = new PeerConnectionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void ondail(View view) {
try {
try {
SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
socket = new SocketIO();
socket.connect(sreverURL, new IOCallback() {
#Override
public void onMessage(JSONObject json, IOAcknowledge ack) {
Log.e("json Object", json.toString());
}
#Override
public void onMessage(String data, IOAcknowledge ack) {
Log.e("json Object", data.toString());
}
#Override
public void onError(SocketIOException socketIOException) {
socketIOException.printStackTrace();
System.out.println(socketIOException.toString());
}
#Override
public void onDisconnect() {
}
#Override
public void onConnect() {
showToast("Connected to " + sreverURL);
startCall("");
}
#Override
public void on(final String event, IOAcknowledge ack, final Object... args) {
Log.e("Socked.on", event + ", " + args);
switch (getEvent(event)) {
case LOG :
break;
case MESSAGE :
if (args instanceof Object[]) {
if (args[0].toString().contains("offer")) {
// startCall("");
}
pc_events.setMessage(args[0].toString());
} else {
pc_events.setMessage(args.toString());
}
break;
case CREATED :
runOnUiThread(new Runnable() {
public void run() {
showToast("Room Created " + args[0]);
}
});
break;
case BROADCAST :
Log.e("Socked.onBROADCAST", args.toString());
break;
case JOIN :
Log.e("Socked.onJOIN", args.toString());
break;
case EMIT :
Log.e("Socked.onEMIT", args.toString());
Log.e("Socked.onBROADCOST", args.toString());
break;
case ERROR :
Log.e("Socked.onERROR", args.toString());
break;
default :
break;
}
}
});
try {
RoomId = roomid.getText().toString();
} catch (Exception e) {
}
socket.emit("create or join", RoomId);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public void oncancel(View view) {
}
public SocketEvent getEvent(String eventString) {
SocketEvent eventType;
try {
if (eventString.contains("log")) {
eventType = SocketEvent.LOG;
} else if (eventString.contains("created")) {
eventType = SocketEvent.CREATED;
} else if (eventString.contains("emit():")) {
eventType = SocketEvent.EMIT;
}
else if (eventString.contains("broadcast():")) {
eventType = SocketEvent.BROADCAST;
} else if (eventString.contains("message")) {
eventType = SocketEvent.MESSAGE;
} else if (eventString.toLowerCase().substring(0, 20).contains("join")) {
eventType = SocketEvent.JOIN;
} else {
eventType = SocketEvent.ERROR;
}
} catch (Exception e) {
eventType = SocketEvent.ERROR;
}
return eventType;
}
public static interface Events {
public void peerConnectionEvent(String clintID, VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender);
public void setFactory(PeerConnectionFactory factory);
public void setMessage(String message);
public void createOffer();
}
private void startCall(String clientID) {
pc_events.setFactory(factory);
pc_events.peerConnectionEvent(clientID, localRender, remoteRender);
}
public void showToast(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(Home.this, message, Toast.LENGTH_SHORT).show();
}
});
}
public void makeOffer(View v) {
pc_events.createOffer();
}
}
PeerEventHandler.java
public class peerEventHandler implements Events {
private PeerConnection peerConnection;
private PeerConnectionFactory factory;
PCObserver pcObserver = new PCObserver();
public LooperExecutor executor;
private MediaStream mediaStream;
private VideoSource videoSource;
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
private VideoCapturerAndroid videoCapturer;
private VideoTrack localVideoTrack;
private VideoTrack remoteVideoTrack;
public boolean preferIsac = false;
public boolean videoCallEnabled = true;
public boolean preferH264 = true;
private static final String VIDEO_CODEC_H264 = "VP8";
private static final String AUDIO_CODEC_ISAC = "ISAC";
private SessionDescription localSdp;
private final SDPObserver sdpObserver = new SDPObserver();
private LinkedList<IceCandidate> queuedRemoteCandidates;
public boolean isInitiator = false;
private MediaConstraints sdpMediaConstraints;
private VideoRenderer.Callbacks localRender;
private VideoRenderer.Callbacks remoteRender;
#Override
public void peerConnectionEvent(String clintID, Callbacks localRender, Callbacks remoteRender) {
this.localRender = localRender;
this.remoteRender = remoteRender;
creatPeerConnection();
}
public void creatPeerConnection() {
queuedRemoteCandidates = new LinkedList<IceCandidate>();
executor = new LooperExecutor();
executor.requestStart();
MediaConstraints pcConstraints = new MediaConstraints();
MediaConstraints videoConstraints = new MediaConstraints();
MediaConstraints audioConstraints = new MediaConstraints();
sdpMediaConstraints = new MediaConstraints();
creatPcConstrains(pcConstraints);
creatvideoConstraints(videoConstraints);
creatsdpMediaConstraints(sdpMediaConstraints);
List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>();
IceServer iceServer = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(iceServer);
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
// TCP candidates are only useful when connecting to a server that
// supports
// ICE-TCP.
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);
Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT), Logging.Severity.LS_WARNING);
mediaStream = factory.createLocalMediaStream("ARDAMS");
String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
String frontCameraDeviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice();
cameraDeviceName = frontCameraDeviceName;
videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);
videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
localVideoTrack.setEnabled(true);
localVideoTrack.addRenderer(new VideoRenderer(localRender));
mediaStream.addTrack(factory.createAudioTrack(AUDIO_TRACK_ID, factory.createAudioSource(audioConstraints)));
mediaStream.addTrack(localVideoTrack);
peerConnection.addStream(mediaStream);
}
#Override
public void createOffer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = true;
peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
}
}
});
}
public void createAnswer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = false;
peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
}
}
});
}
private class PCObserver implements PeerConnection.Observer {
#Override
public void onAddStream(final MediaStream stream) {
Log.e("onAddStream", "onAddStream");
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
// /reportError("Weird-looking stream: " + stream);
return;
}
if (stream.videoTracks.size() == 1) {
remoteVideoTrack = stream.videoTracks.get(0);
remoteVideoTrack.setEnabled(true);
remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
}
}
});
}
#Override
public void onDataChannel(DataChannel arg0) {
Log.e("On data channel", "Data Channel");
}
#Override
public void onIceCandidate(IceCandidate candidate) {
SocketIO socket = Home.socket;
JSONObject json = new JSONObject();
try {
json.putOpt("type", "candidate");
json.putOpt("label", candidate.sdpMLineIndex);
json.putOpt("id", candidate.sdpMid);
json.putOpt("candidate", candidate.sdp);
} catch (JSONException e) {
}
socket.emit("message", json);
}
#Override
public void onIceConnectionChange(IceConnectionState arg0) {
Log.e("onIceConnectionChange", "onIceConnectionChange");
}
#Override
public void onIceConnectionReceivingChange(boolean arg0) {
Log.e("onIceConnectionReceivingChange", "onIceConnectionReceivingChange");
}
#Override
public void onIceGatheringChange(IceGatheringState arg0) {
Log.e("onIceGatheringChange", "onIceGatheringChange");
}
#Override
public void onRemoveStream(MediaStream arg0) {
Log.e("onRemoveStream", "onRemoveStream");
}
#Override
public void onRenegotiationNeeded() {
Log.e("onRenegotiationNeeded", "onRenegotiationNeeded");
}
#Override
public void onSignalingChange(SignalingState arg0) {
Log.e("onSignalingChange", "onSignalingChange");
}
}
public void creatPcConstrains(MediaConstraints pcConstraints) {
pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
}
public void creatvideoConstraints(MediaConstraints videoConstraints) {
String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
int videoWidth = 0;
int videoHeight = 0;
// If VP8 HW video encoder is supported and video resolution is not
// specified force it to HD.
if ((videoWidth == 0 || videoHeight == 0) && true && MediaCodecVideoEncoder.isVp8HwSupported()) {
videoWidth = 1280;
videoHeight = 1280;
}
// Add video resolution constraints.
if (videoWidth > 0 && videoHeight > 0) {
videoWidth = Math.min(videoWidth, 1280);
videoHeight = Math.min(videoHeight, 1280);
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
}
// Add fps constraints.
int videoFps = 30;
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
}
public void creataudioConstraints(MediaConstraints pcConstraints) {
pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
}
public void creatsdpMediaConstraints(MediaConstraints sdpMediaConstraints) {
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", "true"));
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", "true"));
}
private class SDPObserver implements SdpObserver {
#Override
public void onCreateFailure(String arg0) {
System.out.print(arg0);
}
#Override
public void onCreateSuccess(SessionDescription origSdp) {
if (localSdp != null) {
return;
}
String sdpDescription = origSdp.description;
if (preferIsac) {
sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
}
if (videoCallEnabled && preferH264) {
sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
}
final SessionDescription sdp = new SessionDescription(origSdp.type, sdpDescription);
localSdp = sdp;
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
JSONObject json = new JSONObject();
try {
// peerConnection.setLocalDescription(null, localSdp);
json.putOpt("type", "offer");
json.putOpt("sdp", localSdp.description);
} catch (JSONException e) {
e.printStackTrace();
}
Home.socket.emit("message", json);
}
}
});
}
#Override
public void onSetFailure(String arg0) {
Log.e("onSetFailure", arg0);
}
#Override
public void onSetSuccess() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (isInitiator) {
createOffer();
} else {
createAnswer();
}
}
});
}
}
private static String preferCodec(String sdpDescription, String codec, boolean isAudio) {
String[] lines = sdpDescription.split("\r\n");
int mLineIndex = -1;
String codecRtpMap = null;
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding
// parameters>]
String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$";
Pattern codecPattern = Pattern.compile(regex);
String mediaDescription = "m=video ";
if (isAudio) {
mediaDescription = "m=audio ";
}
for (int i = 0; (i < lines.length) && (mLineIndex == -1 || codecRtpMap == null); i++) {
if (lines[i].startsWith(mediaDescription)) {
mLineIndex = i;
continue;
}
Matcher codecMatcher = codecPattern.matcher(lines[i]);
if (codecMatcher.matches()) {
codecRtpMap = codecMatcher.group(1);
continue;
}
}
if (mLineIndex == -1) {
// Log.w(TAG, "No " + mediaDescription + " line, so can't prefer " +
// codec);
return sdpDescription;
}
if (codecRtpMap == null) {
// Log.w(TAG, "No rtpmap for " + codec);
return sdpDescription;
}
// Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap +
// ", prefer at "
// + lines[mLineIndex]);
String[] origMLineParts = lines[mLineIndex].split(" ");
if (origMLineParts.length > 3) {
StringBuilder newMLine = new StringBuilder();
int origPartIndex = 0;
// Format is: m=<media> <port> <proto> <fmt> ...
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
newMLine.append(codecRtpMap);
for (; origPartIndex < origMLineParts.length; origPartIndex++) {
if (!origMLineParts[origPartIndex].equals(codecRtpMap)) {
newMLine.append(" ").append(origMLineParts[origPartIndex]);
}
}
lines[mLineIndex] = newMLine.toString();
// Log.d(TAG, "Change media description: " + lines[mLineIndex]);
} else {
// Log.e(TAG, "Wrong SDP media description format: " +
// lines[mLineIndex]);
}
StringBuilder newSdpDescription = new StringBuilder();
for (String line : lines) {
newSdpDescription.append(line).append("\r\n");
}
return newSdpDescription.toString();
}
private void drainCandidates() {
if (queuedRemoteCandidates != null) {
// Log.d(TAG, "Add " + queuedRemoteCandidates.size() +
// " remote candidates");
for (IceCandidate candidate : queuedRemoteCandidates) {
// if(!candidate.sdpMid.contains("video"))
peerConnection.addIceCandidate(candidate);
}
queuedRemoteCandidates = null;
}
}
public void addRemoteIceCandidate(final IceCandidate candidate) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
if (queuedRemoteCandidates != null) {
queuedRemoteCandidates.add(candidate);
} else {
peerConnection.addIceCandidate(candidate);
}
}
}
});
}
public void setRemoteDescription(final SessionDescription sdp) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
String sdpDescription = sdp.description;
if (preferIsac) {
sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
}
if (videoCallEnabled && preferH264) {
sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
}
SessionDescription sdpRemote = new SessionDescription(sdp.type, sdpDescription);
peerConnection.setRemoteDescription(sdpObserver, sdpRemote);
}
});
}
#Override
public void setFactory(PeerConnectionFactory factory) {
this.factory = factory;
}
public void onWebSocketMessage(final String msg) {
try {
Log.e("onWebSocketMessage", msg);
JSONObject json = new JSONObject(msg);
json = new JSONObject(msg);
String type = json.optString("type");
if (type.equals("candidate")) {
IceCandidate candidate = new IceCandidate(json.getString("id"), json.getInt("label"), json.getString("candidate"));
addRemoteIceCandidate(candidate);
} else if (type.equals("answer")) {
if (isInitiator) {
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else {
}
} else if (type.equals("offer")) {
if (!isInitiator) {
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else {
}
} else if (type.equals("bye")) {
} else {
}
} catch (JSONException e) {
}
}
#Override
public void setMessage(String message) {
if (message.toString().contains("got user media") || message.toString().contains("bye")) {
} else
onWebSocketMessage(message);
}
}
I don't know why but I changed target=android-19 to target=android-21 and everything worked fine as desired (Except Datachannel).
Final Working Code
PS: You can use this code without any royalty and Guarantees :p
Home.java
public class Home extends Activity {
public List<PeerConnection.IceServer> iceServers;
private GLSurfaceView videoView;
public static SocketIO socket;
ArrayList<String> userIDs = new ArrayList<>();
private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
String RoomId = "";
String sreverURL = "http://xx.xx.xx.xx:xxxx/";
private EditText roomid;
private VideoRenderer.Callbacks remote_view;
private VideoRenderer.Callbacks local_view;
protected PeerConnectionFactory factory;
PeerConnectionFactory.Options options = null;
Events pc_events;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
videoView = (GLSurfaceView) findViewById(R.id.glview_call_remote);
VideoRendererGui.setView(videoView, new Runnable() {
#Override
public void run() {
createPeerConnectionFactory();
}
});
remote_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FIT, false);
local_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, true);
iceServers = new ArrayList<>();
IceServer icc = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(icc);
roomid = (EditText) findViewById(R.id.roomId);
Random rand = new Random();
roomid.setText("" + rand.nextInt(9999));
pc_events = new peerEventHandler();
}
private void createPeerConnectionFactory() {
runOnUiThread(new Runnable() {
#Override
public void run() {
PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
PeerConnectionFactory.initializeAndroidGlobals(Home.this, true, true, true, VideoRendererGui.getEGLContext());
try {
factory = new PeerConnectionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void ondail(View view) {
try {
try {
SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
socket = new SocketIO();
socket.connect(sreverURL, new IOCallback() {
#Override
public void onMessage(JSONObject json, IOAcknowledge ack) {
}
#Override
public void onMessage(String data, IOAcknowledge ack) {
}
#Override
public void onError(SocketIOException socketIOException) {
socketIOException.printStackTrace();
}
#Override
public void onDisconnect() {
}
#Override
public void onConnect() {
showToast("Connected to " + sreverURL);
}
#Override
public void on(final String event, IOAcknowledge ack, final Object... args) {
Log.e("Socked.on", event + ", " + args);
switch (getEvent(event)) {
case LOG :
break;
case MESSAGE :
if (args instanceof Object[]) {
pc_events.setMessage(args[0].toString());
} else {
pc_events.setMessage(args.toString());
}
break;
case CREATED :
runOnUiThread(new Runnable() {
public void run() {
showToast("Room Created " + args[0]);
}
});
break;
case BROADCAST :
break;
case JOIN :
break;
case EMIT :
Log.e("Socked.onEMIT", args.toString());
startCall();
pc_events.createOffer();
break;
case ERROR :
Log.e("Socked.onERROR", args.toString());
break;
default :
break;
}
}
});
try {
RoomId = roomid.getText().toString();
} catch (Exception e) {
}
socket.emit("create or join", RoomId);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public void oncancel(View view) {
}
public SocketEvent getEvent(String eventString) {
SocketEvent eventType;
try {
if (eventString.contains("log")) {
eventType = SocketEvent.LOG;
} else if (eventString.contains("created")) {
eventType = SocketEvent.CREATED;
} else if (eventString.contains("emit():")) {
eventType = SocketEvent.EMIT;
}
else if (eventString.contains("broadcast():")) {
eventType = SocketEvent.BROADCAST;
} else if (eventString.contains("message")) {
eventType = SocketEvent.MESSAGE;
} else if (eventString.toLowerCase().substring(0, 20).contains("join")) {
eventType = SocketEvent.JOIN;
} else {
eventType = SocketEvent.ERROR;
}
} catch (Exception e) {
eventType = SocketEvent.ERROR;
}
return eventType;
}
public static interface Events {
public void peerConnectionEvent(VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender);
public void setFactory(PeerConnectionFactory factory);
public void setMessage(String message);
public void createOffer();
public void sendMessage(String msg);
}
private void startCall() {
pc_events.setFactory(factory);
pc_events.peerConnectionEvent(remote_view, local_view);
}
public void showToast(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(Home.this, message, Toast.LENGTH_SHORT).show();
}
});
}
public void makeOffer(View v) {
pc_events.sendMessage("Hello");
}
}
peerEventHandler.java
public class peerEventHandler implements Events {
private PeerConnection peerConnection;
private PeerConnectionFactory factory;
PCObserver pcObserver = new PCObserver();
public LooperExecutor executor;
private MediaStream mediaStream;
private VideoSource videoSource;
private DcObserver dc_observer;
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
private VideoCapturerAndroid videoCapturer;
private VideoTrack localVideoTrack;
private VideoTrack remoteVideoTrack;
public boolean preferIsac = false;
public boolean videoCallEnabled = true;
public boolean preferH264 = false;
private SessionDescription localSdp;
private final SDPObserver sdpObserver = new SDPObserver();
public boolean isInitiator = false;
private MediaConstraints sdpMediaConstraints;
private VideoRenderer.Callbacks remote_view;
private VideoRenderer.Callbacks local_view;
private DataChannel dataChannel;
#Override
public void peerConnectionEvent(Callbacks remoteRender, Callbacks localRender) {
this.remote_view = remoteRender;
this.local_view = localRender;
creatPeerConnection();
}
public void creatPeerConnection() {
executor = new LooperExecutor();
executor.requestStart();
MediaConstraints pcConstraints = new MediaConstraints();
MediaConstraints videoConstraints = new MediaConstraints();
MediaConstraints audioConstraints = new MediaConstraints();
sdpMediaConstraints = new MediaConstraints();
creatPcConstrains(pcConstraints);
creatvideoConstraints(videoConstraints);
creatsdpMediaConstraints(sdpMediaConstraints);
List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>();
IceServer iceServer = new IceServer("stun:stun.l.google.com:19302", "", "");
iceServers.add(iceServer);
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);
Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT), Logging.Severity.LS_WARNING);
mediaStream = factory.createLocalMediaStream("ARDAMS");
String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
String frontCameraDeviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice();
cameraDeviceName = frontCameraDeviceName;
videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);
videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
localVideoTrack.setEnabled(true);
localVideoTrack.addRenderer(new VideoRenderer(local_view));
mediaStream.addTrack(factory.createAudioTrack(AUDIO_TRACK_ID, factory.createAudioSource(audioConstraints)));
mediaStream.addTrack(localVideoTrack);
peerConnection.addStream(mediaStream);
dataChannel = peerConnection.createDataChannel("sendDataChannel", new DataChannel.Init());
dc_observer = new DcObserver();
dataChannel.registerObserver(dc_observer);
}
#Override
public void createOffer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = true;
peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
}
}
});
}
public void createAnswer() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection != null) {
isInitiator = false;
peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
}
}
});
}
private class PCObserver implements PeerConnection.Observer {
#Override
public void onAddStream(final MediaStream stream) {
Log.e("onAddStream", "onAddStream");
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
// /reportError("Weird-looking stream: " + stream);
return;
}
if (stream.videoTracks.size() == 1) {
remoteVideoTrack = stream.videoTracks.get(0);
remoteVideoTrack.setEnabled(true);
remoteVideoTrack.addRenderer(new VideoRenderer(remote_view));
VideoRendererGui.update(local_view, 75, 70, 60, 60, ScalingType.SCALE_ASPECT_FIT, true);
VideoRendererGui.update(remote_view, 0, 0, 200, 200, ScalingType.SCALE_ASPECT_FILL, false);
}
}
});
}
#Override
public void onDataChannel(final DataChannel dc) {
executor.execute(new Runnable() {
#Override
public void run() {
dataChannel = dc;
String channelName = dataChannel.label();
dataChannel.registerObserver(new DcObserver());
}
});
}
#Override
public void onIceCandidate(IceCandidate candidate) {
SocketIO socket = Home.socket;
JSONObject json = new JSONObject();
try {
json.putOpt("type", "candidate");
json.putOpt("label", candidate.sdpMLineIndex);
json.putOpt("id", candidate.sdpMid);
json.putOpt("candidate", candidate.sdp);
} catch (JSONException e) {
e.printStackTrace();
}
socket.emit("message", json);
}
#Override
public void onIceConnectionChange(IceConnectionState arg0) {
}
#Override
public void onIceConnectionReceivingChange(boolean arg0) {
}
#Override
public void onIceGatheringChange(IceGatheringState arg0) {
}
#Override
public void onRemoveStream(MediaStream arg0) {
}
#Override
public void onRenegotiationNeeded() {
}
#Override
public void onSignalingChange(SignalingState arg0) {
}
}
public void creatPcConstrains(MediaConstraints pcConstraints) {
pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
pcConstraints.optional.add(new KeyValuePair("internalSctpDataChannels", "true"));
}
public void creatvideoConstraints(MediaConstraints videoConstraints) {
String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
int videoWidth = 0;
int videoHeight = 0;
if ((videoWidth == 0 || videoHeight == 0) && true && MediaCodecVideoEncoder.isVp8HwSupported()) {
videoWidth = 1280;
videoHeight = 1280;
}
if (videoWidth > 0 && videoHeight > 0) {
videoWidth = Math.min(videoWidth, 1280);
videoHeight = Math.min(videoHeight, 1280);
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
}
int videoFps = 30;
videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
}
public void creataudioConstraints(MediaConstraints pcConstraints) {
pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
pcConstraints.optional.add(new KeyValuePair("internalSctpDataChannels", "true"));
}
public void creatsdpMediaConstraints(MediaConstraints sdpMediaConstraints) {
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", "true"));
sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", "true"));
}
private class SDPObserver implements SdpObserver {
#Override
public void onCreateFailure(String arg0) {
System.out.print(arg0);
}
#Override
public void onCreateSuccess(SessionDescription origSdp) {
if (localSdp != null) {
return;
}
localSdp = origSdp;
setLocalDescription(origSdp);
}
#Override
public void onSetFailure(String arg0) {
}
#Override
public void onSetSuccess() {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
if (isInitiator) {
if (peerConnection != null) {
JSONObject json = new JSONObject();
try {
json.putOpt("type", localSdp.type.toString().toLowerCase());
json.putOpt("sdp", localSdp.description);
} catch (JSONException e) {
e.printStackTrace();
}
Home.socket.emit("message", json);
}
} else {
// createAnswer();
}
}
});
}
}
public void addRemoteIceCandidate(final IceCandidate candidate) {
executor.execute(new Runnable() {
#Override
public void run() {
peerConnection.addIceCandidate(candidate);
}
});
}
public void setLocalDescription(final SessionDescription sdp) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
peerConnection.setLocalDescription(sdpObserver, sdp);
}
});
}
public void setRemoteDescription(final SessionDescription sdp) {
executor.execute(new Runnable() {
#Override
public void run() {
if (peerConnection == null) {
return;
}
peerConnection.setRemoteDescription(sdpObserver, sdp);
}
});
}
#Override
public void setFactory(PeerConnectionFactory factory) {
this.factory = factory;
}
public void onWebSocketMessage(final String msg) {
try {
Log.e("onWebSocketMessage", msg);
JSONObject json = new JSONObject(msg);
json = new JSONObject(msg);
String type = json.optString("type");
if (type.equals("candidate")) {
IceCandidate candidate = new IceCandidate(json.getString("id"), json.getInt("label"), json.getString("candidate"));
addRemoteIceCandidate(candidate);
} else if (type.equals("answer")) {
isInitiator = false;
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else if (type.equals("offer")) {
SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
setRemoteDescription(sdp);
} else if (type.equals("bye")) {
} else {
}
} catch (JSONException e) {
}
}
#Override
public void setMessage(String message) {
if (message.toString().contains("got user media") || message.toString().contains("bye")) {
} else
onWebSocketMessage(message);
}
private class DcObserver implements DataChannel.Observer {
#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("onMessage ", command);
}
#Override
public void onStateChange() {
Log.e("onStateChange ", "onStateChange");
}
#Override
public void onBufferedAmountChange(long arg0) {
Log.e("onMessage ", "" + arg0);
}
}
#Override
public void sendMessage(String msg) {
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
boolean sent = dataChannel.send(new DataChannel.Buffer(buffer, false));
if (sent) {
Log.e("Message sent", "" + sent);
}
}
}