Paho Android client drops connection when a message is shown - android

I am trying to use the basic Eclipse Paho MQTT client version 1.1.0 to connect to a CloudAMQP RabbitMQ instance, subscribe to a topic, and receive messages (which I send from the web admin console).
It works well if the application sends all message payloads to the Log output.
If the application adds the message to a TextView, the message appears, but the connection is dropped immediately and no more message are received.
The full project is available at GitHub. A simple example is below.
There is a service-based MQTT Paho client, but I thought that for very simple applications the basic client should be able to receive and show messages in the Android app UI.
...
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class MainActivity extends AppCompatActivity implements MqttCallback {
private static final String TAG = "main";
private Connection connection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
configureUI();
}
private Button buttonConnect;
private TextView messageWindow;
private void configureUI() {
buttonConnect = (Button) findViewById(R.id.buttonConnect);
messageWindow = (TextView) findViewById(R.id.messageWindow);
buttonConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String s = "***";
String d = "test";
String u = "***";
String p = "***";
if (connection != null && connection.isConnected()) {
connection.disconnect();
connection = null;
messageWindow.setText(String.format("Disconnected from server %s",
new Object[]{s}));
return;
}
messageWindow.setText(String.format("Connecting to server %s as user %s",
new Object[]{s, u}));
connection = new Connection(MainActivity.this, MainActivity.this, s, u, p);
connection.connect();
if (connection.isConnected()) {
messageWindow.append("\n\n");
messageWindow.append(String.format("Connected, listening for messages from topic %s",
new Object[]{d}));
connection.subscribe(d);
}
}
});
}
#Override
public void connectionLost(Throwable cause) {
Log.e(TAG, "connectionLost" + cause.getMessage());
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String msg = new String(message.getPayload());
Log.i(TAG, "Message Arrived: " + msg);
// messageWindow.append(msg);
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.i(TAG, "Delivery Complete!");
}
class Connection {
private static final String TAG = "conn";
private static final String protocol = "tcp://";
private static final int port = 1883;
private static final int version = MqttConnectOptions.MQTT_VERSION_3_1_1;
private static final int keepAliveSeconds = 20 * 60;
private final Context context;
private MqttClient client;
private final String server;
private final String user;
private final String pass;
private final MqttConnectOptions options = new MqttConnectOptions();
public Connection(Context ctx, MqttCallback mqttCallback, String server, String user, String pass) {
this.context = ctx;
this.server = server;
this.user = user;
this.pass = pass;
MqttClientPersistence memPer = new MemoryPersistence();
try {
String url = protocol + server + ":" + port;
client = new MqttClient(url, MqttClient.generateClientId(), memPer);
client.setCallback(mqttCallback);
} catch (MqttException e) {
e.printStackTrace();
}
options.setUserName(user + ":" + user);
options.setPassword(pass.toCharArray());
options.setMqttVersion(version);
options.setKeepAliveInterval(keepAliveSeconds);
}
void connect() {
Log.i(TAG, "buttonConnect");
try {
client.connect(options);
} catch (MqttException ex) {
Log.e(TAG, "Connection attempt failed with reason code = " + ex.getReasonCode() + ":" + ex.getCause());
}
}
public boolean isConnected() {
return client.isConnected();
}
public void disconnect() {
try {
client.disconnect();
} catch (MqttException e) {
Log.e(TAG, "Disconnect failed with reason code = " + e.getReasonCode());
}
}
void subscribe(String dest) {
try {
client.subscribe(dest);
} catch (MqttException e) {
Log.e(TAG, "Subscribe failed with reason code = " + e.getReasonCode());
}
}
}
}

I would guess this is because you are trying to update the TextView from a none UI thread.
Try wrapping the messageWindow.append(msg); in a runOnUiThread call.
public void messageArrived(String topic, MqttMessage message) throws Exception {
String msg = new String(message.getPayload());
Log.i(TAG, "Message Arrived: " + msg);
runOnUiThread(new Runnable(){
public void run() {
messageWindow.append(msg);
}
});
}

Related

connecting to xmpp server in android

