global mqtt object: android - android

I am working on an android app (on android studio) where I have successfully implemented the paho mqtt library for a single activity. I have now run into the issue for the case where I need to persist my mqtt client across multiple activities.
Will i need to create a new client for each activity (subscribe to the needed topics again) and pass modified data of the old client through intents to update the new client ? [this seems like a really bad method and I am assuming that there's a more simple straightforward solution that I am missing]

On your paho mqtt class, you can send broadcast message to your activities. Here how I used;
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.d("MQTT MESSAGE", "Message arrived: Topic: " + topic + " Message: " + message);
broadcastSendAction("mqttMessage", "Topic: " + topic + " Message: " + message);
}
#Override
public void connectComplete(boolean b, String s) {
Log.d("MQTT MESSAGE", "Connection SUCCESS.");
mqttStatus = true;
broadcastSendAction("mqttConnection", true);
if (!mqttSubs)
subscribeTopic(mSharedPreferences.getString(getResources().getString(R.string.regId), SepMessageUtils.DEFAULT_REGID), client);
}

Related

pubnub setup not working on android OS9 android java

I have setup pubnub on android using java
And on all other Operating Systems the setup works fine. On Android P however the attach listener always gives this error -
PNStatus(category=PNBadRequestCategory, errorData=PNErrorData(information=null, throwable=PubNubException(errormsg=CLEARTEXT communication to ps.pndsn.com not permitted by network security policy, pubnubError=PubNubError(errorCode=103, errorCodeExtended=0, errorObject=null, message=HTTP Error. Please check network connectivity. Please contact support with error details if issue persists., errorString=null), jso=null, response=null, statusCode=0)), error=true, statusCode=0, operation=PNSubscribeOperation, tlsEnabled=false, uuid=null, authKey=null, origin=null, clientRequest=null, affectedChannels=[5d67fb36f4f95718bf8ec310], affectedChannelGroups=[])
The setup is as follows -
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.setLogVerbosity(PNLogVerbosity.BODY);
pnConfiguration.setPublishKey(getString(R.string.pubnub_publish_key));
pnConfiguration.setSubscribeKey(getString(R.string.pubnub_subscribe_key));
pnConfiguration.setSecure(false);
pubnub = new PubNub(pnConfiguration);
Then i attach listener like this
pubnub.addListener(new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
Log.e("Pubnub:- ", status.toString());
switch (status.getOperation()) {
// let's combine unsubscribe and subscribe handling for ease of use
case PNSubscribeOperation:
case PNUnsubscribeOperation:
// note: subscribe statuses never have traditional
// errors, they just have categories to represent the
// different issues or successes that occur as part of subscribe
switch (status.getCategory()) {
case PNConnectedCategory:
// this is expected for a subscribe, this means there is no error or issue whatsoever
case PNReconnectedCategory:
// this usually occurs if subscribe temporarily fails but reconnects. This means
// there was an error but there is no longer any issue
case PNDisconnectedCategory:
// this is the expected category for an unsubscribe. This means there
// was no error in unsubscribing from everything
case PNUnexpectedDisconnectCategory:
// this is usually an issue with the internet connection, this is an error, handle appropriately
case PNAccessDeniedCategory:
// this means that PAM does allow this client to subscribe to this
// channel and channel group configuration. This is another explicit error
default:
// More errors can be directly specified by creating explicit cases for other
// error categories of `PNStatusCategory` such as `PNTimeoutCategory` or `PNMalformedFilterExpressionCategory` or `PNDecryptionErrorCategory`
}
case PNHeartbeatOperation:
// heartbeat operations can in fact have errors, so it is important to check first for an error.
// For more information on how to configure heartbeat notifications through the status
// PNObjectEventListener callback, consult <link to the PNCONFIGURATION heartbeart config>
if (status.isError()) {
// There was an error with the heartbeat operation, handle here
} else {
// heartbeat operation was successful
}
default: {
// Encountered unknown status type
}
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
String messagePublisher = message.getPublisher();
System.out.println("Message publisher: " + messagePublisher);
System.out.println("Message Payload: " + message.getMessage());
System.out.println("Message Subscription: " + message.getSubscription());
System.out.println("Message Channel: " + message.getChannel());
System.out.println("Message timetoken: " + message.getTimetoken());
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
}
#Override
public void signal(PubNub pubnub, PNSignalResult pnSignalResult) {
System.out.println("Signal publisher: " + signal.getPublisher());
System.out.println("Signal payload: " + signal.getMessage());
System.out.println("Signal subscription: " + signal.getSubscription());
System.out.println("Signal channel: " + signal.getChannel());
System.out.println("Signal timetoken: " + signal.getTimetoken());
}
#Override
public void user(PubNub pubnub, PNUserResult pnUserResult) {
// for Objects, this will trigger when:
// . user updated
// . user deleted
PNUser pnUser = pnUserResult.getUser(); // the user for which the event applies to
pnUserResult.getEvent(); // the event name
}
#Override
public void space(PubNub pubnub, PNSpaceResult pnSpaceResult) {
// for Objects, this will trigger when:
// . space updated
// . space deleted
PNSpace pnSpace = pnSpaceResult.getSpace(); // the space for which the event applies to
pnSpaceResult.getEvent(); // the event name
}
#Override
public void membership(PubNub pubnub, PNMembershipResult pnMembershipResult) {
// for Objects, this will trigger when:
// . user added to a space
// . user removed from a space
// . membership updated on a space
JsonElement data = pnMembershipResult.getData(); // membership data for which the event applies to
pnMembershipResult.getEvent(); // the event name
}
#Override
public void messageAction(PubNub pubnub, PNMessageActionResult pnActionResult) {
PNMessageAction pnMessageAction = pnActionResult.getAction();
System.out.println("Message action type: " + pnMessageAction.getType());
System.out.println("Message action value: " + pnMessageAction.getValue());
System.out.println("Message action uuid: " + pnMessageAction.getUuid());
System.out.println("Message action actionTimetoken: " + pnMessageAction.getActionTimetoken());
System.out.println("Message action messageTimetoken: " + pnMessageAction.getMessageTimetoken());]
System.out.println("Message action subscription: " + pnActionResult.getSubscription());
System.out.println("Message action channel: " + pnActionResult.getChannel());
System.out.println("Message action timetoken: " + pnActionResult.getTimetoken());
}
});
Like i said it works fine in other OS's except 9 and i am using the latest version of pubnub, infact i upgraded to the latest version and the above error was identical.
And i noticed something else, a message which i see in the debugger that only shows up when running in android version 9.
isWhitelistProcess - Process is Whitelisted
I searched for the message and found that its a harmless warning message.
Enabling TLS (SSL) resolves this error:
pnConfiguration.setSecure(true);
The reason is answered in another Stack Overflow thread already. It is not specific to PubNub but because you disabled TLS (SSL) in PubNub, it explicitly called out the PubNub domain in the error.

