Search...

Thursday, December 29, 2011

How to use UPNP search in android?


Copy the below classes in your project and call TestUpnp class.


Main-


public class TestUpnp{


public static void main(String args[]){


    TestUPNPDevice[] devices = TestUpnpManager.getInstance()


.discoverDevices(TestvUPNPConstant.ALL_UPNP_DEVICES, null);

        for(TestUPNPDevice:devices){
             //print searched devices.

         }

     }


}

(0) TestUpnpManager 
package com.code.upnp;

/***
 *
 * Upnp Manager class.
 *
 *
 */
import java.io.IOException;
import com.directv.common.lib.upnp.util.DirectvDiscovery;
import com.directv.common.lib.upnp.util.DirectvUPNPDevice;

public class TestUpnpManager {

private static DirectvUpnpManager singleton = null;

private DirectvUpnpManager() {
}

public final static DirectvUpnpManager getInstance() {

synchronized (DirectvUpnpManager.class) {

if (singleton == null) {
singleton = new DirectvUpnpManager();
}

}
return singleton;
}

public DirectvUPNPDevice[] discoverDevices(String searchTarget,
String manufacName) throws IOException, Exception {

return DirectvDiscovery.discoverDevices(searchTarget, manufacName);
}

}




(1)TestDiscovery.java

package com.code.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import com.Test.navigator.TestApplication;

import android.util.Log;

