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.
Related
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);
}
I want to implement the Online/Offline/IsTying functionality using the pubnub library in android. For this i created the Pubnub connection.
//create the pubnub connection
PNConfiguration configuration = new PNConfiguration();
configuration.setPublishKey(PUBNUB_PUBLISH_KEY);
configuration.setSubscribeKey(PUBNUB_SUBSCRIBE_KEY);
configuration.setLogVerbosity(PNLogVerbosity.BODY);
configuration.setConnectTimeout(100000);
configuration.setSubscribeTimeout(31000);
configuration.setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.ALL);
configuration.setPresenceTimeoutWithCustomInterval(120,59);
configuration.setPresenceTimeout(120);
mPubNub = new PubNub(configuration);
And after that i set the subscriber callback to the listener
**strong text**
private SubscribeCallback subscribeCallback = new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
Log.d("Chat", "status() called with: pubnub = [" + pubnub + "], status = [" + status + "]");
if (status.getOperation() != null) {
switch (status.getOperation()) {
// let's combine unsubscribe and subscribe handling for ease of use
case PNSubscribeOperation:
case PNUnsubscribeOperation:
Toast.makeText(ChatAppService.this, "Status : " + status.getOperation().name(), Toast.LENGTH_SHORT).show();
// 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
Toast.makeText(ChatAppService.this, "Status : " + status.getCategory(), Toast.LENGTH_SHORT).show();
break;
case PNReconnectedCategory:
// this usually occurs if subscribe temporarily fails but reconnects. This means
// there was an error but there is no longer any issue
Toast.makeText(ChatAppService.this, "Status : " + status.getCategory(), Toast.LENGTH_SHORT).show();
HashMap<String,String> map = new HashMap();
map.put("State","Online");
pubnub.setPresenceState().channels(Arrays.asList(Constants.GLOBAL_CHANNEL)).state(map).uuid(pubnub.getConfiguration().getUuid());
break;
case PNDisconnectedCategory:
// this is the expected category for an unsubscribe. This means there
// was no error in unsubscribing from everything
Toast.makeText(ChatAppService.this, "Status : " + status.getCategory(), Toast.LENGTH_SHORT).show();
HashMap<String,String> mapOffline = new HashMap();
mapOffline.put("State","Offline");
pubnub.setPresenceState().channels(Arrays.asList(Constants.GLOBAL_CHANNEL)).state(mapOffline).uuid(pubnub.getConfiguration().getUuid());
break;
case PNTimeoutCategory:
HashMap<String,String> mapTimeout = new HashMap();
mapTimeout.put("State","Offline");
pubnub.setPresenceState().channels(Arrays.asList(Constants.GLOBAL_CHANNEL)).state(mapTimeout).uuid(pubnub.getConfiguration().getUuid());
pubnub.reconnect();
break;
case PNUnexpectedDisconnectCategory:
// this is usually an issue with the internet connection, this is an error, handle appropriately
Toast.makeText(ChatAppService.this, "Status : " + status.getCategory(), Toast.LENGTH_SHORT).show();
pubnub.reconnect();
break;
case PNAccessDeniedCategory:
// this means that PAM does allow this client to subscribe to this
// channel and channel group configuration. This is another explicit error
Toast.makeText(ChatAppService.this, "Status : " + status.getCategory(), Toast.LENGTH_SHORT).show();
break;
default:
// More errors can be directly specified by creating explicit cases for other
// error categories of `PNStatusCategory` such as `PNTimeoutCategory` or `PNMalformedFilterExpressionCategory` or `PNDecryptionErrorCategory`
}
break;
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
}
break;
default: {
// Encountered unknown status type
}
}
} else {
// After a reconnection see status.getCategory()
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
//TODO: If App open: Save to database and broadcast for database change else show Notification
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
Log.d("chat", "presence() called with: pubnub = [" + pubnub + "], presence = [" + presence + "]");
}
};`
Finally the issue is that when i leave the group then no presence callback is coming.I should get the event of leave the channel with user state so that i can remove the user from the online user's list.
for unsubscribing the group
pubnub.unsubscribe().channels(Arrays.asList(channel)).execute()
when i subscribe the channel then presence callback calls three time
1. event : join
2. event : state-changes
3. event : leave
I am not getting how to handle this scenario in Android. Please help me i am stucking in this problem from last three days.
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().
I am working on websocket connection to communicate with server.
Now, we have established a connection and able to send and receive message using Autobahn library.
Here is the code:
private void start() {
// wsuri is the url which has to be hit for connecting to the server i.e
// "ws://hostname:port "
final String wsuri = "ws://" + mHostname.getText() + ":"
+ mPort.getText();
// showing status on the UI.
mStatusline.setText("Status: Connecting to " + wsuri + " ..");
// enabling disconnect button clicking on which the connection will be
// disconnected.
setButtonDisconnect();
try {
mConnection.connect(wsuri, new WebSocketConnectionHandler() {
#Override
public void onOpen() {
// If connection is open, then status will be updated to
// "connected to..."
mStatusline.setText("Status: Connected to " + wsuri);
savePrefs();
// enabling mSendMessage button and mMessage edit Text.
mSendMessage.setEnabled(true);
mMessage.setEnabled(true);
}
#Override
public void onTextMessage(String payload) {
Log.d("ss", payload);
alert(payload);
}
#Override
public void onClose(int code, String reason) {
// If connection is lost, alert will be shown connection is
// lost and status will be set to "Ready".
alert("Connection lost.");
mStatusline.setText("Status: Ready.");
setButtonConnect();
// disabling mSendMessage button and mMessage edit Text.
mSendMessage.setEnabled(false);
mMessage.setEnabled(false);
}
});
} catch (WebSocketException e) {
Log.d(TAG, e.toString());
}
}
We just have a single method onTextMessage(String payload) to handle response from server.
and to send message to server we use mConnection.sendTextMessage(mMessage.getText().toString());
But now we are facing issue that how to hit api on that server?or lets say I want some list of devices or device_Status etc how it will happen??
Any idea how to do that?? At present I am thinking of sending some "key":"pair" value in message in form of json, by which the server will identify what request I have made...
I am still not sure whether this is the right way? Please if somebody can help me in this.
I am creating a generic Chromecast remote control app. Most of the guts of the app are already created and I've managed to get Chromecast volume control working (by connecting to a Chromecast device along side another app that is casting - YouTube for example).
What I've having difficult with is performing other media commands such as play, pause, seek, etc.
Use case example:
1. User opens YouTube on their android device and starts casting a video.
2. User opens my app and connects to the same Chromecast device.
3. Volume control from my app (works now)
4. Media control (play, pause, etc) (does not yet work)
I found the Cast api reference that explains that you can sendMessage(ApiClient, namespace, message) with media commands; however the "message" (JSON) requires the sessionId of the current application (Youtube in this case). I have tried the following, but the connection to the current application always fails; status.isSuccess() is always false:
Cast.CastApi
.joinApplication(mApiClient)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(
Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
sessionId = result.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.i(TAG,
"Joined Application with sessionId: "
+ sessionId
+ " Application Status: "
+ applicationStatus);
} else {
// teardown();
Log.e(TAG,
"Could not join application: "
+ status.toString());
}
}
});
Is is possible to get the sessionId of an already running cast application from a generic remote control app (like the one I am creating)? If so, am I right in my assumption that I can then perform media commands on the connected Chromecast device using something like this:
JSONObject message = new JSONObject();
message.put("mediaSessionId", sessionId);
message.put("requestId", 9999);
message.put("type", "PAUSE");
Cast.CastApi.sendMessage(mApiClient,
"urn:x-cast:com.google.cast.media", message.toString());
Update:
I have tried the recommendations provided by #Ali Naddaf but unfortunately they are not working. After creating mRemoteMediaPlayer in onCreate, I also do requestStatus(mApiClient) in the onConnected callback (in the ConnectionCallbacks). When I try to .play(mApiClient) I get an IllegalStateException stating that there is no current media session. Also, I tried doing joinApplication and in the callback performed result.getSessionId; which returns null.
A few comments and answers:
You can get the sessionId from the callback of launchApplication or joinApplication; in the "onResult(result)", you can get the sessionId from: result.getSessionId()
YouTube is still not on the official SDK so YMMV, for apps using official SDK, you should be able to use the above approach (most of it)
Why are you trying to set up a message yourself? Why not building a RemoteMediaPlayer and using play/pause that is provided there? Whenever you are working with the media playback through the official channel, always use the RemoteMediaPlayer (don't forget to call requestStatus() on it after creating it).
Yes it is possible , First you have to save sesionId and CastDevice device id
and when remove app from background and again open app please check is there sessionId then call bello line.
Cast.CastApi.joinApplication(apiClient, APP_ID,sid).setResultCallback(connectionResultCallback);
if you get success result then need to implement further process in connectionResultCallback listener.
//Get selected device which you selected before
#Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
// Log.d("Route Added", "onRouteAdded");
/* if (router.getRoutes().size() > 1)
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
else
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes(), Toast.LENGTH_SHORT).show();*/
if (router != null && router.getRoutes() != null && router.getRoutes().size() > 1) {
// Show the button when a device is discovered.
// Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
mMediaRouteButton.setVisibility(View.VISIBLE);
titleLayout.setVisibility(View.GONE);
castName.setVisibility(View.VISIBLE);
selectedDevice = CastDevice.getFromBundle(route.getExtras());
routeInfoArrayList = router.getRoutes();
titleLayout.setVisibility(View.GONE);
if (!isCastConnected) {
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
for (int i = 0; i < routeInfoArrayList.size(); i++) {
if (routeInfoArrayList.get(i).getExtras() != null && CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras()).getDeviceId().equalsIgnoreCase(deid)) {
selectedDevice = CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras());
routeInfoArrayList.get(i).select();
ReSelectedDevice(selectedDevice, routeInfoArrayList.get(i).getName());
break;
}
}
}
}
}
//Reconnect google Api Client
public void reConnectGoogleApiClient() {
if (apiClient == null) {
Cast.CastOptions apiOptions = new
Cast.CastOptions.Builder(selectedDevice, castClientListener).build();
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptions)
.addConnectionCallbacks(reconnectionCallback)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
apiClient.connect();
}
}
// join Application
private final GoogleApiClient.ConnectionCallbacks reconnectionCallback = new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
// Toast.makeText(homeScreenActivity, "" + isDeviceSelected(), Toast.LENGTH_SHORT).show();
try {
String sid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_SESSION_ID);
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
if (sid != null && deid != null && sid.length() > 0 && deid.length() > 0)
Cast.CastApi.joinApplication(apiClient, APP_ID, sid).setResultCallback(connectionResultCallback);
isApiConnected = true;
} catch (Exception e) {
}
}
#Override
public void onConnectionSuspended(int i) {
isCastConnected = false;
isApiConnected = false;
}
};