MessageListener.onDistanceChanged not being called

I am publishing and subscribing to messages with Google Nearby and am receiving onFound and onLost properly, but am not receiving any callbacks on onDistanceChanged or onBleSignalChanged. I know that BLE signal changed is based on using BLE for discovery, but is there a similar limitation for onDistanceChanged? The docs don't seem to indicate there is. Here is my message listener below, thanks for any suggestions!
mMessageListener = new MessageListener() {
#Override
public void onFound(Message message) {
String messageAsString = new String(message.getContent());
Log.d(TAG, "Found message: " + messageAsString);
}
#Override
public void onLost(Message message) {
String messageAsString = new String(message.getContent());
Log.d(TAG, "Lost sight of message: " + messageAsString);
}
#Override
public void onDistanceChanged(Message message, Distance distance) {
super.onDistanceChanged(message, distance);
Log.d(TAG, "New distance "+distance.getMeters()+" to message: " + new String(message.getContent()));
}
#Override
public void onBleSignalChanged(Message message, BleSignal bleSignal) {
super.onBleSignalChanged(message, bleSignal);
Log.d(TAG, "New ble signal " + bleSignal.getRssi() + " to message: " + new String(message.getContent()));
}
};
There are two reasons you may not get that callback:
Unfortunately, the distance callback currently only works for BLE beacon messages. We've fixed the docs to say that, but they won't update on the web until the next Google Play Services SDK release (in a couple weeks). We hope to make it work for more messages in the future.
The BLE signal and distance callbacks aren't called for the PendingIntent version of subscribe().