public class TestDiscovery {

// XML tag name returned from each device.
private final static String DEVICE_TYPE = "deviceType";
private final static String FRIENDLY_NAME = "friendlyName";
private final static String MANUFACTURER = "manufacturer";
private final static String MANUFACTURER_URL = "manufacturerURL";
private final static String MODEL_DESC = "modelDescription";
private final static String MODEL_NAME = "modelName";
private final static String MODEL_NUMBER = "modelNumber";
private final static String UDN = "UDN";
private final static String URL_BASE = "URLBase";
private static boolean DEBUG = TestApplication.showLogs();

public TestDiscovery(){
DEBUG = TestApplication.showLogs();
}

/***
* It will send the http request using supplied url
*
* @param url
* @return http reponse.
* @throws URISyntaxException
* @throws ClientProtocolException
* @throws IOException
*/
static public InputStream getUrlData(String url) throws URISyntaxException,
ClientProtocolException, IOException {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet method = new HttpGet(new URI(url));

HttpResponse res = client.execute(method);
return res.getEntity().getContent();
}

/***
* Parse the XML response by using Pull parser and set bean for device
*
*
* @param location
* @return Device Object
* @throws IOException
* @throws Exception
*/

static public TestUPNPDevice getDeviceInfo(String location)
throws IOException, Exception {

TestUPNPDevice device = new TestUPNPDevice();

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();

xpp.setInput(new InputStreamReader(getUrlData(location)));

int eventType = xpp.getEventType();
String currentTagName = null;
while (eventType != XmlPullParser.END_DOCUMENT) {

if (eventType == XmlPullParser.START_TAG) {
currentTagName = xpp.getName();

if(DEBUG){
Log.d("TAG NAME", currentTagName);
}

} else if (eventType == XmlPullParser.TEXT) {

if (currentTagName != null) {

if (currentTagName.equalsIgnoreCase(DEVICE_TYPE)) {
device.setDeviceType(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(FRIENDLY_NAME)) {
device.setFriendlyName(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(MANUFACTURER)) {
device.setManufacturer(xpp.getText());
} else if (currentTagName
.equalsIgnoreCase(MANUFACTURER_URL)) {
device.setManufacturerURL(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(MODEL_DESC)) {
device.setModelDescription(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(MODEL_NAME)) {
device.setModelName(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(MODEL_NUMBER)) {
device.setModelNumber(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(UDN)) {
device.setUDN(xpp.getText());
} else if (currentTagName.equalsIgnoreCase(URL_BASE)) {
device.setUrlBase(xpp.getText());
}

if(DEBUG){
Log.d("TAG VALUE", xpp.getText());
}
currentTagName = null;
}

}
eventType = xpp.next();
}

return device;

}

/***
*
* Main method to start searching UPNP devices.
*
*
* @param searchTarget
* @param manufacName
* @return array of Device object
* @throws IOException
* @throws Exception
*/

public static TestUPNPDevice[] discoverDevices(String searchTarget,
String manufacName) throws IOException, Exception {
if (searchTarget == null || searchTarget.trim().length() == 0) {
throw new IllegalArgumentException("Illegal searchTarget");
}

final Map<String, TestUPNPDevice> devices = new HashMap<String, TestUPNPDevice>();
final String manufacturerName = manufacName;

// Implement the device discovery handler.
TestDiscoveryResultsHandler handler = new TestDiscoveryResultsHandler() {

public void discoveredDevice(String usn, URL location)
throws IOException, Exception {

if(DEBUG){
Log.i("NEW", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
Log.i("DEVICE LOCATION", location.toString());
Log.i("DEVICE USN", usn);
}

TestUPNPDevice searchedDevice = getDeviceInfo(location
.toString());
if (searchedDevice != null) {

if (manufacturerName != null
&& searchedDevice.getManufacturer() != null) {
// Only the requested manufacturer device should be
// added.
if (searchedDevice.getManufacturer().equalsIgnoreCase(
manufacturerName)) {
devices.put(usn, searchedDevice);
}
} else {
devices.put(usn, searchedDevice);
}

}

}
};

// Register the device discovery handler so that it should be call when
// each device is discovered.

TestDiscoveryListener.getInstance().registerResultsHandler(handler,
searchTarget);

// Find the User Internet address and send the Multicast message
for (Enumeration<?> e = NetworkInterface.getNetworkInterfaces(); e
.hasMoreElements();) {
NetworkInterface intf = (NetworkInterface) e.nextElement();
for (Enumeration<?> adrs = intf.getInetAddresses(); adrs
.hasMoreElements();) {
InetAddress adr = (InetAddress) adrs.nextElement();
if (adr instanceof Inet4Address && !adr.isLoopbackAddress()) {
sendSearchMessage(adr, TestUPNPConstant.DEFAULT_TTL,
TestUPNPConstant.DEFAULT_MX, searchTarget);
}
}
}

// Wait for sometime to get response from all the upnp devices on
// network.
try {
Thread.sleep(TestUPNPConstant.DEFAULT_TIMEOUT);
} catch (InterruptedException ex) {
// don't care
}

// Unregister the device discovery handler.
TestDiscoveryListener.getInstance().unRegisterResultsHandler(
handler, searchTarget);

// Create array to put all the searched devices.
int j = 0;

TestUPNPDevice[] rootDevices = new TestUPNPDevice[devices.size()];
for (Iterator<TestUPNPDevice> i = devices.values().iterator(); i.hasNext();) {

rootDevices[j++] = (TestUPNPDevice) i.next();
}
if(DEBUG){
Log.i("TOTAL DEVICE FOUND", rootDevices.length + "");
}
return rootDevices;

}

/**
* Sends an SSDP search message on the network
*
* @param src
*            the sender ip
* @param ttl
*            the time to live
* @param mx
*            the mx field
* @param searchTarget
*            the search target
* @throws IOException
*             if some IO errors occurs during search
*/
public static void sendSearchMessage(InetAddress src, int ttl, int mx,
String searchTarget) throws IOException {

InetSocketAddress adr = new InetSocketAddress(
InetAddress.getByName(TestUPNPConstant.SSDP_IP),
TestUPNPConstant.SSDP_PORT);

java.net.MulticastSocket skt = new java.net.MulticastSocket(null);
skt.bind(new InetSocketAddress(src,
TestUPNPConstant.DEFAULT_SSDP_SEARCH_PORT));
skt.setTimeToLive(ttl);
StringBuffer packet = new StringBuffer();
packet.append("M-SEARCH * HTTP/1.1\r\n");
packet.append("HOST: ").append(TestUPNPConstant.SSDP_IP).append(":")
.append(TestUPNPConstant.SSDP_PORT).append("\r\n");
packet.append("MAN: \"ssdp:discover\"\r\n");
packet.append("MX: ").append(mx).append("\r\n");
packet.append("ST: ").append(searchTarget).append("\r\n")
.append("\r\n");
if (TestUPNPConstant.ENABLE_LOG) {
System.out.println("Sending discovery message on "
+ TestUPNPConstant.SSDP_IP + ":"
+ TestUPNPConstant.SSDP_PORT
+ "multicast address from ip " + src.getHostAddress()
+ ":\n" + packet.toString());
}
String toSend = packet.toString();
byte[] pk = toSend.getBytes();
skt.send(new DatagramPacket(pk, pk.length, adr));
skt.disconnect();
skt.close();
}

}

(2)TestDiscoveryListener.java

package com.code.util;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/****
 *
 *
 *
 * This class has the logic to listen the multicast response and process it.
 *
 *
 *
 */
public class TestDiscoveryListener implements Runnable {

private static boolean MATCH_IP = true;
private Map<String, Set<TestDiscoveryResultsHandler>> registeredHandlers = new HashMap<String, Set<TestDiscoveryResultsHandler>>();
private final Object REGISTRATION_PROCESS = new Object();
private final static TestDiscoveryListener singleton = new TestDiscoveryListener();
private boolean inService = false;
private boolean daemon = true;
private java.net.MulticastSocket skt;
private DatagramPacket input;

private TestDiscoveryListener() {
}

public final static TestDiscoveryListener getInstance() {
return singleton;
}

/**
* Sets the listener as a daemon thread
*
* @param daemon
*            daemon thread
*/
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}

/**
* Registers an SSDP response message handler
*
* @param resultsHandler
*            the SSDP response message handler
* @param searchTarget
*            the search target
* @throws IOException
*             if some errors occurs during SSDP search response messages
*             listener thread startup
*/
public void registerResultsHandler(
TestDiscoveryResultsHandler resultsHandler, String searchTarget)
throws IOException, Exception {
synchronized (REGISTRATION_PROCESS) {
if (!inService)
startDevicesListenerThread();
Set<TestDiscoveryResultsHandler> handlers = registeredHandlers.get(searchTarget);
if (handlers == null) {
handlers = new HashSet<TestDiscoveryResultsHandler>();
registeredHandlers.put(searchTarget, handlers);
}
handlers.add(resultsHandler);
}
}

/**
* Unregisters an SSDP response message handler
*
* @param resultsHandler
*            the SSDP response message handler
* @param searchTarget
*            the search target
*/
public void unRegisterResultsHandler(
TestDiscoveryResultsHandler resultsHandler, String searchTarget) {
synchronized (REGISTRATION_PROCESS) {
Set<TestDiscoveryResultsHandler> handlers = registeredHandlers.get(searchTarget);
if (handlers != null) {
handlers.remove(resultsHandler);
if (handlers.size() == 0) {
registeredHandlers.remove(searchTarget);
}
}
if (registeredHandlers.size() == 0) {
stopDevicesListenerThread();
}
}
}

private void startDevicesListenerThread() throws IOException, Exception {
synchronized (singleton) {
if (!inService) {

this.startMultiCastSocket();
Thread deamon = new Thread(this,
"TestDiscoveryListener daemon");
deamon.setDaemon(daemon);
deamon.start();
while (!inService) {
// wait for the thread to be started let's wait a few ms
try {
Thread.sleep(2);
} catch (InterruptedException ex) {
// don t care
}
}
}
}
}

private void stopDevicesListenerThread() {
synchronized (singleton) {
inService = false;
}
}

private void startMultiCastSocket() throws IOException {
int bindPort = TestUPNPConstant.DEFAULT_SSDP_SEARCH_PORT;

skt = new java.net.MulticastSocket(null);
skt.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"),
bindPort));
skt.setTimeToLive(TestUPNPConstant.DEFAULT_TTL);
skt.setSoTimeout(TestUPNPConstant.DEFAULT_TIMEOUT);
skt.joinGroup(InetAddress.getByName(TestUPNPConstant.SSDP_IP));

byte[] buf = new byte[2048];
input = new DatagramPacket(buf, buf.length);

}

public void run() {
if (!Thread.currentThread().getName()
.equals("TestDiscoveryListener daemon")) {
throw new RuntimeException("No right to call this method");
}
inService = true;
while (inService) {
try {
listenBroadCast();
} catch (SocketTimeoutException ex) {
// ignoring

} catch (IOException ioEx) {
// if(TestApplication.showLogs() == true){
// Log.e("IOException","IO Exception during UPNP TestDiscoveryListener messages listening thread" + ioEx);
// }

} catch (Exception ex) {
// if(TestApplication.showLogs() == true){
// Log.e("Exception", "Fatal Error during UPNP TestDiscoveryListener messages listening thread, thread will exit" + ex);
// }
inService = false;
}
}

try {
skt.leaveGroup(InetAddress.getByName(TestUPNPConstant.SSDP_IP));
skt.close();
} catch (Exception ex) {
// ignoring
}
}

private void listenBroadCast() throws IOException, Exception {

skt.receive(input);
InetAddress from = input.getAddress();
String received = new String(input.getData(), input.getOffset(),
input.getLength());

TestHttpResponse msg = null;
try {
msg = new TestHttpResponse(received);
} catch (IllegalArgumentException ex) {

// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING", "Skipping uncompliant HTTP message " + received);
// }

return;
}
String header = msg.getHeader();
if (header != null && header.startsWith("HTTP/1.1 200 OK")
&& msg.getHTTPHeaderField("st") != null) {
// probably a search repsonse !
String deviceDescrLoc = msg.getHTTPHeaderField("location");
if (deviceDescrLoc == null || deviceDescrLoc.trim().length() == 0) {
// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING","Skipping SSDP message, missing HTTP header 'location' field");
// }

return;
}
URL loc = new URL(deviceDescrLoc);
if (MATCH_IP) {
InetAddress locHost = InetAddress.getByName(loc.getHost());
if (!from.equals(locHost)) {
// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING", "Discovery message sender IP " + from + " does not match device description IP " + locHost);
// }
return;
}
}

// if(TestApplication.showLogs() == true){
// Log.i("PROCESSING", "Processing " + deviceDescrLoc + " device description location ");
// }

String st = msg.getHTTPHeaderField("st");
if (st == null || st.trim().length() == 0) {
// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING", "Skipping SSDP message, missing HTTP header 'st' field");
// }

return;
}
String usn = msg.getHTTPHeaderField("usn");
if (usn == null || usn.trim().length() == 0) {
// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING", "Skipping SSDP message, missing HTTP header 'usn' field");
// }
return;
}

synchronized (REGISTRATION_PROCESS) {
Set<TestDiscoveryResultsHandler> handlers = registeredHandlers.get(st);
if (handlers != null) {
for (Iterator<TestDiscoveryResultsHandler> i = handlers.iterator(); i.hasNext();) {
TestDiscoveryResultsHandler handler = (TestDiscoveryResultsHandler) i
.next();
handler.discoveredDevice(usn, loc);
}
}
}
} else {
// if(TestApplication.showLogs() == true){
// Log.i("SKIPPING", "Skipping uncompliant HTTP message"+ received);
// }
}
}
}


(3)TestDiscoveryResultsHandler.java


package com.code.util;

/****
 *
 * This interface is used to handle discovered devices.
 *
 */

import java.io.IOException;

public interface TestDiscoveryResultsHandler {

public void discoveredDevice(String usn, java.net.URL location)
throws IOException, Exception;

}


(4)TestHttpResponse.java

package com.code.util;

import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * Process the http response and store it for future use.
 *
 *
 */
public class TestHttpResponse {

private String header;
private Map<String, String> fields;
private String body;

/**
* Constructor of the response, will try to parse the raw response data
*
* @param rawHttpResponse
*            the raw response data
* @throws IllegalArgumentException
*             if some error occurs during parsing
*/
protected TestHttpResponse(String rawHttpResponse)
throws IllegalArgumentException {
if (rawHttpResponse == null || rawHttpResponse.trim().length() == 0) {
throw new IllegalArgumentException("Empty HTTP response message");
}
boolean bodyParsing = false;
StringBuffer bodyParsed = new StringBuffer();
fields = new HashMap<String, String>();
String[] lines = rawHttpResponse.split("\\r\\n");
this.header = lines[0].trim();

for (int i = 1; i < lines.length; i++) {

String line = lines[i];
if (line.length() == 0) {
// line break before body
bodyParsing = true;
} else if (bodyParsing) {
// we parse the message body
bodyParsed.append(line).append("\r\n");
} else {
// we parse the header
if (line.length() > 0) {
int delim = line.indexOf(':');
if (delim != -1) {
String key = line.substring(0, delim).toUpperCase();
String value = line.substring(delim + 1).trim();
fields.put(key, value);
} else {
throw new IllegalArgumentException(
"Invalid HTTP message header :" + line);
}
}
}
}
if (bodyParsing) {
body = bodyParsed.toString();
}
}

public String getHeader() {
return header;
}

public String getBody() {
return body;
}

public String getHTTPFieldElement(String fieldName, String elementName)
throws IllegalArgumentException {
String fieldNameValue = getHTTPHeaderField(fieldName);
if (fieldName != null) {

StringTokenizer tokenizer = new StringTokenizer(
fieldNameValue.trim(), ",");
while (tokenizer.countTokens() > 0) {
String nextToken = tokenizer.nextToken().trim();
if (nextToken.startsWith(elementName)) {
int index = nextToken.indexOf("=");
if (index != -1) {
return nextToken.substring(index + 1).trim();
}
}
}
}
throw new IllegalArgumentException("HTTP element field " + elementName
+ " is not present");
}

public String getHTTPHeaderField(String fieldName)
throws IllegalArgumentException {
String field = (String) fields.get(fieldName.toUpperCase());
if (field == null) {
throw new IllegalArgumentException("HTTP field " + fieldName
+ " is not present");
}
return field;
}

}



(5)TestUPNPConstant.java

package com.code.util;

/****
 *
 * This class contains Constants which are used during Upnp Device search over
 * network.
 *
 */
public class TestUPNPConstant {

public final static String ALL_UPNP_DEVICES = "upnp:rootdevice";
public final static String UPNP_MEDIA_RENDERER_DEVICES = "urn:schemas-upnp-org:device:MediaRenderer:1";

public static final int DEFAULT_MX = 3;
public static final int DEFAULT_TTL = 4;
public static final int DEFAULT_TIMEOUT = 5000;
public static final int DEFAULT_SSDP_SEARCH_PORT = 1901;
public final static String SSDP_IP = "239.255.255.250";
public final static int SSDP_PORT = 1900;
public static final boolean ENABLE_LOG = false;

public final static String MANUFACTURER_NAME = "Test";

}


(6)TestUPNPDevice 

package com.code.util;

/****
 *
 * This is a bean class for UPNP Device.
 *
 */
public class TestUPNPDevice {

private String deviceType;
private String friendlyName;
private String manufacturer;
private String manufacturerURL;

private String modelDescription;
private String modelName;
private String modelNumber;
private String urlBase;
private String UDN;

public String getDeviceType() {
return deviceType;
}

public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}

public String getFriendlyName() {
return friendlyName;
}

public void setFriendlyName(String friendlyName) {
this.friendlyName = friendlyName;
}

public String getManufacturer() {
return manufacturer;
}

public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}

public String getManufacturerURL() {
return manufacturerURL;
}

public void setManufacturerURL(String manufacturerURL) {
this.manufacturerURL = manufacturerURL;
}

public String getModelDescription() {
return modelDescription;
}

public void setModelDescription(String modelDescription) {
this.modelDescription = modelDescription;
}

public String getModelName() {
return modelName;
}

public void setModelName(String modelName) {
this.modelName = modelName;
}

public String getModelNumber() {
return modelNumber;
}

public void setModelNumber(String modelNumber) {
this.modelNumber = modelNumber;
}

public String getUrlBase() {
return urlBase;
}

public void setUrlBase(String urlBase) {
this.urlBase = urlBase;
}

public String getUDN() {
return UDN;
}

public void setUDN(String uDN) {
UDN = uDN;
}

}






Tuesday, December 6, 2011

How to create Action Bar for smartphone in android

Use the below layout to design your action bar.


 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="fill_parent"
        android:layout_height="50dip" android:background="#ffbbbbbb">
        <TextView android:layout_width="wrap_content" android:layout_centerVertical="true"
                android:textColor="@color/white" android:textSize="7pt"
                android:textStyle="bold" android:layout_height="wrap_content"
                android:text="Your App" android:padding="8dp" />

        <ImageView android:layout_width="1px" android:src="#ffffffff"
                android:layout_height="wrap_content" android:text="@string/hello"
                android:id="@+id/bordertwo" android:layout_toLeftOf="@+id/refresh"
                android:layout_marginRight="12dip" android:layout_alignParentBottom="true"
                android:layout_alignParentTop="true" />

        <ImageView android:layout_width="25dip" android:src="@drawable/title_refresh"
                android:layout_height="25dip" android:text=""
                android:layout_marginRight="12dip" android:layout_centerVertical="true"
                android:id="@+id/refresh" android:layout_toLeftOf="@+id/borderone"
                android:scaleType="fitXY" />

        <ImageView android:layout_width="wrap_content" android:src="#ffffffff" android:layout_height="wrap_content"
                android:id="@+id/borderone" android:layout_alignParentTop="true"
                android:layout_marginRight="12dip" android:layout_alignParentBottom="true"
                android:layout_alignBottom="@+id/search" android:text="@string/hello"
                android:layout_toLeftOf="@+id/search" />

        <ImageView android:src="@drawable/title_search" android:text=""
                android:layout_width="25dip" android:layout_alignParentRight="true"
                android:layout_centerVertical="true" android:id="@+id/search"
                android:layout_marginRight="12dip" android:layout_height="25dip"
                android:scaleType="fitXY" />
</RelativeLayout>

How to fetch String resource value in android

String name= (String )getActivity().getResources().getString(R.string.app_name)

How to remove the Title Bar in android

In order to turning off the title at the top of the screen, simple call the requestWindowFeature(Window.FEATURE_NO_TITLE) in your code.

How to display the HttpResponse response on console in android

         HttpResponse response = httpclient.execute(request);


         ByteArrayOutputStream v2 = new ByteArrayOutputStream();
        response.getEntity().writeTo(v2);
        System.out.println("----Response------------------------------------");
        System.out.println(v2.toString());
        System.out.println("----------------------------------------");
     

Android App Course

One of the best Online Android App development course : https://sites.google.com/site/androidappcourse/home

How to send/receive JSON Objects using HttpClient in android


This tutorial is focused on creating a very simple HTTP client for Google's mobile operating system Android, which then can communicate with a web server and exchange JSON information. I won't go too much into detail, since the code is pretty much self-explaining and already has a lot of comments describing the program flow.

1) Create a new Android project


2) Add permission to access the Internet from your application to your AndroidManifest.xml

