I'm about to do some refactoring off my app and I came to think about this simple yet complex topic, logging, how can it be so hard to do clean, effective and informative logging...
When you read documentation on logging you often see this snippet
if (BuildConfig.DEBUG) {
Log.d(TAG + "message");
}
and it makes me wonder what the purpose is with it? According to the documentation, Android Developer - Log, the debug log messages are compiled in but stripped at runtime, so you wouldn't need to have the log call within that if statement. Or am I missunderstanding anything?
Then I'm also kind of wondering what the real benefit is with using any other Log.x() calls except debug as the log entries will not be seen by the user or logged into some errorfile, so they will be compiled in and executed in the production environment for no purpose at all? This is maybe a use case for the if statement before?
I mentioned earlier that the log entry isn't logged into a file. Why isn't this a built in feature in Android? Is it because of performance issues, unnecessary permission usage or something else? I have implemented this functionality in my own logging class, but now I wonder if it's bad practice? But it's also nice to have logs with important log entries?
So to wrap it up, to implement clean, effective and informative logging, both during development and in production. What's the best practices?
Logs only needed to debug applications during development, to ensure that the feature works as expected and produces desired results. The best practice is, I believe, to do logging in any way which is most convenient to you and allows to find and resolve issues as quickly and efficiently as possible
I mentioned earlier that the log entry isn't logged into a file. Why
isn't this a built in feature in Android?
Who (except a programmer on the development stage) would want an application to waste limited storage space on one's device with useless data? Users don't see, don't read, don't use logs. They don't need this garbage. Application in production must not produce any logs and, of course, must not save them to files.
The only logging which should be implemented in released application is unhandled exceptions logging. Moreover, it's application's responsibility to handle these logs if it suggests sending crash reports, and removing them after the report has been sent.
Another reason logs should not be created by released apps is that they may contain sensitive data and output which requires user authorization, thus introducing security flaws.
I believe the best practice is to remove all logs as soon as the module or feature is fully implemented and thoroughly tested, before deployment to production. Introducing if (BuildConfig.DEBUG) condition helps to ensure this has been achieved.
This will generate clean debug tags with this format ClasssName[MethodName] - LineNumber with reflection.
Complete code with inline comments is available as a gist here.
import android.util.Log;
public class Logger {
public enum LOGGER_DEPTH {
ACTUAL_METHOD(4),
LOGGER_METHOD(3),
STACK_TRACE_METHOD(1),
JVM_METHOD(0);
private final int value;
private LOGGER_DEPTH(final int newValue) {
value = newValue;
}
public int getValue() {
return value;
}
}
private static final String personalTAG = "Logger";
private StringBuilder sb;
private Logger() {
if (LoggerLoader.instance != null) {
Log.e(personalTAG, "Error: Logger already instantiated");
throw new IllegalStateException("Already Instantiated");
} else {
this.sb = new StringBuilder(255);
}
}
public static Logger getLogger() {
return LoggerLoader.instance;
}
private String getTag(LOGGER_DEPTH depth) {
try {
String className = Thread.currentThread().getStackTrace()[depth.getValue()].getClassName();
sb.append(className.substring(className.lastIndexOf(".") + 1));
sb.append("[");
sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getMethodName());
sb.append("] - ");
sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getLineNumber());
return sb.toString();
} catch (Exception ex) {
ex.printStackTrace();
Log.d(personalTAG, ex.getMessage());
} finally {
sb.setLength(0);
}
return null;
}
public void d(String msg) {
try {
Log.d(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void d(String msg, LOGGER_DEPTH depth) {
try {
Log.d(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void d(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.d(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg) {
try {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg, LOGGER_DEPTH depth) {
try {
Log.e(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.e(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg) {
try {
Log.w(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg, LOGGER_DEPTH depth) {
try {
Log.w(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.w(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg) {
try {
Log.v(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg, LOGGER_DEPTH depth) {
try {
Log.v(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.v(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg) {
try {
Log.i(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg, LOGGER_DEPTH depth) {
try {
Log.i(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.i(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg) {
try {
Log.wtf(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg, LOGGER_DEPTH depth) {
try {
Log.wtf(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.wtf(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
private static class LoggerLoader {
private static final Logger instance = new Logger();
}
}
I've stumbled with that very same problem since I started working in Android and I have created this open source project(Android Studio Macros) that allows you to do what you want plus some more complicated things by using "//<#DEBUG_AREA> and //<#/DEBUG_AREA>" tags within your code, the basic idea is that anything within those tags will be commented when you change your build variants for example if you have something like this in a for loop:
//=========This piece of code is only for logging purposes...=========
Log.e("LogUserInfo", "Name: " + name);
Log.e("LogUserInfo", "Id: " + user.getId());
Log.e("LogUserInfo", "Id: " + user.getDistance());
//====================================================================
In stead of doing this:
if(DEBUG){
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
}
With this macro you can do this(full method):
private List<String> getNamesOfUsersNearMe(String zipCode){
List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
if(users == null || users.size() < 1){
return null;
}
List<String> names = new ArrayList<String>();
int totalUsers = users.size();
for(int i = 0; i < totalUsers; i++){
User user = users.get(i);
String name = user.getName();
names.add(name);
//<#DEBUG_AREA>
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
//</#DEBUG_AREA>
}
return names;
}
And when you change your build variant to release it would become something like this:
private List<String> getNamesOfUsersNearMe(String zipCode){
List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
if(users == null || users.size() < 1){
return null;
}
List<String> names = new ArrayList<String>();
int totalUsers = users.size();
for(int i = 0; i < totalUsers; i++){
User user = users.get(i);
String name = user.getName();
names.add(name);
/*<#DEBUG_OFF>
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
</#DEBUG_OFF>*/
}
return names;
}
Which is way better in performance for long loops and makes your code cleaner by getting rid of the unnecessary code while in "release" mode, of course if you go back to "debug" it will uncomment the area and leave it the way it originally was with the "<#DEBUG_AREA>" tags...
Also trying to fit the most common scenarios, seems like there's times when you don't need a full area to get rid of but instead only one single Log, so for that case the project also has a Log wrapper class that you can use as follows:
if(users == null || users.size() < 1){
ASDebuggerMacroLog.e("LogUserInfo", "There's no users available near me...");
return null;
}
The line of code used by the class "ASDebuggerMacroLog" will be commented after changing to "release" mode in your Android Studio.
Hope it Helps!
Regards!
Standard outputs should not be used directly to log anything (squid:S106)
When logging a message there are several important requirements which must be fulfilled:
The user must be able to easily retrieve the logs
The format of all logged message must be uniform to allow the user to easily read
the log
Logged data must actually be recorded
Sensitive data must only be logged securely
If a program directly writes to the standard outputs, there is absolutely no way to comply with those requirements. That's why defining and using a dedicated logger is highly recommended.
Source: Sonarcloud
I highly recommend using Timber library: https://github.com/JakeWharton/timber
It's a very small library on top of Android Log class which takes care of all logging requirements easily. some features:
It automatically figures out which class is being called and use its name as TAG
You can plant different Tree for each build type
All logs going through a central place in Tree. so you process them or upload them somewhere if needed.
Related
Where should I place/split the following code?
The ViewModel shouldn't be responsible for business logic, but I also don't know how to handle the UseCases, have two 'special' UseCases for updating the Ip and the object, which then depend on a general UseCase (SendDataToWatch) to actually send the data? In that case, where should I get the Nodes list and decide to which node to send the message, in ViewModel?
Or should I pass the context to the Repository, but then, which one? (ConnectionDataRep, CmdObjectsRep or one created for this purpose).
Also, regarding the ThreadExecutor, since it is expensive to create, should I pass it from the ViewModel, or create it in each UseCase/Repository?
private Collection<String> getNodes() {
HashSet<String> results = new HashSet<>();
Wearable.getCapabilityClient()
Task<CapabilityInfo> nodeListTask = Wearable.getCapabilityClient(context).getCapability(WATCH_CAPABILITY, CapabilityClient.FILTER_REACHABLE);
try {
// Block on a task and get the result synchronously (because this is on a background
// thread).
Set<Node> nodes = Tasks.await(nodeListTask).getNodes();
for (Node node : nodes) {
results.add(node.getId());
}
} catch (ExecutionException exception) {
Log.e(TAG, "Task failed: " + exception);
} catch (InterruptedException exception) {
Log.e(TAG, "Interrupt occurred: " + exception);
}
return results;
}
private void updateAddrOnWatch(String node, String message) {
sendMessageToEndpoint(node, KeysConstants.UPDATE_CONN_PATH, strToBytes(message));
}
private void updateCmdObjects(String node, String jsonObjectsArr) {
sendMessageToEndpoint(node, KeysConstants.UPDATE_OBJECTS_PATH, strToBytes(jsonObjectsArr));
}
private void sendMessageToEndpoint(String node, String endpoint, byte[] message) {
Task<Integer> sendMessageTask =
Wearable.getMessageClient(context)
.sendMessage(node, endpoint, message);
try {
// Block on a task and get the result synchronously (because this is on a background
// thread).
Integer result = Tasks.await(sendMessageTask);
Log.d(TAG, "Message sent: " + result);
} catch (ExecutionException exception) {
Log.e(TAG, "Task failed: " + exception);
} catch (InterruptedException exception) {
Log.e(TAG, "Interrupt occurred: " + exception);
}
}
Is there anyone here had an experience with having an error when getting the resourceId in the context?
I am using context.getResourceID() but it says a NotFoundException Error.
I am fetching a layout resource just to be specific.
I already tried cleaning and rebuilding it but I haven't got any luck.
By the way I'm creating a library to be used for creating an Adobe Native Extension.
Here is my sample code:
public class RunVRFunction implements FREFunction {
public static final String TAG = "RunVRFunction";
#Override
public FREObject call(FREContext extContext, FREObject[] args) {
int layoutID;
Intent i = new Intent(extContext.getActivity(), Main2Activity.class);
try {
layoutID = extContext.getResourceId("layout.activity_main2");
} catch (Exception e) {
System.out.printf("getResourceID() failed with error:\n\"%s\"\n",
e.toString());
Toast.makeText(extContext.getActivity(), "getResourceID() failed" +
e.toString(), Toast.LENGTH_SHORT).show();
return null;
}
i.putExtra("layoutID", layoutID);
try {
extContext.getActivity().startActivity(i);
} catch (Exception e) {
System.out.printf("startActivity() failed with error:\n\"%s\"\n",
e.toString());
Toast.makeText(extContext.getActivity(), "startActivity() failed: " +
e.toString(), Toast.LENGTH_SHORT).show();
}
return null;
}
}
Here is the error in logcat:
android.content.res.Resources$NotFoundException: layout.activity_main2
Thanks.
I'm using the following:
GoogleApiClient mApiClient = new GoogleApiClient.Builder(this)
.addApi( Wearable.API )
...
Since Wearable.API is deprecated? What is the appropriate replacement?
I found something nice which is helpful
private class StartWearableActivityTask extends AsyncTask<Void, Void, Void> {
final String key;
public StartWearableActivityTask(String msg){
key = msg;
}
#Override
protected Void doInBackground(Void... args) {
Collection<String> nodes = getNodes();
for (String node : nodes) {
sendStartActivityMessage(node,key);
}
return null;
}
}
#WorkerThread
private Collection<String> getNodes() {
HashSet<String> results = new HashSet<>();
Task<List<Node>> nodeListTask =
Wearable.getNodeClient(getApplicationContext()).getConnectedNodes();
try {
// Block on a task and get the result synchronously (because this is on a background
// thread).
List<Node> nodes = Tasks.await(nodeListTask);
for (Node node : nodes) {
results.add(node.getId());
}
} catch (ExecutionException exception) {
Log.e(TAG, "Task failed: " + exception);
} catch (InterruptedException exception) {
Log.e(TAG, "Interrupt occurred: " + exception);
}
return results;
}
#WorkerThread
private void sendStartActivityMessage(String node,String event) {
Task<Integer> sendMessageTask =
Wearable.getMessageClient(this).sendMessage(node, event, new byte[0]);
try {
// Block on a task and get the result synchronously (because this is on a background
// thread).
Integer result = Tasks.await(sendMessageTask);
} catch (ExecutionException exception) {
Log.e(TAG, "Task failed: " + exception);
} catch (InterruptedException exception) {
Log.e(TAG, "Interrupt occurred: " + exception);
}
}
I found answer here:
https://developer.android.com/training/wearables/data-layer/migrate-to-googleapi
Migrate Wear apps to GoogleApi
Starting with version 11.8.0 of Google Play services, Wear OS apps should migrate away from the GoogleApiClient class and instead use client objects that are based on the GoogleApi class.
Use of GoogleApi makes it easier to set up asynchronous operations. For example, as described in the introduction to the Tasks API, you can obtain a Task object instead of a PendingResult object.
I'm using azure sdk for android and follow the tutorial https://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-android-get-started-data/.
When I'm trying to connect and insert data to mobile service table all is ok, but when I query the table in activity my app gets stuck, though there are only several entries in the table and execute method successfully returns Future.
public static MobileServiceClient mClient;
public static void connect(Context context) {
try {
mClient = new MobileServiceClient(storageLink, key, context);
} catch (MalformedURLException e) {
Log.e("AzureService.connect", "Storage access failed" + storageLink);
}
}
public static InstallationData get(final String deviceId) {
MobileServiceTable<InstallationData> table= mClient.getTable(InstallationData.class);
final MobileServiceList<InstallationData> result;
try {
result = table.where().field("deviceid").eq(deviceId).execute().get();
for (InstallationData item : result) {
return item;
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
public static void store(final InstallationData item) {
mClient.getTable(InstallationData.class).insert(item, new TableOperationCallback<InstallationData>() {
public void onCompleted(InstallationData entity, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
Log.d("AzureService.store()", "Data about " + item.getDeviceid() + "" + "is successfully updated");
} else {
exception.printStackTrace();
Log.e("AzureService.store()", "Data about " + item.getDeviceid() + "" + "is failed to update");
}
}
});
}
Thank you in advance!
I am developing one chat application , But it not work properly, giving different-differet error like 406 or 407 , So please advice me My following code for that is proper or not ,
First Login When Application Start :
public void LoginWithUser() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
SASLAuthentication.unregisterSASLMechanism("org.jivesoftware.smack.sasl.javax.SASLDigestMD5Mechanism");
SmackInitialization initialization = new SmackInitialization();
XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder();
config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
config.setServiceName(Constants.SERVICE);
config.setHost(Constants.HOST);
config.setPort(Constants.PORT);
config.setResource("myresource");
config.setDebuggerEnabled(true);
config.setKeystoreType("AndroidCAStore");
config.setConnectTimeout(100000);
try {
config.setUsernameAndPassword(getUserName(), password);
} catch (Exception e) {
e.getMessage();
}
Constants.connection = new XMPPTCPConnection(config.build()); //new XMPPConnection(Constants.connConfig);
try {
if (!Constants.connection.isConnected()) {
Constants.connection.connect();
}
Log.i("ChatActivity", "Connected to "
+ Constants.connection.getHost());
} catch (XMPPException ex) {
Log.e("ChatActivity",
"Failed to connect to " + Constants.connection.getHost());
Log.e("ChatActivity", ex.toString());
// setConnection(null);
} catch (IOException | SmackException e) {
e.printStackTrace();
Log.e("my error outer", e.getMessage() + " <- Understand 0 ? !!!");
}
try {
Log.d("chat : user name", getUserName());
try {
Log.d("chat : password", AESCrypt.decrypt(Constants.key_store_pair, enc_login_key));
} catch (Exception e) {
e.printStackTrace();
}
try {
SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
Constants.connection.login(getUserName(), password);
Constants.connection.getServiceName();
Log.d("service name=", Constants.connection.getServiceName());
} catch (Exception e) {
e.printStackTrace();
Log.e("my error inner", e.getMessage() + " <- Understand 1 ? !!!");
}
Log.i("ChatActivity", "Logged in as "
+ Constants.connection.getUser());
Log.i("You are valid user",
"your Token is " + Constants.connection.getUser());
} catch (Exception ex) {
}
}
});
t.start();
}
It connected Successfully...
After when I creating New Conference Room at that time i register that conference room by calling following method :
private MultiUserChat createGroupChat(XMPPConnection connection,String room_id, String groupName, String registered_beam_iddd) throws XMPPException, SmackException {
// This code call when creating new conference room
Constants.mucM = MultiUserChatManager.getInstanceFor(connection); //new MultiUserChatManager(connection, registered_beam_iddd + "#" + groupName);
Constants.muc = Constants.mucM.getMultiUserChat(registered_beam_iddd + "#" + groupName);
if(Constants.connection.isConnected())
{
if(Constants.connection.isAuthenticated()) {
Constants.muc.createOrJoin(room_id + "#" + groupName); //
Form form = Constants.muc.getConfigurationForm();
Form submitForm = form.createAnswerForm();
Constants.muc.sendConfigurationForm(submitForm);
Log.d("Room Created : Name : " , room_id + "#" + groupName);
}
else
{
//Toast.makeText(getActivity(),"Authenicated false",Toast.LENGTH_LONG).show();
Log.d("ooo", "authentication failed in AddBeam");
}
}
else
{
Toast.makeText(getActivity(),"Connection Loss",Toast.LENGTH_LONG).show();
}
return Constants.muc;
//onesecond
}
When I Click on number of conference room ( listview ) , that means when i enter on any of my conference room , i call following method to join room and get history,
private MultiUserChat joinGroupChat(XMPPConnection connection, String room_id, String groupName, String registered_beam_iddd) throws XMPPException, SmackException {
// This code called when user enter in Chat-conference room
if (Constants.connection.isConnected()) {
if (Constants.muc == null) {
Constants.mucM = MultiUserChatManager.getInstanceFor(connection); //new MultiUserChatManager(connection, registered_beam_iddd + "#" + groupName);
Constants.muc = Constants.mucM.getMultiUserChat(registered_beam_iddd + "#" + groupName);
}
if (Constants.connection.isAuthenticated()) {
if (!Constants.muc.isJoined()) {
DiscussionHistory history = new DiscussionHistory();
history.setMaxStanzas(20);
Constants.muc.join(room_id + "#" + groupName, "password", history, Constants.connection.getPacketReplyTimeout()); // #conference.tubsystems.com
} else {
Log.d("joined: ", room_id + "#" + groupName);
}
} else {
Log.d("ooo", "authentication failed in AddBeam");
}
} else {
Toast.makeText(getApplicationContext(), "Connection Loss", Toast.LENGTH_LONG).show();
try {
Constants.connection.connect();
Log.i("ChatActivity", "Connected to "
+ Constants.connection.getHost());
} catch (Exception ex) {
Log.e("ChatActivity",
"Failed to connect to " + Constants.connection.getHost());
Log.e("ChatActivity", ex.toString());
// setConnection(null);
}
}
return Constants.muc;
//onesecond
}
For Sending Message to conference :
private void sendmessage(String text, String room) {
String to = beamId + "#"+groupname;
Message msg = new Message(to, Message.Type.groupchat);
msg.setBody(text);
if (Constants.connection != null) {
try {
Constants.connection.sendPacket(msg);
Log.d("Send to room : Name : ", to);
} catch (Exception e) {
Log.d("ooo", "msg exception" + e.getMessage());
}
messages.add(text);
msg_send_receive_val = 1;
new setListAdapter().execute();
}
}
But it giving Some time connection error , some time 406 or 407 error and when i first time enter in the conference room at that time only it display past history message and then automatically removed, and message also not sending some times and some times sending , i dont know what is problem , while sending message it giving 406-407 modify- not acceptable error and some times giving other error.
I don't know but anything missing in above code , or anything other required to configure conference room ? Please help me as much fast as possible.
Thanks in advance.