How to push stream of events in Android app from Google App Engine

I am quite new to Android developpement and Google App engine stuff. I am still into processing the documentation and the tutorials about it.
I have a game concept for Android, based on a turn-by-turn battle for 2 players, but with very short turns (< 20 secs). For what I have understand this far, Google Cloud Endpoints allows to create a REST-like API.
Is there a way for the app to warn a client that the other player has played, and react accordingly ? Because of the "small" timer, one is not expected to leave the app for the during of the battle.
So far, I have found the Channel API, but it is not avaiable for Android clients.
Thank you in advance !
Google Cloud Messaging for Android is meant just for pushing from server to Android client.
You will need the Messages with payload and handle it in your Android app.
For high volumes of push messages, you should also consider keeping a dedicated live connection using Socket API
Personally, I suggest using the PubNub lib, http://www.pubnub.com/docs/java/android/android-sdk.html, which IHO is more robust than GCM.
When a game is created with x number of players, each player would programmically be subscribe to a pubnub channel.
When player X does something in the game, that action would be instantly published in the channel, and all the players subscribed to that channel would instantly get notification
( in the code) of that action, so then you could take that indication and visible notify the awaiting player its her turn.
Here's a snippet of using the Pubnub id lib on the client side, to receiving incoming msgs on a particular chan:
public void Startpubnub(String channel) {
Toast.makeText(this, channelName+"TransportLine activated...", Toast.LENGTH_LONG).show();
Log.i("PUBNUB", channelName+"TransportLine activated...");
try {
pubnub.subscribe(new String[] {channel}, new Callback() {
public void connectCallback(String channel) {
notifyUser("CONNECT on channel:" + channel);
}
public void disconnectCallback(String channel) {
notifyUser("DISCONNECT on channel:" + channel);
}
public void reconnectCallback(String channel) {
notifyUser("RECONNECT on channel:" + channel);
}
#Override
public void successCallback(String channel, Object message) {
Log.i("tag","broadcast is sent!");
notifyUser(channel + " " + message.toString());
Log.i("afterBroadcastisSent", message.toString());
try {
Message m = Message.obtain();
Bundle b = new Bundle();
b.putString("message", message.toString());
m.setData(b);
mMessageHandler.sendMessage(m);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void errorCallback(String channel, Object message) {
notifyUser(channel + " " + message.toString());
}
});
} catch (PubnubException e) {
}
Then subscribe to that chan on your backend aswell, http://www.pubnub.com/docs/java/javase/javase-sdk.html,
https://github.com/pubnub/python,
then pub a msg back to the chan based on the incoming msg.
You can even communicate with GCM over PubNub.
And if you plan on supporting devices especially international based devices, that doesn't have google play services install because of licensing issues, PubNub is the ultimate quick and fast real time push/pull mechanism. PubNub is essentially that Socket communications that #Ashish described above but outside of Google infrastructure.

Upstream messaging in Android

I am doing work on GCM (Google Cloud Messaging) in Android. I am looking for the upstream message using GCM.
Code send the GCM messages to cloud here
try {
Bundle data = new Bundle();
// the account is used for keeping
// track of user notifications
data.putString("account", account);
// the action is used to distinguish
// different message types on the server
data.putString("action", Constants.ACTION_REGISTER);
String msgId = Integer.toString(getNextMsgId());
gcm.send(projectId + "#gcm.googleapis.com", msgId,
Constants.GCM_DEFAULT_TTL, data);
} catch (IOException e) {
Log.e("grokkingandroid",
"IOException while sending registration id", e);
}
Now question is that what cloud would do for that upstream message, Where it can be useful in Android and How ??
When you send an upstream message from your app, the GCM Cloud Connection Server (CCS) transfers that message to your server. In order for that to work, you must implement a server that supports XMPP protocol and establishes a TLS connection with GCM Cloud Connection Server. You also need your API project to be white-listed for using this feature. You can read more about it here.
As for usefulness, it allows you to send messages to your app via the GCM connection instead of via your own connection between your app and your server. That's more battery efficient.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "FCM Token creation logic");
// Get variables reference
deviceText = (TextView) findViewById(R.id.deviceText);
editTextEcho = (EditText) findViewById(R.id.editTextEcho);
buttonUpstreamEcho = (Button) findViewById(R.id.buttonUpstreamEcho);
//Get token from Firebase
FirebaseMessaging.getInstance().subscribeToTopic("test");
final String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Token: " + token);
deviceText.setText(token);
//Call the token service to save the token in the database
tokenService = new TokenService(this, this);
tokenService.registerTokenInDB(token);
buttonUpstreamEcho.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Echo Upstream message logic");
String message = editTextEcho.getText().toString();
Log.d(TAG, "Message: " + message + ", recipient: " + token);
FirebaseMessaging.getInstance().send(new RemoteMessage.Builder(FCM_PROJECT_SENDER_ID + FCM_SERVER_CONNECTION)
.setMessageId(Integer.toString(RANDOM.nextInt()))
.addData("message", message)
.addData("action", BACKEND_ACTION_ECHO)
.build());
// To send a message to other device through the XMPP Server, you should add the
// receiverId and change the action name to BACKEND_ACTION_MESSAGE in the data
}
});
}
This is a sample Android project to showcase the Firebase Cloud Messaging (FCM) to manage upstream and downstream messages.
https://github.com/carlosCharz/FCMTest
This is the video in youtube that explains what it does.
https://www.youtube.com/watch?v=SEzOKSoAMG0
Hope you find it useful.