I want to enable my app to send and recessive messages using xmpp server, i have installed and configured openfire but my app can't still send messages through xmpp server.
below is the code I am trying to connect to xmpp server with
ServerConnection
public class ServerConnection implements ConnectionListener {
private static final String TAG = "ServerConnection";
public static XMPPTCPConnection connection;
private Context mApplicationContext = null;
private String mUsername = null;
private String mPassword = null;
private String mServiceName = null;
private XMPPTCPConnection mConnection;
private BroadcastReceiver uiThreadMessageReceiver;//Receives messages from the ui thread.
private final Set<ChatMessageListener> listeners = new CopyOnWriteArraySet<>();
public ServerConnection() {
}
public static enum ConnectionState
{
CONNECTED ,AUTHENTICATED, CONNECTING ,DISCONNECTING ,DISCONNECTED;
}
public static enum LoggedInState
{
LOGGED_IN , LOGGED_OUT;
}
public ServerConnection ( Context context){
Log.d(TAG,"ServerConnection Constructor called.");
mApplicationContext = context.getApplicationContext();
String jid = PreferenceManager.getDefaultSharedPreferences(mApplicationContext)
.getString("xmpp_jid",null);
mPassword = PreferenceManager.getDefaultSharedPreferences(mApplicationContext)
.getString("xmpp_password",null);
if( jid != null)
{
mUsername = jid.split("#")[0];
mServiceName = jid.split("#")[1];
}else
{
mUsername ="";
mServiceName="192.168.8.101";
}
}
public void connect() throws IOException, XMPPException, SmackException, InterruptedException, XmppStringprepException
{
Log.d(TAG, "Connecting to server " + mServiceName);
DomainBareJid domainName = JidCreate.domainBareFrom("localhost");
XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
.setServiceName("192.168.8.101")
.setHost("192.168.8.101")
.setResource("Roster")
//Was facing this issue
//https://discourse.igniterealtime.org/t/connection-with-ssl-fails-with-java-security-keystoreexception-jks-not-found/62566
.setKeystoreType(null) //This line seems to get rid of the problem
.setSecurityMode(ConnectionConfiguration.SecurityMode.required)
.setCompressionEnabled(true).build();
// Log.d(TAG, "Username : "+mUsername);
// Log.d(TAG, "Password : "+mPassword);
// Log.d(TAG, "Server : "+mServiceName);
//Set up the ui thread broadcast message receiver.
setupUiThreadBroadCastMessageReceiver();
// mConnection = new XMPPTCPConnection(config);
mConnection.addConnectionListener(this);
Log.d(TAG, "Calling connect() ");
mConnection.connect();
mConnection.login(mUsername,mPassword);
Log.d(TAG, " login() Called ");
org.jivesoftware.smack.chat2.ChatManager.getInstanceFor(mConnection).addIncomingListener(new IncomingChatMessageListener() {
#Override
public void newIncomingMessage(EntityBareJid entityBareJid, Message message, org.jivesoftware.smack.chat2.Chat chat) {
}
please i really need to know if my code is correct.

Smack 4.2 and CCS (XMPP to FCM/GCM) - response incorrectly parsed by Smack - drops JSON payload

Version 4.2 of the Smack library seems to drop the message body, as sent by FCM.
When I set the debugger on, I can see the following incoming message:
<message><data:gcm xmlns:data="google:mobile:data">{"message_type":"nack","from":"xxx","message_id":"aaaa1","error":"BAD_REGISTRATION","error_description":""}</data:gcm></message>
However, when Smack 4.2 parses this message, it drops the JSON body inside the message and gives me the following in my packet listener:
<message><gcm xmlns="google:mobile:data"></gcm></message>
Here's my test class:
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
import javax.net.ssl.SSLSocketFactory;
public class CcsClient {
private static final String HOST = "fcm-xmpp.googleapis.com";
private static final int PORT = 5235;
private final XMPPConnection conn;
public CcsClient(String senderId, String serverKey) {
SASLAuthentication.supportSASLMechanism("PLAIN", 0);
ConnectionConfiguration conf = new ConnectionConfiguration(HOST, PORT);
conf.setSASLAuthenticationEnabled(true);
conf.setSocketFactory(SSLSocketFactory.getDefault());
conf.setServiceName(HOST);
conf.setSendPresence(false);
conf.setCompressionEnabled(false);
conf.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
conf.setDebuggerEnabled(true);
this.conn = new XMPPConnection(conf);
conn.addConnectionListener(new AbstractConnectionListener() {
#Override
public void connectionClosed() {
super.connectionClosed();
}
#Override
public void connectionClosedOnError(Exception e) {
super.connectionClosedOnError(e);
}
#Override
public void reconnectingIn(int seconds) {
super.reconnectingIn(seconds);
}
#Override
public void reconnectionFailed(Exception e) {
super.reconnectionFailed(e);
}
#Override
public void reconnectionSuccessful() {
super.reconnectionSuccessful();
}
});
try {
conn.connect();
conn.login(senderId + "#gcm.googleapis.com", serverKey);
System.out.println("connected!");
} catch (XMPPException e) {
e.printStackTrace();
}
}
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super("gcm", "google:mobile:data");
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return "<gcm xmlns=\"google:mobile:data\">" + StringUtils.escapeForXML(json) + "</gcm>";
}
public Message toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
public static void main(String[] args) throws InterruptedException {
final CcsClient c = new CcsClient("xxx", "xxx");
c.conn.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
System.out.println("incoming!" + packet.toString());
}
}, new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
});
for (int i = 0; i < 1; i++) {
c.conn.sendPacket(new GcmPacketExtension("{\"to\":\"xxx\", \"message_id\":\"aaaa"+i+"\", \"delivery_receipt_requested\":true}").toPacket());
}
Thread.sleep(1000000);
}
}
What am I doing wrong?
So I finally got it working. An example client is here: https://gist.github.com/judepereira/fd8dc0a5321179b699f5c5e54812770c

