Note this examlpe won’t work with the lastest Android builds 1.0+, sorry.
This post provides some details on the complexities of sending a GoogleData message from an Android client to standalone server using XMPP. The XMPP data messages can be used as a message bus for pushing information between Android and a standalone server. Post assumes basic knowledge of Android’s XMPP capabilities and the Smack API. Sorry I’m being really lazy and skipping a lot of detail, maybe if I get some interest I might come back and fill in the blanks.
One of the first gotchas to get this working is to inform android that the jid and resource name that you want to send a message to are ‘google data message capable’. You can do this manually by navigating to the database in the following directory using the adb shell;
data/com.google.android.providers.im
and running this insert statement substituting bare_jid and resource values with your required IM endpoint.
insert into xmppDataMessageCapable (bare_jid, resource) values (’bob@gmail.com’,’server’);
Alternatively you can use the following code to enable google data sending for a given JID and resource via androids XmppDataMessageCapable provider.
public class DataEnabledApplication extends Application
{
private static final String[] XMPPDATACAPABLE_PROJECTION = new String[] { Im.XmppDataMessageCapable.BARE_JID, Im.XmppDataMessageCapable.RESOURCE };
private static final ContentURI XMPPDATACAPABLE_CONTENT_URI = ContentURI.create("content://im/xmppDataMessageCapable");
private static final String XMPPDATACAPABLE_SELECTION = Im.XmppDataMessageCapable.BARE_JID + " = ? AND " + Im.XmppDataMessageCapable.RESOURCE + " = ?";
private static final String[] XMPPDATACAPABLE_SELECTION_ARGS = {BullroarerXmpp.BARE_JID, BullroarerXmpp.RESOURCE};
/**
* In order to push XMMP message to the server JID we need to list the JID as data capable
*/
private void setupServerJIDforGoogleData() {
IContentProvider provider = getContentResolver().getProvider(XMPPDATACAPABLE_CONTENT_URI);
Cursor cursor = null;
try {
cursor = provider.query(XMPPDATACAPABLE_CONTENT_URI, XMPPDATACAPABLE_PROJECTION, XMPPDATACAPABLE_SELECTION,
XMPPDATACAPABLE_SELECTION_ARGS, null, null, null);
if (!cursor.first()) {
ContentValues initialValues = new ContentValues();
initialValues.put(Im.XmppDataMessageCapable.BARE_JID, BARE_JID);
initialValues.put(Im.XmppDataMessageCapable.RESOURCE, RESOURCE);
provider.insert(XMPPDATACAPABLE_CONTENT_URI, initialValues);
}
} catch (DeadObjectException e) {
Log.i(LOG_TAG, "unable to setup " + FULL_JID + "as data capable", e);
notificationHelper.logMessage("unable to initalise communication provider (see logs)");
} finally {
if (cursor != null)
cursor.close();
}
}
}
Now you will be able to send an XMPP message to any given Jabber JID, our goal is to send the message to a JID end point on a standalone server process. For more help with sending XMPP message from andoird have a look at the samples in the google documentation or this post.To handle the received messages on the server end we will use a custom smack extension. The code for the extension is below, see smack documentation for instructions on how to use these classes.
/*
* Copyright (C) 2007 Craig B Baker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.elimatta.xmpp.x;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.xmlpull.v1.XmlPullParser;
/**
* @author Craig Baker
* @version $Id$
*
* http://kickjava.com/src/org/jivesoftware/smackx/provider/DataFormProvider.java.htm
* <a href="http://kickjava.com/src/DelayExtensionProvider.java.htm" mce_href="http://kickjava.com/src/DelayExtensionProvider.java.htm">http://kickjava.com/src/DelayExtensionProvider.java.htm
</a> */
public class GoogleDataPacketExtensionProvider implements PacketExtensionProvider
{
/**
* Creates a new GoogleDataPacketExtensionProvider.
* ProviderManager requires that every PacketExtensionProvider has a public, no-argument constructor
*/
public GoogleDataPacketExtensionProvider()
{
}
/** Installs the provider. */
public static void install(ProviderManager manager)
{
manager.addExtensionProvider("x", GoogleXmppDataExtension.NAMESPACE, new GoogleDataPacketExtensionProvider());
}
public PacketExtension parseExtension(XmlPullParser parser) throws Exception
{
GoogleXmppDataExtension result = new GoogleXmppDataExtension();
int c = parser.getAttributeCount();
String name;
String value;
for (int i = 0; i < c; i++)
{
value = parser.getAttributeValue(i);
name = parser.getAttributeName(i);
if (name.equals("intent_action"))
{
result.setIntentAction(value);
}
if (name.equals("token"))
{
result.setToken(value);
}
}
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("app-data")) {
result.addAppData(parseAppData(parser));
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(result.getElementName())) {
done = true;
}
}
}
return result;
}
private GoogleXmppDataExtension.AppData parseAppData(XmlPullParser parser) throws Exception
{
final String type = parser.getAttributeValue("", "type");
final String value = parser.nextText();
return new GoogleXmppDataExtension.AppData(type, value);
}
}
/*
* Copyright (C) 2007 Craig B Baker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.elimatta.xmpp.x;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* Google Data Message extension
*
* @author Craig Baker
*
* http://kickjava.com/src/org/jivesoftware/smackx/packet/MUCUser.java.htm
*/
public class GoogleXmppDataExtension implements PacketExtension
{
public static final String NAMESPACE = "google:data-msg";
private String intentAction;
private String token = "P8s0A-0";
private Set appDatas;
public String getElementName()
{
return "x";
}
public String getNamespace()
{
return NAMESPACE;
}
public String toXML()
{
final StringBuffer buf = new StringBuffer();
buf.append("");
if (appDatas != null)
{
for (AppData data : appDatas)
{
buf.append(data.toXML());
}
}
buf.append("");
return buf.toString();
}
public void setIntentAction(String intentAction)
{
this.intentAction = intentAction;
}
public String getIntentAction()
{
return this.intentAction;
}
public void setToken(String token)
{
this.token = token;
}
public String getToken()
{
return this.token;
}
public Set getAppData()
{
if (appDatas == null)
Collections. emptySet();
return appDatas;
}
public Map getAppDataMap()
{
Map dataMap = new HashMap();
for (AppData appData : appDatas)
{
dataMap.put(appData.getType(), appData.getData());
}
return dataMap;
}
public void addAppData(AppData appData)
{
if (appData == null)
throw new IllegalArgumentException("app data can not be null");
if (appDatas == null)
appDatas = new HashSet();
appDatas.add(appData);
}
public static class AppData
{
private String type;
private String data;
public AppData(String type, String data)
{
this.type = type;
this.data = data;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public String getData()
{
return data;
}
public void setData(String data)
{
this.data = data;
}
public String toXML()
{
StringBuffer buf = new StringBuffer();
buf.append("");
if (getData() != null)
{
buf.append(getData());
}
buf.append("");
return buf.toString();
}
@Override
public String toString()
{
return type + ":" + data;
}
};
/**
* TODO - implement
*/
@Override
public boolean equals(Object obj)
{
return super.equals(obj);
}
@Override
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append("intentAction:" + getIntentAction() + ",");
buf.append("token:" + getToken() + ",");
buf.append("app-data:");
for (GoogleXmppDataExtension.AppData data : getAppData())
{
buf.append("[" + data + "];");
}
return buf.toString();
}
}
hi whats the plugin you are using to get this lovely code display?
By: theWizard on May 19, 2008
at 12:42 pm
Dear,
Could you please provide the link to smack.jar for android?
The original link is obsolete.
Thanks alot,
Canh cut
By: Canh cut on November 14, 2008
at 8:48 am
I seemed to have trouble even compiling the first app. Do I need to import any package for IM?
Thanks.
By: Chunyen Liu on December 28, 2008
at 4:39 am
This code most likely doesn’t work against the latest SDK. I remember reading that Google had disabled this functionality out of security concerns.
By: Craig Baker on December 28, 2008
at 9:10 am
Just want to share the info with fellow Android developers. How to create a patched version of Smack that works with the latest release Android SDK 1.0 is available here:
http://blog.jayway.com/2008/11/21/give-back-my-xmpp-in-android/
By: Chunyen Liu on January 8, 2009
at 3:31 pm