Differentiate between GCM and C2DM ids

I'm migrating my app from C2DM to GCM but the server on which i'll be sending the registration id will still be entertaining C2DM ids. So my question is what can I do from my app so that server can identify that this is GCM id. Google recommends to send a new bit along with registration id. How can I do that. Please give an exmaple.
Here's what you need to do if you're not using AppEngine.
In your server, you should already have a table that stores all the registration id (reg_id). You need to add another column to this table, say 'is_gcm_reg_id'. You would default all existing rows to '0' for this column since they are currently all C2DM reg_ids. You could also just create a new table to store the GCM reg_ids, if you like.
Then in your GCM enabled application, you just need to let your server know the reg_id is a GCM reg_id. You didn't mention how you upload the reg_id to your server (Web Service call? Simple POST request?). If using Web Service call, just create a new method that will be used exclusively by your new GCM-enabled app. If using POST request, just add another key value pair like 'gcm=true' and your server should look for this kvp.
Finally, you should have all the pieces needed to know the reg_id is of GCM, and update the 'is_gcm_reg_id' column field (or a new GCM table) accordingly.
Assuming you're using App Engine to implement GCM/C2DM.
In the deviceInfo entity for your App Engine there is already a "type" field. This is the field you will modify to either say "ac2dm" or "gcm".
An easy way to do this is to create a new registration request that simply passes "gcm" instead of "ac2dm". Take a look in your AppEngine project and locate the RegistrationInfo class. Look at the register() method. Simply replace "ac2dm" with "gcm".
EX:
// original
public void register() {
log.info("register " + this);
try {
doRegister(getDeviceRegistrationId(), "ac2dm", getDeviceId(), getAccountName());
} catch (Exception e) {
log.info("Got exception in registration: " + e + " - " + e.getMessage());
for (StackTraceElement ste : e.getStackTrace()) {
log.info(ste.toString());
}
}
log.info("Successfully registered");
}
// new version
public void register() {
log.info("register " + this);
try {
doRegister(getDeviceRegistrationId(), "gcm", getDeviceId(), getAccountName());
} catch (Exception e) {
log.info("Got exception in registration: " + e + " - " + e.getMessage());
for (StackTraceElement ste : e.getStackTrace()) {
log.info(ste.toString());
}
}
log.info("Successfully registered");
}
You will probably have to recompile the RPC service to get this to work.
Now, it's up to you to make sure your server checks for the "gcm" tag of the deviceInfo entity and acts accordingly. Also, read this if you haven't seen it https://developer.android.com/guide/google/gcm/c2dm.html

Categories

Resources