android - Autobahn Websocket Connection

I am working on Autobahn Web socket communication. There is a carousel view in my application, and there are four images. When users click on of the images, then connects to server with websocket and send message. But the problem is that when I select the images, it connects to server correctly, but client(android device) connects to the websocket every single time when the message is sent.
Here is my code..
if (pos == 0) {
product_photo.setImageResource(R.drawable.myoffers_0);
product_photo.setOnClickListener(new ImageButton.OnClickListener(){
public void onClick(View v){
String id = "Product0";
Log.d(TAG, "Current product is : " + id);
A.sendMessage(id);
}
});
}
Websocket.class
public class WebSocket_Connector extends Activity{
private static final String TAG = "ECHOCLIENT";
private static final String TAG1 = "My app";
public final WebSocketConnection mConnection = new WebSocketConnection();
private String tmpString = "";
public void connect(final String wsuri) {
Log.d(TAG, "Connecting to: " + wsuri);
try {
mConnection.connect(wsuri, new WebSocketHandler() {
#Override
public void onOpen() {
Log.d(TAG, "Status: Connected to " + wsuri );
Log.d(TAG, "Connection successful!\n");
mConnection.sendTextMessage(tmpString);
tmpString = "";
}
#Override
public void onTextMessage(String payload) {
Log.d(TAG, "Got echo: " + payload);
}
#Override
public void onClose(int code, String reason) {
Log.d(TAG, "Connection closed.");
}
});
} catch (WebSocketException e) {
Log.d(TAG, e.toString());
}
}
public void sendMessage(String message) {
if (mConnection.isConnected()) {
Log.d(TAG1, "Messeage is sent : " + message);
mConnection.sendTextMessage(message);
}
else {
tmpString = message;
connect("ws://xxx.xxx.x.xxx:xxxx");
}
}
}
It doens't go to 'if (mConnection.isConnected())' here..always goes to else.

login issue using twitter api in android