<MANIFEST>

2      

3     <USES-PERMISSION android:name="android.permission.INTERNET">

4 </USES-PERMISSION>

5 </MANIFEST>


3) Create a new (static) class called HttpClient.java
package com.devstream.http;

016 import java.io.BufferedReader;

017 import java.io.IOException;

018 import java.io.InputStream;

019 import java.io.InputStreamReader;

020 import java.util.zip.GZIPInputStream;

021 import org.apache.http.Header;

022 import org.apache.http.HttpEntity;

023 import org.apache.http.HttpResponse;

024 import org.apache.http.client.methods.HttpPost;

025 import org.apache.http.entity.StringEntity;

026 import org.apache.http.impl.client.DefaultHttpClient;

027 import org.json.JSONObject;

028 import android.util.Log;

029 public class HttpClient {

030  private static final String TAG = "HttpClient";

031  public static JSONObject SendHttpPost(String URL, JSONObject jsonObjSend) {

032   try {

033    DefaultHttpClient httpclient = new DefaultHttpClient();

034    HttpPost httpPostRequest = new HttpPost(URL);

035    StringEntity se;

036    se = new StringEntity(jsonObjSend.toString());

037    // Set HTTP parameters

038    httpPostRequest.setEntity(se);

039    httpPostRequest.setHeader("Accept", "application/json");

040    httpPostRequest.setHeader("Content-type", "application/json");

041    httpPostRequest.setHeader("Accept-Encoding", "gzip"); // only set this parameter if you would like to use gzip compression

042    long t = System.currentTimeMillis();

043    HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);

044    Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

045    // Get hold of the response entity (-> the data):

046    HttpEntity entity = response.getEntity();

047    if (entity != null) {

048     // Read the content stream

049     InputStream instream = entity.getContent();

050     Header contentEncoding = response.getFirstHeader("Content-Encoding");

051     if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {

052      instream = new GZIPInputStream(instream);

053     }

054     // convert content stream to a String

055     String resultString= convertStreamToString(instream);

056     instream.close();

057     resultString = resultString.substring(1,resultString.length()-1); // remove wrapping "[" and "]"

058     // Transform the String into a JSONObject

059     JSONObject jsonObjRecv = new JSONObject(resultString);

060     // Raw DEBUG output of our received JSON object:

061     Log.i(TAG,"<jsonobject>\n"+jsonObjRecv.toString()+"\n</jsonobject>");

062     return jsonObjRecv;

063    }

064   }

065   catch (Exception e)

066   {

067    // More about HTTP exception handling in another tutorial.

068    // For now we just print the stack trace.

069    e.printStackTrace();

070   }

071   return null;

072  }

