I have a Servlet deployed on Google App Engine, which is playing the role of posting broadcast message to GCM. Android clients will receive that broadcast message from GCM. The Servlet extends BaseServlet with following snippet.
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//when receiving a gcm broadcast request, send message to GCM
Builder mb = new Message.Builder();
mb.addData("message", "The message to send");
Message message = mb.build();
sender.sendNoRetry(message, regIds);
...
}
When "the message to send" is in English, all things fine. But if "the message to send" is replaced by other language such as Chinese, the Android client will receive a string of garbled text. On Android client, I use a class extends GCMBaseIntentService to deal with GCM broadcast.
#Override
protected void onMessage(Context context, Intent intent) {
String message = "";
message = intent.getStringExtra("message")!=null ? intent.getStringExtra("message") : "";
doNotify(message);
}
I have tried to re-encode the message but doesn't work.
message = new String(message.getBytes("ISO-8859-1"), "UTF-8");
Have any idea about the problem? I need your help, Thanks.
Try URLEncoder
mb.addData("message", URLEncoder.encode("世界","UTF-8");
another option:
mb.addData("message", new StringEntity("世界", "UTF-8");
After looking at the source code of GCM : com.google.android.gcm.server.Sender , it is useing HttpPost as json, and Java uses UTF-16 to internally so before you post, you will need to encode it properly.
And as comment stated, at client decode the String
String yourAwesomeUnicodeString=URLDecoder.decode(intent.getStringExtra("message"),"UTF-8");
Related
I'm newby in smack 4.2.4 and xmpp. I've sent bunch of messages but the recipent is not available to get them,I've close the application and next time when I'll open the application want to check the status of messages, which is delivered or not.
You can use XEP-0184: Message Delivery Receipts for check delivery of messages to destination. First you must add the gradle dependency of smack-extensions:
implementation 'org.igniterealtime.smack:smack-extensions:4.2.2;
Then use this code, when you want to send a message, to add receipt request to stanza:
DeliveryReceiptRequest.addTo(message);
Then you can receive the delivery status in a listener like this:
DeliveryReceiptManager d = DeliveryReceiptManager.getInstanceFor(connection);
d.addReceiptReceivedListener(new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
Log.i("delivery", "for: " + receiptId + " received");
//here you can use sid or receiptId to identify which message is delivered
}
});
Consider that when you are sending message, a random unique stanza-id (sid)
will be setted to your stanza. you must save it in your message row in database, then you can identify that with this sid when receipt received.
I am developing an android chatting app with the use of Openfire as a support server for XMPP and smack library as a android implementation of XMPP.
Things are going well. Till i find this received message from another user. The format is like this :
<message to="rajesh2#peacock-hp" id="0mpqe-10" type="chat" from="rajesh1#peacock-hp/Smack">
<body>{"Date":"8 Jul 2016","Time":"0:40p.m.","body":" vhklv","isMine":true,"msgid":"909-08","receiver":"rajesh2","sender":"rajesh1","senderName":"rajesh1"}</body>
<thread>06ed73bb-21ad-4276-80cb-0ea4fc9d9dfb</thread>
</message>
My listener which is receiving messages :
private class MMessageListener implements ChatMessageListener {
public MMessageListener(Context contxt) {
}
#Override
public void processMessage(final org.jivesoftware.smack.chat.Chat chat,
final Message message) {
Log.i("MyXMPP_MESSAGE_LISTENER", "Xmpp message received: '"
+ message);
}
}
My Question is : Can i receive this message in JSON format instead of
XML ??
As I am learning smack and xmpp please guide me if i am wrong at some places. correct me if any one of you find me wrong.
You can convert messages to JSON format through a project on Github.
Example :
public class Main {
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
public static String TEST_XML_STRING =
"<?xml version=\"1.0\" ?><test attrib=\"moretest\">Turn this to JSON</test>";
public static void main(String[] args) {
try {
JSONObject xmlJSONObj = XML.toJSONObject(TEST_XML_STRING);
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
System.out.println(jsonPrettyPrintString);
} catch (JSONException je) {
System.out.println(je.toString());
}
}
}
Output is:
{
"test": {
"attrib": "moretest",
"content": "Turn this to JSON"
}
}
Credit goes to Quickest way to convert XML to JSON in Java
Json it's not the reply format for Openfire.
Of course, you can rewrite all Openfire to "talk" in Json, but to me has no sense.
What I suggest to you:
if you have a performance issue, you can look maybe for Ejabber
If you need a Json, maybe Prosody IM has a plugin
If you don't want to broke your head with XmlPullParser, give a try
to Bubbler in alternative to Smack.
If you just don't feel confident with XML, just implement a "toJson" for each Stanza type so you'll have something like
(thanking #Khan)
MyMessage extends Message
public String toJson()
{
JSONObject xmlJSONObj = XML.toJSONObject(this.toXML());
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
}
and you'll be able to use a Json.
I followed Google Cloud Messaging (GCM) with local device groups on Android gives HTTP Error code 401 to manage local device groups on Android and successfully got a notification key, but when I send message to the notification key, I never get the message back.
Has anyone ever got this work?
My send code is like:
public void sendMessage(View view) {
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
try {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
String to = notificationKey; // the notification key
AtomicInteger msgId = new AtomicInteger();
String id = Integer.toString(msgId.incrementAndGet());
Bundle data = new Bundle();
data.putString("hello", "world");
gcm.send(to, id, data);
Log.e(TAG, "sendMessage done.");
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
return null;
}
};
task.execute();
}
It seems there's a misunderstanding about the GCM concept. The app server is an integral part of GCM messaging.
The server side of Google Cloud Messaging (GCM) consists of two
components:
GCM connection servers provided by Google. These servers take messages
from an app server and send them to a client app running on a device.
Google provides connection servers for HTTP and XMPP.
An application
server that you must implement in your environment. This application
server sends data to a client app via the chosen GCM connection
server, using the appropriate XMPP or HTTP protocol.
Try the Android GCM Playground to get a better understanding of this.
Here's a snippet:
public void sendMessage() {
String senderId = getString(R.string.gcm_defaultSenderId);
if (!("".equals(senderId))) {
String text = upstreamMessageField.getText().toString();
if (text == "") {
showToast("Please enter a message to send");
return;
}
// Create the bundle for sending the message.
Bundle message = new Bundle();
message.putString(RegistrationConstants.ACTION, RegistrationConstants.UPSTREAM_MESSAGE);
message.putString(RegistrationConstants.EXTRA_KEY_MESSAGE, text);
try {
gcm.send(GcmPlaygroundUtil.getServerUrl(senderId),
String.valueOf(System.currentTimeMillis()), message);
showToast("Message sent successfully");
} catch (IOException e) {
Log.e(TAG, "Message failed", e);
showToast("Upstream FAILED");
}
}
}
The to field of the send method represents the sender ID of your project. You cannot use this method to send messages to Instance ID tokens (other devices), Device to Device messaging is not currently supported by GCM.
You are correct to avoid including the API key in your client app, so currently you will need an app server to send these types of messages.
I am trying to send custom information with IQ in asmack from android.
So I am using below code to send the custom IQ message.
public void onClick(View arg0) {
CustomIQ req = new CustomIQ();
req.myData="Hello world";
req.setType(IQ.Type.GET);
req.setTo(Activity_title+Constants.DOMAIN);
MainActivity.connection.sendPacket(req);
Log.d(Constants.TAG, "xml value :"+req.toXML());
Log.d(Constants.TAG, "child element value :"+req.getChildElementXML());
Log.d(Constants.TAG, " custom IQ req sent");
Below is my custom IQ class implementation:
import org.jivesoftware.smack.packet.IQ;
public class CustomIQ extends IQ {
String myData;
#Override
public String getChildElementXML() {
String request = "<query xmlns='myxmlns'>"
+ "<myData>"+ myData + "</myData>"
+ "</query>";
return request;
}
}
}
But after the sending the custom IQ, I am getting in the IQ listener as Service Unavailable and error code as 503.
Below are the request to server :
xml value :<iq id="BTn30-5" to="swathi#btp121374" type="get"><query xmlns='myxmlns'><myData>Hello world</myData></query></iq>
child element value :<query xmlns='myxmlns'><myData>Hello world</myData></query>
Below is the response from server:
xml value :<iq id="BTn30-5" to="ganesh#btp121374/Smack" from="swathi#btp121374" type="error"><query xmlns='myxmlns'><myData>Hello world</myData></query><error code="503" type="CANCEL"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
So what is the reason for I am getting response from server as Service Unavailable.
Edit:
I implemented the IQProvider as below
public class customIQProvider implements IQProvider{
#Override
public IQ parseIQ(XmlPullParser parser) throws Exception {
Log.d(Constants.TAG, "In custom IQ provider");
CustomIQ myIQ_ref = new CustomIQ();
int eventType = parser.next();
while(eventType == XmlPullParser.START_TAG){
switch(parser.getEventType()){
case XmlPullParser.START_TAG:
{
if(parser.getName().equals("myData")){
myIQ_ref.myData=(parser.nextText());
}
}
return myIQ_ref;
}
}
return null;
}
}
I believe you're falling foul of the iq routing rules in XMPP. If you send an iq stanza to "swathi#btp121374", you're not asking it to be routed to a client, you're asking the btp121374 server to handle it on behalf of swathi#btp121374.
Given the resource of the sending JID, I imagine you want to send to "swathi#btp121374/Smack" or similar. Sending it to the full JID (JID including resource) tells the server to route it to the client instead of handling it itself.
(Note that the routing rules for presence, message and iq are different - the above only applies to iq)
Obviously the server doesn't know how to handle your new namespace, unless you also implement it on the server. What do you expect the server to do with your custom element?
Also, it's generally a bad idea to generate XML by concatenating strings. If myData contains and <, or any of the other characters that need to be escaped, your XML will be invalid. Smack surely has better ways to generate your custom data.
I am creating a project on Google Cloud Messaging (GCM) and am following this tutorial.
I am done with the client-side work and set up the device on the client side. Also I had registered the device using the following code.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, "483910217912");
Log.d(tag, "Registered");
}
else {
Log.v(tag, "Already registered");
}
}
Now I am stuck at a point to create server for my GCM project. Note that I am creating a project to notify when a new message is received. However, I had not implemented the service to receive a message, but I will implement it when the server setting is finished.
You can create a GCM server in Android using the blog post Google cloud Messaging (GCM) tutorial , but I would prefer to use PHP for server side code. You can create a GCM Server in cURL (PHP) in easy steps:
Create a server key from the Google API console page.
Identify the device token of a device for which this message is sent to.
You can find the easy steps in How to implement a GCM PHP push server for Android to implement the push server.
you can use this code
package yourpackage.android.gcm.server;
import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.MulticastResult;
import com.google.android.gcm.server.Sender;
import java.util.ArrayList;
class Notify {
public static void main(String args[]) {
try {
Sender sender = new Sender("AIzaSyCn3N2OIm-EDtiGwTyQfSIB8NRvDtIOx30");
ArrayList<String> devicesList = new ArrayList<String>();
//add you deviceID
devicesList.add("APA91bELVJbxB_NLnLbTkkkX87SDdkJc6OfCN2slhC9t4cLq-KA32eGgiW4-Gi--ZEsEMKIh0AtYJMs5rQGswfm3cH1qK853WcpV98bkaplAaC5AiycDmifuVFSRl21vgf-Rqj0dCrFF");
//devicesList.add("APA91bHIdM4XGqrjJLTuwCX5OOrTYG4ACXYEVkZDM1bPs5qFdzJP4Bpql-sZqyKB8BU7fDtdxB84aTygHLyASYg_XNY6lqrcA4wj4sZHJXGVFzz_0UEADMfFCx9NAfRZxunIYso_dkBa");
//APA91bFA-i2l3iEMnIBs0JK80pTLHOsE7p1s-DysRpKGas1MQOVILyIs9xwY7soysSWGz5Uif68uXR6F5Xn0tCTYesv78uQZxhC310a1cvf8aFohhfMGY6awbOSg3t1GRz2i3U-8kVSF
// Use this line to send message without payload data
// Message message = new Message.Builder().build();
// use this line to send message with payload data
Message message = new Message.Builder()
//.collapseKey("message")
//.timeToLive(241000)
.delayWhileIdle(true)
.addData("message", "Your message send")
.build();
/**/
// Use this code to send to a single device
// Result result = sender
// .send(message,
// "APA91bGiRaramjyohc2lKjAgFGpzBwtEmI8tJC30O89C2b3IjP1CuMeU1h9LMjKhmWuZwcXZjy1eqC4cE0tWBNt61Kx_SuMF6awzIt8WNq_4AfwflaVPHQ0wYHG_UX3snjp_U-5kJkmysdRlN6T8xChB1n3DtIq98w",
// 1);
// Use this for multicast messages
MulticastResult result = sender.send(message, devicesList, 1);
//sender.send(message, devicesList, 0);
System.out.println(result.toString());
if (result.getResults() != null) {
int canonicalRegId = result.getCanonicalIds();
if (canonicalRegId != 0) {
}
} else {
int error = result.getFailure();
System.out.println(error);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
The com.google.android.gcm.server library is deprecated. Just encode your message to JSON object and POST it to GCM URL https://android.googleapis.com/gcm/send
JSON example:
{
"registration_ids" : ["APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",...],
"data" : {
"Team" : "Portugal",
"Score" : "3",
"Player" : "Varela",
},
}
Here is more http://developer.android.com/google/gcm/http.html
You can find sample code for gcm-client and gcm-server in the Android SDK directory. It is good point to get started. Directory is :
path_to_android_sdk/extras/google/gcm/samples
In your main function implement following code to send push notification to your app
final String apiKey = "specify your api key generated by gcm";
To make http connection to gcm using following code
URL url = new URL("https://android.googleapis.com/g...");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Authorization", "key="+apiKey);
conn.setDoOutput(true);
JSON message format accepted by GCM
String input = "{\"registration_ids\" : [\"Specify token you got from GCM\"],\"data\" : {\"message\": \"hai welcome\"},}";
To send notification
OutputStream os = conn.getOutputStream();
os.write(input.getBytes());
os.flush();
In your client app you need to have proper BroadcastReceiver class to receive the message sent from GCM
I would insist you to test the demo that is being provided on the develpers site. I had just created a demo sample based on that with all the steps that one should follow for executing the demo sample. You can check my blog and also find the source from my github.