what is the procedure to login throw twitter api1.1.
i have used old api that will show me twitter connection failed because of api 1 is deprecated.
private final TwDialogListener mTwLoginDialogListener = new TwDialogListener()
{
public void onComplete(String value)
{
getTwitterDetail();
}
public void onError(String value) {
Toast.makeText(LoginActivity.this, "Twitter connection failed", Toast.LENGTH_LONG).show();
}
};
LOG
{"errors": [{"message": "The Twitter REST API v1 is no longer active.
Please migrate to API v1.1. https://dev.twitter.com/docs/api/1.1/overview.", "code": 68}]}
As you see from error log API v.1 is no longer active and everybody must migrate to v1.1. In API v1.1. you must log in via OAUTH to get connected. So you also have to register your app on dev.twitter.com.
You can find below example here
public class Main extends Activity{
public static final String TAG = Main.class.getSimpleName();
public static final String TWITTER_OAUTH_REQUEST_TOKEN_ENDPOINT = "..."; //cannot share more then 2 lins, sorry
public static final String TWITTER_OAUTH_ACCESS_TOKEN_ENDPOINT = "...";
public static final String TWITTER_OAUTH_AUTHORIZE_ENDPOINT = "...";
private CommonsHttpOAuthProvider commonsHttpOAuthProvider;
private CommonsHttpOAuthConsumer commonsHttpOAuthConsumer;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
commonsHttpOAuthProvider = new CommonsHttpOAuthProvider(TWITTER_OAUTH_REQUEST_TOKEN_ENDPOINT,
TWITTER_OAUTH_ACCESS_TOKEN_ENDPOINT, TWITTER_OAUTH_AUTHORIZE_ENDPOINT);
commonsHttpOAuthConsumer = new CommonsHttpOAuthConsumer(getString(R.string.twitter_oauth_consumer_key),
getString(R.string.twitter_oauth_consumer_secret));
commonsHttpOAuthProvider.setOAuth10a(true);
TwDialog dialog = new TwDialog(this, commonsHttpOAuthProvider, commonsHttpOAuthConsumer,
dialogListener, R.drawable.android);
dialog.show();
}
private Twitter.DialogListener dialogListener = new Twitter.DialogListener() {
public void onComplete(Bundle values) {
String secretToken = values.getString("secret_token");
Log.i(TAG,"secret_token=" + secretToken);
String accessToken = values.getString("access_token");
Log.i(TAG,"access_token=" + accessToken);
new Tweeter(accessToken,secretToken).tweet(
"Tweet from sample Android OAuth app. unique code: " + System.currentTimeMillis());
}
public void onTwitterError(TwitterError e) { Log.e(TAG,"onTwitterError called for TwitterDialog",
new Exception(e)); }
public void onError(DialogError e) { Log.e(TAG,"onError called for TwitterDialog", new Exception(e)); }
public void onCancel() { Log.e(TAG,"onCancel"); }
};
public static final Pattern ID_PATTERN = Pattern.compile(".*?\"id_str\":\"(\\d*)\".*");
public static final Pattern SCREEN_NAME_PATTERN = Pattern.compile(".*?\"screen_name\":\"([^\"]*).*");
public class Tweeter {
protected CommonsHttpOAuthConsumer oAuthConsumer;
public Tweeter(String accessToken, String secretToken) {
oAuthConsumer = new CommonsHttpOAuthConsumer(getString(R.string.twitter_oauth_consumer_key),
getString(R.string.twitter_oauth_consumer_secret));
oAuthConsumer.setTokenWithSecret(accessToken, secretToken);
}
public boolean tweet(String message) {
if (message == null && message.length() > 140) {
throw new IllegalArgumentException("message cannot be null and must be less than 140 chars");
}
// create a request that requires authentication
try {
HttpClient httpClient = new DefaultHttpClient();
Uri.Builder builder = new Uri.Builder();
builder.appendPath("statuses").appendPath("update.json")
.appendQueryParameter("status", message);
Uri man = builder.build();
HttpPost post = new HttpPost("http://twitter.com" + man.toString());
oAuthConsumer.sign(post);
HttpResponse resp = httpClient.execute(post);
String jsonResponseStr = convertStreamToString(resp.getEntity().getContent());
Log.i(TAG,"response: " + jsonResponseStr);
String id = getFirstMatch(ID_PATTERN,jsonResponseStr);
Log.i(TAG,"id: " + id);
String screenName = getFirstMatch(SCREEN_NAME_PATTERN,jsonResponseStr);
Log.i(TAG,"screen name: " + screenName);
final String url = MessageFormat.format("https://twitter.com/#!/{0}/status/{1}",screenName,id);
Log.i(TAG,"url: " + url);
Runnable runnable = new Runnable() {
public void run() {
((TextView)Main.this.findViewById(R.id.textView)).setText("Tweeted: " + url);
}
};
Main.this.runOnUiThread(runnable);
return resp.getStatusLine().getStatusCode() == 200;
} catch (Exception e) {
Log.e(TAG,"trying to tweet: " + message, e);
return false;
}
}
}
public static String convertStreamToString(java.io.InputStream is) {
try {
return new java.util.Scanner(is).useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
public static String getFirstMatch(Pattern pattern, String str){
Matcher matcher = pattern.matcher(str);
if(matcher.matches()){
return matcher.group(1);
}
return null;
}

Upgrage Twitter login to use Twitter API 1.1

I use this sample to login Twitter, post status and photo. I used it for a long time. Now Twitter requires upgrading from Twitter API 1.0 to Twitter API 1.1. What do I have to do to upgrade it? I tried to replace the old lib with this lib and there is no problem so far but I'm scared of I didn't do the change completely.
You must log in via OAUTH (https://dev.twitter.com/docs/auth/using-oauth) to get connected. So you also have to register your app on dev.twitter.com.
You can find below example here https://github.com/browep/Android-OAuth-Twitter-Example.
public class Main extends Activity{
public static final String TAG = Main.class.getSimpleName();
public static final String TWITTER_OAUTH_REQUEST_TOKEN_ENDPOINT = "..."; //cannot share more then 2 lins, sorry
public static final String TWITTER_OAUTH_ACCESS_TOKEN_ENDPOINT = "...";
public static final String TWITTER_OAUTH_AUTHORIZE_ENDPOINT = "...";
private CommonsHttpOAuthProvider commonsHttpOAuthProvider;
private CommonsHttpOAuthConsumer commonsHttpOAuthConsumer;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
commonsHttpOAuthProvider = new CommonsHttpOAuthProvider(TWITTER_OAUTH_REQUEST_TOKEN_ENDPOINT,
TWITTER_OAUTH_ACCESS_TOKEN_ENDPOINT, TWITTER_OAUTH_AUTHORIZE_ENDPOINT);
commonsHttpOAuthConsumer = new CommonsHttpOAuthConsumer(getString(R.string.twitter_oauth_consumer_key),
getString(R.string.twitter_oauth_consumer_secret));
commonsHttpOAuthProvider.setOAuth10a(true);
TwDialog dialog = new TwDialog(this, commonsHttpOAuthProvider, commonsHttpOAuthConsumer,
dialogListener, R.drawable.android);
dialog.show();
}
private Twitter.DialogListener dialogListener = new Twitter.DialogListener() {
public void onComplete(Bundle values) {
String secretToken = values.getString("secret_token");
Log.i(TAG,"secret_token=" + secretToken);
String accessToken = values.getString("access_token");
Log.i(TAG,"access_token=" + accessToken);
new Tweeter(accessToken,secretToken).tweet(
"Tweet from sample Android OAuth app. unique code: " + System.currentTimeMillis());
}
public void onTwitterError(TwitterError e) { Log.e(TAG,"onTwitterError called for TwitterDialog",
new Exception(e)); }
public void onError(DialogError e) { Log.e(TAG,"onError called for TwitterDialog", new Exception(e)); }
public void onCancel() { Log.e(TAG,"onCancel"); }
};
public static final Pattern ID_PATTERN = Pattern.compile(".*?\"id_str\":\"(\\d*)\".*");
public static final Pattern SCREEN_NAME_PATTERN = Pattern.compile(".*?\"screen_name\":\"([^\"]*).*");
public class Tweeter {
protected CommonsHttpOAuthConsumer oAuthConsumer;
public Tweeter(String accessToken, String secretToken) {
oAuthConsumer = new CommonsHttpOAuthConsumer(getString(R.string.twitter_oauth_consumer_key),
getString(R.string.twitter_oauth_consumer_secret));
oAuthConsumer.setTokenWithSecret(accessToken, secretToken);
}
public boolean tweet(String message) {
if (message == null && message.length() > 140) {
throw new IllegalArgumentException("message cannot be null and must be less than 140 chars");
}
// create a request that requires authentication
try {
HttpClient httpClient = new DefaultHttpClient();
Uri.Builder builder = new Uri.Builder();
builder.appendPath("statuses").appendPath("update.json")
.appendQueryParameter("status", message);
Uri man = builder.build();
HttpPost post = new HttpPost("http://twitter.com" + man.toString());
oAuthConsumer.sign(post);
HttpResponse resp = httpClient.execute(post);
String jsonResponseStr = convertStreamToString(resp.getEntity().getContent());
Log.i(TAG,"response: " + jsonResponseStr);
String id = getFirstMatch(ID_PATTERN,jsonResponseStr);
Log.i(TAG,"id: " + id);
String screenName = getFirstMatch(SCREEN_NAME_PATTERN,jsonResponseStr);
Log.i(TAG,"screen name: " + screenName);
final String url = MessageFormat.format("https://twitter.com/#!/{0}/status/{1}",screenName,id);
Log.i(TAG,"url: " + url);
Runnable runnable = new Runnable() {
public void run() {
((TextView)Main.this.findViewById(R.id.textView)).setText("Tweeted: " + url);
}
};
Main.this.runOnUiThread(runnable);
return resp.getStatusLine().getStatusCode() == 200;
} catch (Exception e) {
Log.e(TAG,"trying to tweet: " + message, e);
return false;
}
}
}
public static String convertStreamToString(java.io.InputStream is) {
try {
return new java.util.Scanner(is).useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
public static String getFirstMatch(Pattern pattern, String str){
Matcher matcher = pattern.matcher(str);
if(matcher.matches()){
return matcher.group(1);
}
return null;
}

Categories

Resources