073  private static String convertStreamToString(InputStream is) {

074   /*

075    * To convert the InputStream to String we use the BufferedReader.readLine()

076    * method. We iterate until the BufferedReader return null which means

077    * there's no more data to read. Each line will appended to a StringBuilder

078    * and returned as String.

079    *

080    * (c) public domain: http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple-restful-client-at-android/

081    */

082   BufferedReader reader = new BufferedReader(new InputStreamReader(is));

083   StringBuilder sb = new StringBuilder();

084   String line = null;

085   try {

086    while ((line = reader.readLine()) != null) {

087     sb.append(line + "\n");

088    }

089   } catch (IOException e) {

090    e.printStackTrace();

091   } finally {

092    try {

093     is.close();

094    } catch (IOException e) {

095     e.printStackTrace();

096    }

097   }

098   return sb.toString();

099  }

}


4) Add the following code to your MainActivity.java


package com.devstream.http;

17 import org.json.JSONException;

18 import org.json.JSONObject;

19 import android.app.Activity;

20 import android.os.Bundle;

21 import android.util.Log;

22 public class MainActivity extends Activity {

23  private static final String TAG = "MainActivity";

24  private static final String URL = "http://www.yourdomain.com:80";

25  @Override

26  public void onCreate(Bundle savedInstanceState) {

27   super.onCreate(savedInstanceState);

28   setContentView(R.layout.main);

29   // JSON object to hold the information, which is sent to the server

30   JSONObject jsonObjSend = new JSONObject();

31   try {

32    // Add key/value pairs

33    jsonObjSend.put("key_1", "value_1");

34    jsonObjSend.put("key_2", "value_2");

35    // Add a nested JSONObject (e.g. for header information)

36    JSONObject header = new JSONObject();

37    header.put("deviceType","Android"); // Device type

38    header.put("deviceVersion","2.0"); // Device OS version

39    header.put("language", "es-es"); // Language of the Android client

40    jsonObjSend.put("header", header);

41    

42    // Output the JSON object we're sending to Logcat:

43    Log.i(TAG, jsonObjSend.toString(2));

44   } catch (JSONException e) {

45    e.printStackTrace();

46   }

47   // Send the HttpPostRequest and receive a JSONObject in return

48   JSONObject jsonObjRecv = HttpClient.SendHttpPost(URL, jsonObjSend);

49   /*

50    *  From here on do whatever you want with your JSONObject, e.g.

51    *  1) Get the value for a key: jsonObjRecv.get("key");

52    *  2) Get a nested JSONObject: jsonObjRecv.getJSONObject("key")

53    *  3) Get a nested JSONArray: jsonObjRecv.getJSONArray("key")

54    */

55  }

}





