I'm trying to implement a pusher service in my Android app, doesn't have access to the server just copying from an iOS app previous implementation. Everything works fine in connection process but when subscribe to a private channel the authentication fails with:
"com.pusher.client.AuthorizationFailureException: java.io.FileNotFoundException: https://authorization_url"
The implementation goes like this:
HttpAuthorizer authorizer = new HttpAuthorizer(PUSHER_AUTH_URL);
PusherOptions options = new PusherOptions().setEncrypted(true).setWssPort(443).setAuthorizer(authorizer);
pusher = new Pusher(PUSHER_KEY, options);
pusher.connect(new com.pusher.client.connection.ConnectionEventListener() {
#Override
public void onConnectionStateChange(ConnectionStateChange change) {
if (change.getCurrentState() == ConnectionState.CONNECTED) {
Channel channel = pusher.subscribePrivate(PUSH_CHANNEL, new PrivateChannelEventListener() {
#Override
public void onAuthenticationFailure(String s, Exception e) {
Log.w("PUSHER", "Channel subscription authorization failed");
}
#Override
public void onSubscriptionSucceeded(String s) {
Log.w("PUSHER", "Channel subscription authorization succeeded");
}
#Override
public void onEvent(String s, String s2, String s3) {
Log.w("PUSHER", "An event with name " + s2 + " was delivered!!");
}
}, "my-event");
}
}
#Override
public void onError(String message, String code, Exception e) {
Log.w("PUSHER", "There was a problem connecting with code " + code + " and message " + message);
}
}, ConnectionState.ALL);
UPDATE
I'm sure that the problem is with the authentication, there is a function call in iOS version that set some headers to the channel subscription or something like that:
(void)pusher:(PTPusher *)pusher willAuthorizeChannel:(PTPusherChannel *)channel withRequest:(NSMutableURLRequest *)request;
{
[request addAuthorizationHeadersForUser:self.credentials.user];
}
Im trying to figure out where to add the headers in android, try to add it to the authorizer but nothing change:
authorizer.setHeaders(addMapAuthorizationHeaders());
Any idea of what is the equivalent in Android of that iOS function: willAuthorizeChannel??
Ok solved, it was what I thought, the HttpAuthorizer needed a set of headers that you can set directly when creating it like:
HttpAuthorizer authorizer = new HttpAuthorizer(PUSHER_AUTH_URL);
authorizer.setHeaders(MY_AUTH_HEADERS); //a HashMap with the headers
PusherOptions options = new PusherOptions().setEncrypted(true).setWssPort(443).setAuthorizer(authorizer);
pusher = new Pusher(PUSHER_KEY, options);
And with that works fine, in case somebody have a similar problem.
EDIT:
this is how to set the authorization headers. It's a Map set to "Key" "Value" pair for example:
public static HashMap<String, String> getMapAuthorizationHeaders() {
try {
HashMap<String, String> authHeader = new HashMap<>();
authHeader.put("HeaderKey1", "HeaderValue1");
authHeader.put("HeaderKey2", "HeaderValue2");
return authHeader;
} catch (Exception e) {
return null;
}
}
So the pusher config will be like:
authorizer.setHeaders(getMapAuthorizationHeaders());
I've been struggling with this as well... the solution is simple.
First check this out: https://github.com/pusher/pusher-websocket-java/blob/master/src/main/java/com/pusher/client/util/HttpAuthorizer.java
Then implement the abstract interface Authorizer and override the authorize method with your own code and that's it, you get the same thing as on the iOS.
Some snippet to get you started (with a custom constructor):
CustomSocketHttpAuthorizer authorizer = new CustomSocketHttpAuthorizer(ServerComm.API_MAIN_LINK + ServerComm.API_LINK_PUSHER_AUTH, pusherServerAuthTimeStamp, MessageActivity.this);
PusherOptions options = new PusherOptions().setAuthorizer(authorizer).setEncrypted(true);;
clientPusher = new Pusher(ServerComm.PUSHER_CLIENT_KEY, options);
clientPusher.connect(new ConnectionEventListener() .....
Related
want to start development with AWS IOT using Android app
I am seeking for example for IOT in android. need to start basic configuration on AWS console and android app. i already tested temperature demo but didn't get any clue from that! need a basic steps on shadow, policy , role. how to configure them step by step and use of cognito.
below getshadow() method is called onCreate , need to update value on real time basis not ony onCreate.
public void getShadows() {
GetShadowTask getControlShadowTask = new GetShadowTask("TemperatureControl");
getControlShadowTask.execute();
}
private class GetShadowTask extends AsyncTask<Void, Void, AsyncTaskResult<String>> {
private final String thingName;
public GetShadowTask(String name) {
thingName = name;
}
#Override
protected AsyncTaskResult<String> doInBackground(Void... voids) {
try {
GetThingShadowRequest getThingShadowRequest = new GetThingShadowRequest()
.withThingName(thingName);
GetThingShadowResult result = iotDataClient.getThingShadow(getThingShadowRequest);
// Toast.makeText(getApplication(),result.getPayload().remaining(),Toast.LENGTH_LONG).show();
byte[] bytes = new byte[result.getPayload().remaining()];
result.getPayload().get(bytes);
String resultString = new String(bytes);
return new AsyncTaskResult<String>(resultString);
} catch (Exception e) {
Log.e("E", "getShadowTask", e);
return new AsyncTaskResult<String>(e);
}
}
#Override
protected void onPostExecute(AsyncTaskResult<String> result) {
if (result.getError() == null) {
JsonParser parser=new JsonParser();
JsonObject jsonObject= (JsonObject) parser.parse(result.getResult());
response=result.getResult();
setPoint=jsonObject.getAsJsonObject("state").getAsJsonObject("reported")
.get("current_date").getAsString();
textView.setText(setPoint);
// Toast.makeText(getApplication(),setPoint,Toast.LENGTH_LONG).show();
Log.i(GetShadowTask.class.getCanonicalName(), result.getResult());
} else {
Log.e(GetShadowTask.class.getCanonicalName(), "getShadowTask", result.getError());
Toast.makeText(getApplication(),result.getError().toString(),Toast.LENGTH_LONG).show();
}
}
}
UPDATE
Thing Shadow
{
"desired": {
"welcome": "aws-iot"
},
"reported": {
"welcome": "aws-iot",
"current_date": "06-Sep-2017 1:26:40 PM"
}
}
AWS has provided a complete Github repo of Android samples. In the samples do the PubSubWebSocket to connect, subscribe and publish the data to the shadow.
If you have a closer look into the PubSubWebSocket example you will find a detailed information on how to to make a thing policy and role. It cannot be more concise and clear than that.
For understanding and using Cognito follow AmazonCognitoAuthDemo example to make the identity pool and use it in the PubSubWebSocket example.
To get a better understanding of roles and Cognito. Please read the AWS documentation.
Update:
In the IoT thing policy did you give appropriate permissions to connect, subscribe and publish. The option can be found in AWS IoT->Security->Policy->Create Policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": "arn:aws:iot:us-east-2:293751794947:topic/replaceWithATopic"
}
]
}
The above policy gives all access to the user. Also, make sure your pool which you created is for unauthenticated users.
To get the changes to the shadow type the following in the sample android(WebSocketAwsPubSub) edit box $aws/things/thing_name/shadow/update/accepted
And to publish the data to the shadow type $aws/things/thing_name/shadow/update
Update 2:
Android Code where you will receive the reported messaged. Its suscribing to the device. Its the copy of the snippet from PubSubWebSocketSample.
public void AwsSubscribe(){
final String topic = "$aws/things/D1/shadow/update/accepted";
Log.d(LOG_TAG, "topic = " + topic);
try {
mqttManager.subscribeToTopic(topic, AWSIotMqttQos.QOS0,
new AWSIotMqttNewMessageCallback() {
#Override
public void onMessageArrived(final String topic, final byte[] data) {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
String message = new String(data, "UTF-8");
Log.d(LOG_TAG, "Message arrived:");
Log.d(LOG_TAG, " Topic: " + topic);
Log.d(LOG_TAG, " Message: " + message);
tvLastMessage.setText(message);
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Message encoding error.", e);
}
}
});
}
});
} catch (Exception e) {
Log.e(LOG_TAG, "Subscription error.", e);
}
}
If you want to create a topic, just change the value of this variable final String topic = "YOUR TOPIC" then subscribe to it by using the sample code.
I am integratingV3 version in my app.. Notification controls and app lock controls does not appear for the device which initiated the casting . if i connect from the other devices i could see the controls..
My cast provider is as follows
public class CastOptionsProvider implements OptionsProvider {
public static final String CUSTOM_NAMESPACE = "urn:x-cast:com.test.cast.player";
// #Override
// public CastOptions getCastOptions(Context context) {
// List<String> supportedNamespaces = new ArrayList<>();
// supportedNamespaces.add(CUSTOM_NAMESPACE);
// CastOptions castOptions = new CastOptions.Builder()
// .setReceiverApplicationId(context.getString(R.string.app_id))
// .setSupportedNamespaces(supportedNamespaces)
// .build();
// return castOptions;
// }
#Override
public CastOptions getCastOptions(Context context) {
List<String> supportedNamespaces = new ArrayList<>();
supportedNamespaces.add(CUSTOM_NAMESPACE);
NotificationOptions notificationOptions = new NotificationOptions.Builder()
.setActions(Arrays.asList(MediaIntentReceiver.ACTION_SKIP_NEXT,
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{1, 2})
.setTargetActivityClassName(CustomExpandedControlsActivity.class.getName())
.build();
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
.setImagePicker(new ImagePickerImpl())
.setNotificationOptions(notificationOptions)
.setExpandedControllerActivityClassName(CustomExpandedControlsActivity.class.getName())
.build();
return new CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
//.setSupportedNamespaces(supportedNamespaces)
.setCastMediaOptions(mediaOptions)
.build();
}
#Override
public List<SessionProvider> getAdditionalSessionProviders(Context appContext) {
return null;
}
private static class ImagePickerImpl extends ImagePicker {
#Override
public WebImage onPickImage(MediaMetadata mediaMetadata, int type) {
if ((mediaMetadata == null) || !mediaMetadata.hasImages()) {
return null;
}
List<WebImage> images = mediaMetadata.getImages();
if (images.size() == 1) {
return images.get(0);
} else {
if (type == ImagePicker.IMAGE_TYPE_MEDIA_ROUTE_CONTROLLER_DIALOG_BACKGROUND) {
return images.get(0);
} else {
return images.get(1);
}
}
}
}
}
Probably a bit late to help you, but I'll answer in case this helps others. I wrestled with this issue for several days. In the end the problem turned out to be in the custom receiver app we developed, not in the Android app. The presence of the playback controls in the sender app (the Android side) depends on receiving exactly the right payload in the messages sent via the message bus in the media namespace (urn:x-cast:com.google.cast.media). So if your receiver app is not providing all of the correct data structures, or sending things in an unexpected sequence, the playback controls will not show up on the Android side. To debug this you'll need to compare the logs from an app that works with the one that doesn't. You can see what messages are coming back to the Android sender by adding a listener for the media namespace channel:
public static final String MEDIA_NAMESPACE = "urn:x-cast:com.google.cast.media";
private Cast.MessageReceivedCallback messageReceivedCallback = new Cast.MessageReceivedCallback() {
#Override
public void onMessageReceived(CastDevice castDevice, String namespace, String message) {
Log.d(TAG, "Received message (" + namespace + "): " + message);
}
};
castSession.setMessageReceivedCallbacks(MEDIA_NAMESPACE, messageReceivedCallback);
In my case there were two problems. The receiver app was not sending the volume in the correct format, and we were not sending out an initial 'IDLE' message that included all of the correct media information. Any deviation from the expected message format can result in a parse error on the sender side that breaks the normal flow. If that happens you won't see any information for the loaded media or the playback controls when you begin casting. The first message sent out always seems to be an 'IDLE' message, and it looks like this in our app:
{
"type":"MEDIA_STATUS",
"status":[
{
"mediaSessionId":1,
"playbackRate":1,
"playerState":"IDLE",
"currentTime":0,
"supportedMediaCommands":15,
"volume":{
"level":1,
"muted":false
},
"media":{
"contentId":"http://your.server/movie.mp4",
"streamType":"BUFFERED",
"contentType":"application/x-mpegurl",
"metadata":{
"metadataType":1,
"images":[
{
"url":"http://your.server/some.jpg",
"width":200,
"height":200
}
],
"title":"The Movie",
"subtitle":"The Thing Worth Watching"
},
"duration":0,
"customData":{
"description":"A very cool movie that you will probably want to see."
}
},
"currentItemId":1,
"extendedStatus":{
"playerState":"LOADING",
"media":{
"contentId":"http://your.server/movie.mp4",
"streamType":"BUFFERED",
"contentType":"application/x-mpegurl",
"metadata":{
"metadataType":1,
"images":[
{
"url":"http://your.server/some.jpg",
"width":200,
"height":200
}
],
"title":"The Movie",
"subtitle":"The Thing Worth Watching"
},
"duration":0,
"customData":{
"description":"A very cool movie that you will probably want to see."
}
}
},
"repeatMode":"REPEAT_OFF"
}
],
"requestId":0
}
Your app might not be providing all of the same data, so your message will look a bit different, but you should make sure that all of the fields that you need are there and have the correct members and data types.
I need a working example for a custom API for Microsoft Azure App Service.
I could not get any useful or working information/examples for that, or they just show each time different approaches which are outdated?!?!
For now I have a working table controller which gets information from database and returns it back to my Android client. Now I need to define a custom API Controller to get a string back. In the examples they are all sending an object to the service in order to get an object back. I do not want to send anything to the API, just retrieve some information back from a GET Request.
Regards
// EDIT - Added / edited client / server code to Post a String.
You can use the following code to do a GET request on the auto generated API controller Visual Studio creates (ValuesController).
private void getStringFromAzure() throws MalformedURLException {
// Create the MobileService Client object and set your backend URL
String yourURL = "https://yourApp.azurewebsites.net/";
MobileServiceClient mClient = new MobileServiceClient(yourURL, this);
// Your query pointing to yourURL/api/values
ListenableFuture<JsonElement> query = mClient.invokeApi("values", null, GetMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
// Since you are on a async task, you need to show the result on the UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Throwable throwable) {
Log.d(TAG, "onFailure: " + throwable.getMessage());
}
});
}
public void sendString(final String someString) throws MalformedURLException {
// Your query pointing to /api/values/{String}
ListenableFuture<JsonElement> query = mClient.invokeApi("values/" + someString, null, PostMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
}
#Override
public void onFailure(Throwable throwable) { }
});
}
The backend API: (ValuesController)
{
// Use the MobileAppController attribute for each ApiController you want to use
// from your mobile clients
[MobileAppController]
public class ValuesController : ApiController
{
// GET api/values
public string Get()
{
return "Hello World!";
}
// POST api/values/inputString
public string Post(string inputString)
{
return inputString;
}
}
}
You can also send parameters along in the following way:
List<Pair<String, String>> parameters = new ArrayList<>();
parameters.add(new Pair<>("name", "John"));
parameters.add(new Pair<>("password", "fourwordsalluppercase"));
ListenableFuture<JsonElement> query = client.invokeApi("yourAPI", PostMethod, parameters);
Or as json in the body:
JsonObject body = new JsonObject();
body.addProperty("currentPassword", currentPassword);
body.addProperty("password", password);
body.addProperty("confirmPassword", confirmPassword);
ListenableFuture<JsonElement> query = mClient.invokeApi("yourAPI", body, PostMethod, null);
Based on my understanding, I think there are two parts in your question which include as below. And I think you can separately refer to two sections to get the answers and write your own example.
How to define a custom API on Azure Mobile App to retrieve data from database? Please refer to the section Custom APIs to know how to do with Azure Mobile App backend.
How to call a custom API from Android App? Please refer to the section How to: Call a custom API to know how to do with Android SDK.
We were using GoogleCloudMessaging.getInstance(context).send(context.getString(R.string.gcm_defaultSenderId) + "#gcm.googleapis.com", mId, mBundle); to send upstream messages, but since I was trying to migrate new fcm concept, I need to change that too, but could not find any documentation yet.
My best guess is to use :
RemoteMessage message = new RemoteMessage.Builder(<?>).setMessageId(mId).setData ...
FirebaseMessaging.getInstance().send(message);
but then what is it the Builder takes as a parameter? Yet again, could not find api...
So simply as title states, how to send upstream messages using new fcm concept?
Well, my answer comes fast. Keeping question and answer for future reference. I have found the answer on https://firebase.google.com/docs/cloud-messaging/android/upstream#sample-send
thanks to google's smart exclusion to search results In order to show you the most relevant results, we have omitted some entries very similar to the 2 already displayed.
If you like, you can repeat the search with the omitted results included.
new API would be like:
FirebaseMessaging fm = FirebaseMessaging.getInstance();
fm.send(new RemoteMessage.Builder(SENDER_ID + "#gcm.googleapis.com")
.setMessageId(Integer.toString(msgId.incrementAndGet()))
.addData("my_message", "Hello World")
.addData("my_action","SAY_HELLO")
.build());
Well you can send your message directly to android devices from android application, here is the simple implementation I have done and it works great for me.
compile android volley library
compile 'com.android.volley:volley:1.0.0'
Just copy paste this simple function ;) and your life will become smooth just like knife in butter. :D
public static void sendPushToSingleInstance(final Context activity, final HashMap dataValue /*your data from the activity*/, final String instanceIdToken /*firebase instance token you will find in documentation that how to get this*/ ) {
final String url = "https://fcm.googleapis.com/fcm/send";
StringRequest myReq = new StringRequest(Request.Method.POST,url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(activity, "Bingo Success", Toast.LENGTH_SHORT).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(activity, "Oops error", Toast.LENGTH_SHORT).show();
}
}) {
#Override
public byte[] getBody() throws com.android.volley.AuthFailureError {
Map<String,String> rawParameters = new Hashtable<String, String>();
rawParameters.put("data", new JSONObject(dataValue).toString());
rawParameters.put("to", instanceIdToken);
return new JSONObject(rawParameters).toString().getBytes();
};
public String getBodyContentType()
{
return "application/json; charset=utf-8";
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "key="+YOUR_LEGACY_SERVER_KEY_FROM_FIREBASE_CONSOLE);
return headers;
}
};
Volley.newRequestQueue(activity).add(myReq);
}
Note
If you want to send message to topics so you can change parameter instanceIdToken to something like /topics/topicName.
For groups implementation is the same but you just need to take care of parameters. checkout Firebase documentation and you can pass those parameters.
let me know if you face any issue.
I am trying to develop a Facebook application for Android. I am unable to integrate the "Add Comment" feature to photos in Facebook Albums. Using the Graph API, I can show the previously made comments on a photo. However, I just can't add new comments to a photo.
Can somebody provide me some helpful advice?
here is simple example on doing that..
// post comment to single photo
Bundle parameters = new Bundle();
String target = "";
target = "<PHOTO_ID>/comments";
parameters.putString("message", "post Comment testing");
mAsyncRunner.request(target, parameters, "POST", new FacebookRequestListener(target+" (post comment)"));
and here is a simple example for the listener (you can get this from examples of facebook-android-sdk too)
public class FacebookRequestListener extends BaseRequestListener {
String caller = "default";
public FacebookRequestListener() {
}
public FacebookRequestListener(String caller) {
this.caller = caller;
}
public void onComplete(final String response) {
try {
Log.d(TAG, "FacebookRequestListener|"+caller+":" + response);
} catch (Exception e) {
Log.w(TAG, "Error:"+e.getMessage());
}
}
}