Saturday, December 3, 2011

How to Create Custom UI Elements in Your Android Application

In this article I will show you how to create a new UI element using the predefined Android elements we have available. After that, we will add new attributes to this element so we can improve our widget functionality.
Introduction
Sometimes, when we are coding our Android application UI, we realize that we can create more complex elements, nor just a image here, or some text there. What if we could create a single UI element that is an image and has text as a footnote? And if we go a bit further... what if we add the following functionality to the image, when you long click on it, a short message appears on the screen. And all this in a single UI element. Lets start!
Creating a new UI element
We need to code a little bit to achieve our goal, but at the end, it will have been worth the effort, because you can re-use this new element in your application.
First of all, let's create the base layout of our new element. In this example I'm going to create a complex UI element with an ImageView and a TextView as a footnote. So this is the code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/image"
android:layout_centerHorizontal="true"></ImageView>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/footnote"
android:text="None Footnote" android:gravity="center"
android:layout_below="@+id/image"></TextView>
</RelativeLayout>
I have created a bright_image.xml file inside the /layout folder. As you can see in the xml, it's just an image above some text. That simple.

Introducing Google Music
Google.com/Music
Upload 20,000 of Your Songs, Free. Listen Anywhere, Even Offline.
Ads by Google
Now, let's work on the Android code part. I have created a class called BrightHubImage.java that extends LinearLayout and it has the following constructors:
public class BrightHubImage extends LinearLayout{
public BrightHubImage(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.bright_image,this);
}

public BrightHubImage(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.bright_image,this);
}
}
We have to take care of both constructors, the first one is used when we haven't added new attributes to our element, and the second one is used when we add new attributes to our UI element. We are going to focus on the second constructor. You will see why in the following paragraphs.
What is that inflate thing? It's like saying “We are going to get the content of the bright_image.xml layout file, and we are going to add it to this layout class” so we have a Layout class called BrightHubImage with the layout brigh_image I have created in the very first moment.

Now, how do we use this new element we have created? Here it is: this is the main.xml file,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bright="http://schemas.android.com/apk/res/com.bright.hub"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.bright.hub.custom.BrightHubImage
android:layout_width="fill_parent" android:layout_height="fill_parent">
</com.bright.hub.custom.BrightHubImage>
</LinearLayout>
As you can see, here there is a couple of new things:
xmlns:bright="http://schemas.android.com/apk/res/com.bright.hub"
and the
<com.bright.hub.custom.BrightHubImage
With the xmlns line, we are adding the com.bright.hub package to our namespaces so Android can recognise UI elements we create in our packages. (you can see your package name in the Android Manifest).
The com.bright.hub.custom.BrightHubImage is our element. We have to place the full path of our custom layout so Android parser finds it.
So, we have here the basics to create a custom UI element! Now let's continue with this example adding new attributes to our BrightHubImage element.
Adding new attributes
We are going to add 3 new attributes to our BrightHubImage element:
A string attribute with the text that is going to be placed in the footnote: footnote_string
A string attribute with the text to appear in a toast when a long click is done: message_string
And a drawable with the image that is going to be shown: brigh_image
For the purpose of adding new attributes, we have to create a file in the /values folder called “attrs.xml”. This file will contain the following code, according to the attribute described above:
<resources>
<declare-styleable name="BrighHubImage">
<attr name="message_string" format="string" />
<attr name="footnote_string" format="string" />
<attr name="bright_image" format="integer" />
</declare-styleable>
</resources>
We have declared the bright_image as an integer because it is going to be the id reference in the R.java class. Remember that all the resources are identified as integer.
Now, in the main.xml code, we are going to put these attributes in our custom element:
<com.bright.hub.custom.BrightHubImage
android:layout_width="fill_parent" android:layout_height="fill_parent"
bright:message_string="This will be shown in a toast"
bright:footnote_string="This is the footNote"
bright:bright_image="@drawable/bright_hub_image">
</com.bright.hub.custom.BrightHubImage>
Remember that we used the xmlns:bright namespaces at the top of the xml file, now we are referencing those namespaces with “bright:message_string”. These are our attributes, not Android ones.
The attributes are defined, but what do we do with them? Where do we use them? Let's go back to the BrightHubImage.java file in our Android code: The following lines are going to be the most important parts of the code to make this work, don't worry because the full source code is available and you can check how it's done more clearly.
One of the functionalities we wanted was:
When you do a long click on the image, a toast message should appear.
So, in your LinearLayout we implement the onLongClickListener:
public class BrightHubImage extends LinearLayout implements OnLongClickListener
This will force us to create the method..
@Override
public boolean onLongClick(View arg0) {
return false;
}
I'm going to add the following objects, we will need them in the code:
private CharSequence toastMessage;
private CharSequence footNoteMessage;
private Drawable image;
private Context cxt;
private TextView footNoteText;
private ImageView brightImage;
We are going to focus in the attributes constructor (the one with the AttributeSet attrs input), here we will add the following code lines:
this.setOnLongClickListener(this);
cxt = context;
Setting ourselves as listeners of the incoming long-click events and the context from our application.
footNoteText = (TextView) findViewById(R.id.footnote);
brightImage = (ImageView) findViewById(R.id.image);
initializing the elements inside the BrightHubImage. We need them because we are going to change their inner attributes.
TypedArray styleAttributes = context.obtainStyledAttributes(attrs, R.styleable.BrighHubImage);
toastMessage = styleAttributes.getString(R.styleable.BrighHubImage_message_string);
image = styleAttributes.getDrawable(R.styleable.BrighHubImage_bright_image);
footNoteMessage = styleAttributes.getString(R.styleable.BrighHubImage_footnote_string);
And with these lines of code, we obtain the attributes from our custom element and store them in the object I created above.
Now, in the toastMessage object we have the string from the:
bright:message_string="This will be shown in a toast"
in the image object we have the drawable from
bright:bright_image="@drawable/bright_hub_image" (the bright_hub_image was added by me to the drawables image, you can use the image you want)
and in the footNoteMessage object we have the string from
bright:footnote_string="This is the footNote"
Well, let's place these values in the right elements:
The image object will be the background of our inner “brightImage” ImageView and the footNotemessage will be the text of our inner “footNoteText” TextView from our bright_image.xml element.
footNoteText.setText(footNoteMessage);
brightImage.setBackgroundDrawable(image);
The last line of code will be placed inside the OnLongClick:
Toast.makeText(cxt, toastMessage, Toast.LENGTH_LONG).show();
we show a toast with the text form the bright:message_string attribute when we do a long click.

Getting Started with the NDK in android

Most Android developers start their learning from pure Java, Android SDK based apps. While everyone is aware that there is the NDK (the Native Development Kit), they don’t face the need to use it. Since NDK is distributed separately from the SDK, including documentations and samples, you are not likely to get familiar with NDK before you actually try it as a solution to one of your development challenges.
Because of that, many people think of NDK as of “black magic” of Android development. Many developers who are not familiar with NDK think it is 1) complex to understand and use, and at the same time a lot of developers will think it is a sort of a 2) silver bullet that can solve any problem that can’t be solved with SDK.
Well, both opinions are rather wrong, as I hope to show further in this post. Although it does have a maintenance cost and does add technical complexity to your project, NDK is not difficult to install or use in your project. And, while there are cases where NDK will be really helpful for your app, NDK has a rather limited API that’s mostly focused on several performance-critical areas, such as:
  • OpenGL, including support for some newer versions that the (Java) SDK supports
  • Math (some, but not all, calculation-intensive algorithms might benefit from being done on the native layer)
  • 2D graphics – pixelbuffer support (only starting with 2.2)
  • libc – it’s there for compatibility and perhaps to allow you to port existing native code
In this tutorial we will take our basic Android development environment that we created in one of the previous articles and add NDK support to it. We will also create a basic skeleton project that uses NDK that you can use as the foundation for your NDK-powered apps.

The downloads that are necessary for the initial configuration of the environment might take some time (around 30 minutes total), so be prepared.
Ready? Let’s go!

Step 1: Installing C/C++ Support on Eclipse

This is where we stand right after we’re done with the previous tutorial. In short, we have a basic Eclipse installation, plus Android SDK and ADT that brings Android support to Eclipse:

(we’re not going to use our old project, foobar, so you can just close it.)
While NDK supports both C and C++, C++ support is rather limited. For example, exceptions are not supported and there are some known bugs in static constructor/destructor invocations. Also, most of the time when you will use NDK as it was intended – for moving the most performance-critical parts of code to the native layer – you are not likely to need much OOP abstraction and other design goodies. What I’m trying to say is that NDK code is more likely to be written in C rather than C++.
Anyway, our Eclipse installation does not support either C or C++ right now. We don’t really need the full support, including building etc. But we would like to have syntax coloring and basic syntax checking. Thus we have to add some Eclipse features via the update mechanism, almost like when we added Android support.
Right now go to HelpInstall New Software menu item. Choose Galileo as the update site (“Work with”). Let the item tree load, and check Eclipse C/C++ Development Tools in the Programming Languages branch:

Then press Next. Say yes to everything, accept the licenses and let Eclipse finish the update. Once it is done, you will see the prompt to restart Eclipse:

Say Yes and wait for Eclipse to restart. You have C/C++ support in your Eclipse IDE now.

Step 2: Installing Cygwin

Android is Linux based, and thus it is no surprise that when you build native code for it, you need some Unix tools. On Windows, NDK supports Cygwin 1.7.x and above. If you don’t know what Cygwin is, it’s just a set of software that emulates Unix environment on Windows which is necessary in some cases, including ours.
In order to get Cygwin, go to cygwin.com:

There’s a small grey box on the right side of the page that says “Install or update Cygwin now!”. Click it, and Cygwin’s setup.exe will download and run:

Choose Install from Internet, then click Next, then choose the installation directory (be sure to choose a directory path that contains no spaces in it) – and by the way, the whole thing is going to require up to few gigs of space. Then, choose the local package directory – just use some temporary directory, you’re not likely to need it afterwards.
At this point Cygwin will connect to its central site and download the list of mirror sites. Choosing a mirror site that looks geographically close to you may save you some download time:

After you choose the mirror and click Next, Cygwin will download and present to you the list of available packages:

By default, only the base packages are installed. We, however, need the development packages. Rather than picking the packages we think we need, and then struggling with missing dependencies and other typical Unix nightmares, I suggest that we install the entire Devel branch. Click (once) on the word “Default” next to the root Devel node and wait few seconds while the setup hangs. When it is back, you will see that “Default” changes to “Install” for the Devel node, just like on the screenshot above.
Now click next and let Cygwin download the packages and install the environment:

This might take a while, so you can go have a coffee now. Or lunch if your internet connection is slow.
When you are back, you will hopefully see the final setup screen:

Allow it to create an icon on the desktop. Here’s what you will see on your desktop after you click Finish – an icon that launches the Cygwin console:

Click it once, let the Cygwin console start up and initialize:

To check that we have the tool that is important for Android NDK, type make -v in the console:

You should see the same response that tells us that GNU Make is present in our Unix environment that is emulated by Cygwin. Cool!
Note: From my own experience, Cygwin installation is often unstable and can be error-prone. If you have a specific issue, let’s discuss it in comments and I’ll try to help. Don’t go further if something is wrong at this step, since correctly installed Cygwin is a requirement for working with NDK on Windows.

Step 3: Installing the Android NDK

Our next step is to download Android NDK itself and place it on our filesystem. You can get NDK from the official Android site:

Download the NDK zip for Windows and extract it somewhere, but again, be sure that there are no spaces in the path. In my case, I extracted it to C:\, so the path is C:\android-ndk-r4.
Now we have the environment ready for our first NDK app!

Step 4: Making a Basic NDK App

The general idea of using NDK in apps is to put your native pieces of code into libraries that you can then consume from the Java code. Thus, you always start with a standard (Java) app and then add NDK pieces to it. Thus, let’s create a basic app in Eclipse as we did previously, using the New Android Project wizard:

There is, however, an important thing to check, and believe it or not, it is spaces in the path again. My Eclipse workspace is located in a directory that has spaces in it, so I had to uncheck the Use default location checkbox and manually choose a path that does not have spaces, as you can see in the screenshot above. In general, it’s better to keep Eclipse workspaces located in space-free paths.
Otherwise, there are no NDK-specific things you should do when creating the app. I allowed the wizard to create a dummy activity called NdkFooActivity that we will use later on.
After the app has been created by the wizard…

..Make a folder called jni in the root of the project (right-click the project node, NewFolder). Create a file called Android.mk (NewFile) within that folder with the following contents:
1
2
3
4
5
6
7
8
9
LOCAL_PATH := $(call my-dir)
  
include $(CLEAR_VARS)
  
# Here we give our module name and source file(s)
LOCAL_MODULE    := ndkfoo
LOCAL_SRC_FILES := ndkfoo.c
  
include $(BUILD_SHARED_LIBRARY)
Except the module name (ndkfoo), treat everything in that file as magic. You can go deeper into Unix Makefiles later if you want.
The Android.mk file is important for the NDK build process to recognize your NDK modules. In our case we named our module ndkfoo and told the build tool that it consists of one source file named ndkfoo.c. Let’s create it in the same jni folder:

Here’s the content for you to copy and paste:
1
2
3
4
5
6
#include <string.h>
#include <jni.h>
  
jstring Java_com_mindtherobot_samples_ndkfoo_NdkFooActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) {
  return (*env)->NewStringUTF(env, "Hello from native code!");
}
Android actually uses the standard Java way to communicate with native code called JNI (Java Native Interface). It defines conventions and mechanisms that Java code and C/C++ code use to interact. You can read more about JNI in the official Sun docs, but for now you might notice that the name of the C function is not just random – it matches the Java class name. In addition, what the function does is it uses the JNIEnv object to create a Java string from a literal, and returns the string to the caller.
You will need to learn more JNI if you’re going to use NDK a lot. By the way, it is also possible to call Java methods from native code, create custom objects and so on.
Now, in order to create a binary library from the C source that we wrote, we will use a combination of Cygwin and Android NDK tools. Launch the Cygwin console and use the cd command to go directly to the folder where your project is. Notice that Windows drives are mapped under /cygdrive within the emulated Unix environment you work with in the Console console. In my case, the command line is: cd /cygdrive/c/projects/ndkfoo
Then, issue a command that will invoke the NDK build tool. In my case, since NDK is installed in C:\ it looks like this: /cygdrive/c/android-ndk-r4/ndk-build
Here’s a screenshot for you to check:

As you can notice, a successful run of the ndk-build tool will create an .so file in a new folder called libs that will be created in your project root (if it’s not there yet). The .so file is the binary library that will be included into the application .apk package and will be available for the Java code of the app to link to. You just need to hit F5 in Eclipse after selecting the project root to update the Eclipse project with the changes you did in the Cygwin console.
You have to repeat the ndk-build command every time you modify the C/C++ source of your NDK code. Eclipse ADT does not support NDK so you need to do it from the Cygwin console. Don’t forget to refresh Eclipse every time!
Anyway, the NDK part is actually finished. What we need to do now is to change the Java code of the NdkFooActivity class to use the NDK code:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.mindtherobot.samples.ndkfoo;
  
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
  
public class NdkFooActivity extends Activity {
  
  // load the library - name matches jni/Android.mk
  static {
    System.loadLibrary("ndkfoo");
  }
  
  // declare the native code function - must match ndkfoo.c
  private native String invokeNativeFunction();
  
  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
  
        // this is where we call the native code
        String hello = invokeNativeFunction();
  
        new AlertDialog.Builder(this).setMessage(hello).show();
    }
}
As you can probably guess, this code will invoke the NDK method that will return the string, that will be displayed as an alert on the screen. Here’s the result of running the ndkfoo app on the emulator:

Congratulations! Your first NDK app is running fine.

Step 5: Be Wise and Careful

As it was mentioned above, and as you probably understand better now, NDK is not a monster and is quite easy to use in your app. However, every time you want to use NDK, please think twice and perform investigation to see how much you could actually gain from using it. Mixing C/C++ with Java is generally a bad idea from the code quality point of view, and Dalvik VM is getting faster and faster so you can avoid the NDK in many cases.
Also, be sure to read the NDK docs and learn JNI thoroughly to make sure you use NDK correctly and safely.
Otherwise, happy NDK’ing. :)