Android Tutorial - Development : Xml

 Using xml resource

package com.commonsware.android.files;

import android.app.Activity;
import android.os.Bundle;
import android.app.ListActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class StaticFileDemo extends ListActivity {
  TextView selection;
  ArrayList<String> items=new ArrayList<String>();
  
  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);
    selection=(TextView)findViewById(R.id.selection);
    
    try {
      InputStream in=getResources().openRawResource(R.raw.words);
      DocumentBuilder builder=DocumentBuilderFactory
                                .newInstance()
                                .newDocumentBuilder();
      Document doc=builder.parse(in, null);
      NodeList words=doc.getElementsByTagName("word");
      
      for (int i=0;i<words.getLength();i++) {
        items.add(((Element)words.item(i)).getAttribute("value"));
      }
      
      in.close();
    }
    catch (Throwable t) {
      Toast
        .makeText(this, "Exception: "+t.toString(), 2000)
        .show();
    }
    
    setListAdapter(new ArrayAdapter<String>(this,
                                  android.R.layout.simple_list_item_1,
                                  items));
  }
  
  public void onListItemClick(ListView parent, View v, int position,
                  long id) {
    selection.setText(items.get(position).toString());
  }
}

//res\raw\words.xml
<words>
  <word value="lorem" />
  <word value="ipsum" />
  <word value="dolor" />
  <word value="sit" />
  <word value="amet" />
  <word value="consectetuer" />
  <word value="adipiscing" />
  <word value="elit" />
  <word value="morbi" />
  <word value="vel" />
  <word value="ligula" />
  <word value="vitae" />
  <word value="arcu" />
  <word value="aliquet" />
  <word value="mollis" />
  <word value="etiam" />
  <word value="vel" />
  <word value="erat" />
  <word value="placerat" />
  <word value="ante" />
  <word value="porttitor" />
  <word value="sodales" />
  <word value="pellentesque" />
  <word value="augue" />
  <word value="purus" />
</words>



//res\layout\main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" >
  <TextView
    android:id="@+id/selection"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
  />
  <ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:drawSelectorOnTop="false"
  />
</LinearLayout>

XML Resource Demo

package com.commonsware.android.resources;

import android.app.Activity;
import android.os.Bundle;
import android.app.ListActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.InputStream;
import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

public class XMLResourceDemo extends ListActivity {
  TextView selection;
  ArrayList<String> items=new ArrayList<String>();
  
  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);
    selection=(TextView)findViewById(R.id.selection);
    
    try {
      XmlPullParser xpp=getResources().getXml(R.xml.words);
      
      while (xpp.getEventType()!=XmlPullParser.END_DOCUMENT) {
        if (xpp.getEventType()==XmlPullParser.START_TAG) {
          if (xpp.getName().equals("word")) {
            items.add(xpp.getAttributeValue(0));
          }
        }
        
        xpp.next();
      }
    }
    catch (Throwable t) {
      Toast
        .makeText(this, "Request failed: "+t.toString(), 4000)
        .show();
    }
    
    setListAdapter(new ArrayAdapter<String>(this,
                            android.R.layout.simple_list_item_1,
                            items));
  }
  
  public void onListItemClick(ListView parent, View v, int position,
                  long id) {
    selection.setText(items.get(position).toString());
  }
}


//res\xml\words.xml
<words>
  <word value="lorem" />
  <word value="ipsum" />
  <word value="dolor" />
  <word value="sit" />
  <word value="amet" />
  <word value="consectetuer" />
  <word value="adipiscing" />
  <word value="elit" />
  <word value="morbi" />
  <word value="vel" />
  <word value="ligula" />
  <word value="vitae" />
  <word value="arcu" />
  <word value="aliquet" />
  <word value="mollis" />
  <word value="etiam" />
  <word value="vel" />
  <word value="erat" />
  <word value="placerat" />
  <word value="ante" />
  <word value="porttitor" />
  <word value="sodales" />
  <word value="pellentesque" />
  <word value="augue" />
  <word value="purus" />
</words>



//res\values\strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">XMLResourceDemo</string>
</resources>



//res\layout\main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" >
  <TextView
    android:id="@+id/selection"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
  />
  <ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:drawSelectorOnTop="false"
  />
</LinearLayout>

Load style from styles.xml

     

package app.test;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Spannable;
import android.text.style.BackgroundColorSpan;
import android.text.style.StyleSpan;
import android.text.util.Linkify;
import android.widget.EditText;
import android.widget.TextView;

public class Test extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        TextView tv =(TextView)this.findViewById(R.id.tv);
        tv.setAutoLinkMask(Linkify.ALL);
        tv.setText("asdf");

        TextView tv3 =(TextView)this.findViewById(R.id.tv3);
        tv3.setText("asdf",TextView.BufferType.SPANNABLE);
        Spannable spn = (Spannable) tv3.getText();
        spn.setSpan(new BackgroundColorSpan(Color.RED), 0, 7,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        spn.setSpan(new StyleSpan(android.graphics.Typeface.BOLD_ITALIC),0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        EditText et =(EditText)this.findViewById(R.id.et);
        et.setText("asdf");
        Spannable spn2 = (Spannable) et.getText();
        spn2.setSpan(new BackgroundColorSpan(Color.RED), 0, 7,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        spn2.setSpan(new StyleSpan(android.graphics.Typeface.BOLD_ITALIC),0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

//main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"  android:layout_height="fill_parent"
    >
<TextView   
    android:layout_width="fill_parent"  android:layout_height="wrap_content" 
    android:autoLink="web|map"
    android:text="Please visit www.androidbook.com for more help on using Android."
    android:minLines="5"
    android:typeface="serif"
    />
<TextView android:id="@+id/tv"
    android:layout_width="fill_parent"  android:layout_height="wrap_content" 
    />
<TextView android:id="@+id/tvStyled"
    android:layout_width="fill_parent"  android:layout_height="wrap_content"
    android:text="@+string/styledText"
    />
<TextView android:id="@+id/tv3"
    android:layout_width="fill_parent"  android:layout_height="wrap_content" 
    />
<EditText android:id="@+id/et"
    android:layout_width="fill_parent"  android:layout_height="wrap_content" 
    android:inputType="text|textAutoCorrect|textAutoComplete|textMultiLine"
    />
<Button android:id="@+id/button1"
  android:layout_width="wrap_content"  android:layout_height="wrap_content"
  android:text="@string/tv"
  />
<TextView android:id="@+id/errors"
    style="@style/ErrorText"
    android:text="There's trouble down at the mill."
    />
<TextView android:id="@+id/danger"
    style="@style/ErrorText.Danger"
    android:text="SERIOUSLY! THERE'S TROUBLE DOWN AT THE MILL!!"
    />
</LinearLayout>

//strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="hello">Hello World, ActivityMenu</string>
  <string name="app_name">HelloMenu</string>
  <string name="button1">button1</string>
  <string name="button2">button2</string>
    <string name="tv">I am a TextView</string>
    <string name="et">I am an EditText</string>
    <string name="actv">AutoComplete:</string>
    <string name="mactv">MultiAutoComplete:</string>  
</resources>

//styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="ErrorText">
        <item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">#FF0000</item>
        <item name="android:typeface">monospace</item>
    </style>
    <style name="ErrorText.Danger"  >
        <item name="android:textStyle">bold</item>
    </style>
</resources>

   

Define PreferenceScreen in xml file

     


package app.test;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class Test extends PreferenceActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.main);
    }
}

//xml/main.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/xml/chkbox.xml -->
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
  android:key="flight_columns_pref" 
  android:title="Flight Search Preferences"
  android:summary="Set Columns for Search Results">
  <CheckBoxPreference
      android:key="show_airline_column_pref"
    android:title="Airline"
    android:summary="Show Airline column" />
  <CheckBoxPreference
      android:key="show_departure_column_pref"
    android:title="Departure"
    android:summary="Show Departure column" />
  <CheckBoxPreference
      android:key="show_arrival_column_pref"
    android:title="Arrival"
    android:summary="Show Arrival column" />
  <CheckBoxPreference
      android:key="show_total_travel_time_column_pref"
    android:title="Total Travel Time" 
    android:summary="Show Total Travel Time column" />
  <CheckBoxPreference
      android:key="show_price_column_pref"
    android:title="Price" 
    android:summary="Show Price column" />
</PreferenceScreen>

Using XML Parser

     
package app.test;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class Test extends Activity {
  XMLUser aUser;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    String xml = "<?xml version=\"1.0\"?>\n" + "<user>\n"
        + "<user-id>1</user-id>\n" + "<username>a</username>\n"
        + "<firstname>B</firstname>\n"
        + "<lastname>C</lastname>\n" + "</user>\n";
    SAXParserFactory aSAXParserFactory = SAXParserFactory.newInstance();
    try {
      SAXParser aSAXParser = aSAXParserFactory.newSAXParser();
      XMLReader anXMLReader = aSAXParser.getXMLReader();
      UserXMLHandler aUserXMLHandler = new UserXMLHandler();
      anXMLReader.setContentHandler(aUserXMLHandler);
      anXMLReader.parse(new InputSource(new ByteArrayInputStream(xml.getBytes())));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  class UserXMLHandler extends DefaultHandler {

    static final int NONE = 0;
    static final int ID = 1;
    static final int FIRSTNAME = 2;
    static final int LASTNAME = 3;

    int state = NONE;

    static final String ID_ELEMENT = "user-id";
    static final String FIRSTNAME_ELEMENT = "firstname";
    static final String LASTNAME_ELEMENT = "lsatname";

    @Override
    public void startDocument() throws SAXException {
      Log.v("SimpleXMLParser", "startDocument");
      aUser = new XMLUser();
    }

    @Override
    public void endDocument() throws SAXException {
      Log.v("SimpleXMLParser", "endDocument");
      Log.v("SimpleXMLParser", "User Info: " + aUser.user_id + " "
          + aUser.firstname + " " + aUser.lastname);
    }

    @Override
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
      Log.v("SimpleXMLParser", "startElement");
      if (localName.equalsIgnoreCase(ID_ELEMENT)) {
        state = ID;
      } else if (localName.equalsIgnoreCase(FIRSTNAME_ELEMENT)) {
        state = FIRSTNAME;
      } else if (localName.equalsIgnoreCase(LASTNAME_ELEMENT)) {
        state = LASTNAME;
      } else {
        state = NONE;
      }
    }

    @Override
    public void endElement(String uri, String localName, String qName)
        throws SAXException {
      Log.v("SimpleXMLParser", "endElement");

    }

    @Override
    public void characters(char[] ch, int start, int length)
        throws SAXException {
      String stringChars = new String(ch, start, length);
      if (state == ID) {
        aUser.user_id += stringChars.trim();
        Log.v("SimpleXMLParser", "user_id:" + aUser.user_id);
      } else if (state == FIRSTNAME) {
        aUser.firstname += stringChars.trim();
        Log.v("SimpleXMLParser", "firstname:" + aUser.firstname);
      } else if (state == LASTNAME) {
        aUser.lastname += stringChars.trim();
        Log.v("SimpleXMLParser", "lastname:" + aUser.lastname);
      }
    }
  }
}

class XMLUser {
  String user_id;
  String firstname;
  String lastname;

  public XMLUser() {
    user_id = "";
    firstname = "";
    lastname = "";
  }
}

XML-defined adapters can be used to easily create adapters in your own application or to pass adapters to other processes.

package com.example.android.xmladapters;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * <p>This class can be used to load {@link android.widget.Adapter adapters} defined in
 * XML resources. XML-defined adapters can be used to easily create adapters in your
 * own application or to pass adapters to other processes.</p>
 * 
 * <h2>Types of adapters</h2>
 * <p>Adapters defined using XML resources can only be one of the following supported
 * types. Arbitrary adapters are not supported to guarantee the safety of the loaded
 * code when adapters are loaded across packages.</p>
 * <ul>
 *  <li><a href="#xml-cursor-adapter">Cursor adapter</a>: a cursor adapter can be used
 *  to display the content of a cursor, most often coming from a content provider</li>
 * </ul>
 * <p>The complete XML format definition of each adapter type is available below.</p>
 * 
 * <a name="xml-cursor-adapter"></a>
 * <h2>Cursor adapter</h2>
 * <p>A cursor adapter XML definition starts with the
 * <a href="#xml-cursor-adapter-tag"><code>&lt;cursor-adapter /&gt;</code></a>
 * tag and may contain one or more instances of the following tags:</p>
 * <ul>
 *  <li><a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code></a></li>
 *  <li><a href="#xml-cursor-adapter-bind-tag"><code>&lt;bind /&gt;</code></a></li>
 * </ul>
 * 
 * <a name="xml-cursor-adapter-tag"></a>
 * <h3>&lt;cursor-adapter /&gt;</h3>
 * <p>The <code>&lt;cursor-adapter /&gt;</code> element defines the beginning of the
 * document and supports the following attributes:</p>
 * <ul>
 *  <li><code>android:layout</code>: Reference to the XML layout to be inflated for
 *  each item of the adapter. This attribute is mandatory.</li>
 *  <li><code>android:selection</code>: Selection expression, used when the
 *  <code>android:uri</code> attribute is defined or when the adapter is loaded with
 *  {@link Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
 *  This attribute is optional.</li>
 *  <li><code>android:sortOrder</code>: Sort expression, used when the
 *  <code>android:uri</code> attribute is defined or when the adapter is loaded with
 *  {@link Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
 *  This attribute is optional.</li>
 *  <li><code>android:uri</code>: URI of the content provider to query to retrieve a cursor.
 *  Specifying this attribute is equivalent to calling
 *  {@link Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
 *  If you call this method, the value of the XML attribute is ignored. This attribute is
 *  optional.</li>
 * </ul>
 * <p>In addition, you can specify one or more instances of
 * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code></a> and
 * <a href="#xml-cursor-adapter-bind-tag"><code>&lt;bind /&gt;</code></a> tags as children
 * of <code>&lt;cursor-adapter /&gt;</code>.</p>
 * 
 * <a name="xml-cursor-adapter-select-tag"></a>
 * <h3>&lt;select /&gt;</h3>
 * <p>The <code>&lt;select /&gt;</code> tag is used to select columns from the cursor
 * when doing the query. This can be very useful when using transformations in the
 * <code>&lt;bind /&gt;</code> elements. It can also be very useful if you are providing
 * your own <a href="#xml-cursor-adapter-bind-data-types">binder</a> or
 * <a href="#xml-cursor-adapter-bind-data-types">transformation</a> classes.
 * <code>&lt;select /&gt;</code> elements are ignored if you supply the cursor yourself.</p>
 * <p>The <code>&lt;select /&gt;</code> supports the following attributes:</p>
 * <ul>
 *  <li><code>android:column</code>: Name of the column to select in the cursor during the
 *  query operation</li>
 * </ul>
 * <p><strong>Note:</strong> The column named <code>_id</code> is always implicitly
 * selected.</p>
 * 
 * <a name="xml-cursor-adapter-bind-tag"></a>
 * <h3>&lt;bind /&gt;</h3>
 * <p>The <code>&lt;bind /&gt;</code> tag is used to bind a column from the cursor to
 * a {@link android.view.View}. A column bound using this tag is automatically selected
 * during the query and a matching
 * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code> tag is therefore
 * not required.</p>
 * 
 * <p>Each binding is declared as a one to one matching but
 * custom binder classes or special
 * <a href="#xml-cursor-adapter-bind-data-transformation">data transformations</a> can
 * allow you to bind several columns to a single view. In this case you must use the
 * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code> tag to make
 * sure any required column is part of the query.</p>
 * 
 * <p>The <code>&lt;bind /&gt;</code> tag supports the following attributes:</p>
 * <ul>
 *  <li><code>android:from</code>: The name of the column to bind from.
 *  This attribute is mandatory. Note that <code>@</code> which are not used to reference resources
 *  should be backslash protected as in <code>\@</code>.</li>
 *  <li><code>android:to</code>: The id of the view to bind to. This attribute is mandatory.</li>
 *  <li><code>android:as</code>: The <a href="#xml-cursor-adapter-bind-data-types">data type</a>
 *  of the binding. This attribute is mandatory.</li>
 * </ul>
 * 
 * <p>In addition, a <code>&lt;bind /&gt;</code> can contain zero or more instances of
 * <a href="#xml-cursor-adapter-bind-data-transformation">data transformations</a> children
 * tags.</p>
 *
 * <a name="xml-cursor-adapter-bind-data-types"></a>
 * <h4>Binding data types</h4>
 * <p>For a binding to occur the data type of the bound column/view pair must be specified.
 * The following data types are currently supported:</p>
 * <ul>
 *  <li><code>string</code>: The content of the column is interpreted as a string and must be
 *  bound to a {@link android.widget.TextView}</li>
 *  <li><code>image</code>: The content of the column is interpreted as a blob describing an
 *  image and must be bound to an {@link android.widget.ImageView}</li>
 *  <li><code>image-uri</code>: The content of the column is interpreted as a URI to an image
 *  and must be bound to an {@link android.widget.ImageView}</li>
 *  <li><code>drawable</code>: The content of the column is interpreted as a resource id to a
 *  drawable and must be bound to an {@link android.widget.ImageView}</li>
 *  <li><code>tag</code>: The content of the column is interpreted as a string and will be set as
 *  the tag (using {@link View#setTag(Object)} of the associated View. This can be used to
 *  associate meta-data to your view, that can be used for instance by a listener.</li>
 *  <li>A fully qualified class name: The name of a class corresponding to an implementation of
 *  {@link Adapters.CursorBinder}. Cursor binders can be used to provide
 *  bindings not supported by default. Custom binders cannot be used with
 *  {@link android.content.Context#isRestricted() restricted contexts}, for instance in an
 *  application widget</li>
 * </ul>
 * 
 * <a name="xml-cursor-adapter-bind-transformation"></a>
 * <h4>Binding transformations</h4>
 * <p>When defining a data binding you can specify an optional transformation by using one
 * of the following tags as a child of a <code>&lt;bind /&gt;</code> elements:</p>
 * <ul>
 *  <li><code>&lt;map /&gt;</code>: Maps a constant string to a string or a resource. Use
 *  one instance of this tag per value you want to map</li>
 *  <li><code>&lt;transform /&gt;</code>: Transforms a column's value using an expression
 *  or an instance of {@link Adapters.CursorTransformation}</li>
 * </ul>
 * <p>While several <code>&lt;map /&gt;</code> tags can be used at the same time, you cannot
 * mix <code>&lt;map /&gt;</code> and <code>&lt;transform /&gt;</code> tags. If several
 * <code>&lt;transform /&gt;</code> tags are specified, only the last one is retained.</p>
 * 
 * <a name="xml-cursor-adapter-bind-transformation-map" />
 * <p><strong>&lt;map /&gt;</strong></p>
 * <p>A map element simply specifies a value to match from and a value to match to. When
 * a column's value equals the value to match from, it is replaced with the value to match
 * to. The following attributes are supported:</p>
 * <ul>
 *  <li><code>android:fromValue</code>: The value to match from. This attribute is mandatory</li>
 *  <li><code>android:toValue</code>: The value to match to. This value can be either a string
 *  or a resource identifier. This value is interpreted as a resource identifier when the
 *  data binding is of type <code>drawable</code>. This attribute is mandatory</li>
 * </ul>
 * 
 * <a name="xml-cursor-adapter-bind-transformation-transform"></a>
 * <p><strong>&lt;transform /&gt;</strong></p>
 * <p>A simple transform that occurs either by calling a specified class or by performing
 * simple text substitution. The following attributes are supported:</p>
 * <ul>
 *  <li><code>android:withExpression</code>: The transformation expression. The expression is
 *  a string containing column names surrounded with curly braces { and }. During the
 *  transformation each column name is replaced by its value. All columns must have been
 *  selected in the query. An example of expression is <code>"First name: {first_name},
 *  last name: {last_name}"</code>. This attribute is mandatory
 *  if <code>android:withClass</code> is not specified and ignored if <code>android:withClass</code>
 *  is specified</li>
 *  <li><code>android:withClass</code>: A fully qualified class name corresponding to an
 *  implementation of {@link Adapters.CursorTransformation}. Custom
 *  transformations cannot be used with
 *  {@link android.content.Context#isRestricted() restricted contexts}, for instance in
 *  an app widget This attribute is mandatory if <code>android:withExpression</code> is
 *  not specified</li>
 * </ul>
 * 
 * <h3>Example</h3>
 * <p>The following example defines a cursor adapter that queries all the contacts with
 * a phone number using the contacts content provider. Each contact is displayed with
 * its display name, its favorite status and its photo. To display photos, a custom data
 * binder is declared:</p>
 * 
 * <pre class="prettyprint">
 * &lt;cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
 *     android:uri="content://com.android.contacts/contacts"
 *     android:selection="has_phone_number=1"
 *     android:layout="@layout/contact_item"&gt;
 *
 *     &lt;bind android:from="display_name" android:to="@id/name" android:as="string" /&gt;
 *     &lt;bind android:from="starred" android:to="@id/star" android:as="drawable"&gt;
 *         &lt;map android:fromValue="0" android:toValue="@android:drawable/star_big_off" /&gt;
 *         &lt;map android:fromValue="1" android:toValue="@android:drawable/star_big_on" /&gt;
 *     &lt;/bind&gt;
 *     &lt;bind android:from="_id" android:to="@id/name"
 *              android:as="com.google.android.test.adapters.ContactPhotoBinder" /&gt;
 *
 * &lt;/cursor-adapter&gt;
 * </pre>
 * 
 * <h3>Related APIs</h3>
 * <ul>
 *  <li>{@link Adapters#loadAdapter(android.content.Context, int, Object[])}</li>
 *  <li>{@link Adapters#loadCursorAdapter(android.content.Context, int, android.database.Cursor, Object[])}</li>
 *  <li>{@link Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}</li>
 *  <li>{@link Adapters.CursorBinder}</li>
 *  <li>{@link Adapters.CursorTransformation}</li>
 *  <li>{@link android.widget.CursorAdapter}</li>
 * </ul>
 * 
 * @see android.widget.Adapter
 * @see android.content.ContentProvider
 * 
 * attr ref android.R.styleable#CursorAdapter_layout
 * attr ref android.R.styleable#CursorAdapter_selection
 * attr ref android.R.styleable#CursorAdapter_sortOrder
 * attr ref android.R.styleable#CursorAdapter_uri
 * attr ref android.R.styleable#CursorAdapter_BindItem_as
 * attr ref android.R.styleable#CursorAdapter_BindItem_from
 * attr ref android.R.styleable#CursorAdapter_BindItem_to
 * attr ref android.R.styleable#CursorAdapter_MapItem_fromValue
 * attr ref android.R.styleable#CursorAdapter_MapItem_toValue
 * attr ref android.R.styleable#CursorAdapter_SelectItem_column
 * attr ref android.R.styleable#CursorAdapter_TransformItem_withClass
 * attr ref android.R.styleable#CursorAdapter_TransformItem_withExpression
 */
@SuppressWarnings({"JavadocReference"})
public class Adapters {
    private static final String ADAPTER_CURSOR = "cursor-adapter";

    /**
     * <p>Interface used to bind a {@link android.database.Cursor} column to a View. This
     * interface can be used to provide bindings for data types not supported by the
     * standard implementation of {@link Adapters}.</p>
     * 
     * <p>A binder is provided with a cursor transformation which may or may not be used
     * to transform the value retrieved from the cursor. The transformation is guaranteed
     * to never be null so it's always safe to apply the transformation.</p>
     * 
     * <p>The binder is associated with a Context but can be re-used with multiple cursors.
     * As such, the implementation should make no assumption about the Cursor in use.</p>
     *
     * @see android.view.View 
     * @see android.database.Cursor
     * @see Adapters.CursorTransformation
     */
    public static abstract class CursorBinder {
        /**
         * <p>The context associated with this binder.</p>
         */
        protected final Context mContext;

        /**
         * <p>The transformation associated with this binder. This transformation is never
         * null and may or may not be applied to the Cursor data during the
         * {@link #bind(android.view.View, android.database.Cursor, int)} operation.</p>
         * 
         * @see #bind(android.view.View, android.database.Cursor, int) 
         */
        protected final CursorTransformation mTransformation;

        /**
         * <p>Creates a new Cursor binder.</p> 
         * 
         * @param context The context associated with this binder.
         * @param transformation The transformation associated with this binder. This
         *        transformation may or may not be applied by the binder and is guaranteed
         *        to not be null.
         */
        public CursorBinder(Context context, CursorTransformation transformation) {
            mContext = context;
            mTransformation = transformation;
        }

        /**
         * <p>Binds the specified Cursor column to the supplied View. The binding operation
         * can query other Cursor columns as needed. During the binding operation, values
         * retrieved from the Cursor may or may not be transformed using this binder's
         * cursor transformation.</p>
         * 
         * @param view The view to bind data to.
         * @param cursor The cursor to bind data from.
         * @param columnIndex The column index in the cursor where the data to bind resides.
         * 
         * @see #mTransformation
         * 
         * @return True if the column was successfully bound to the View, false otherwise.
         */
        public abstract boolean bind(View view, Cursor cursor, int columnIndex);
    }

    /**
     * <p>Interface used to transform data coming out of a {@link android.database.Cursor}
     * before it is bound to a {@link android.view.View}.</p>
     * 
     * <p>Transformations are used to transform text-based data (in the form of a String),
     * or to transform data into a resource identifier. A default implementation is provided
     * to generate resource identifiers.</p>
     * 
     * @see android.database.Cursor
     * @see Adapters.CursorBinder
     */
    public static abstract class CursorTransformation {
        /**
         * <p>The context associated with this transformation.</p>
         */
        protected final Context mContext;

        /**
         * <p>Creates a new Cursor transformation.</p>
         * 
         * @param context The context associated with this transformation.
         */
        public CursorTransformation(Context context) {
            mContext = context;
        }

        /**
         * <p>Transforms the specified Cursor column into a String. The transformation
         * can simply return the content of the column as a String (this is known
         * as the identity transformation) or manipulate the content. For instance,
         * a transformation can perform text substitutions or concatenate other
         * columns with the specified column.</p>
         * 
         * @param cursor The cursor that contains the data to transform. 
         * @param columnIndex The index of the column to transform.
         * 
         * @return A String containing the transformed value of the column.
         */
        public abstract String transform(Cursor cursor, int columnIndex);

        /**
         * <p>Transforms the specified Cursor column into a resource identifier.
         * The default implementation simply interprets the content of the column
         * as an integer.</p>
         * 
         * @param cursor The cursor that contains the data to transform. 
         * @param columnIndex The index of the column to transform.
         * 
         * @return A resource identifier.
         */
        public int transformToResource(Cursor cursor, int columnIndex) {
            return cursor.getInt(columnIndex);
        }
    }

    /**
     * <p>Loads the {@link android.widget.CursorAdapter} defined in the specified
     * XML resource. The content of the adapter is loaded from the content provider
     * identified by the supplied URI.</p>
     * 
     * <p><strong>Note:</strong> If the supplied {@link android.content.Context} is
     * an {@link android.app.Activity}, the cursor returned by the content provider
     * will be automatically managed. Otherwise, you are responsible for managing the
     * cursor yourself.</p>
     * 
     * <p>The format of the XML definition of the cursor adapter is documented at
     * the top of this page.</p>
     * 
     * @param context The context to load the XML resource from.
     * @param id The identifier of the XML resource declaring the adapter.
     * @param uri The URI of the content provider.
     * @param parameters Optional parameters to pass to the CursorAdapter, used
     *        to substitute values in the selection expression.
     * 
     * @return A {@link android.widget.CursorAdapter}
     * 
     * @throws IllegalArgumentException If the XML resource does not contain
     *         a valid &lt;cursor-adapter /&gt; definition.
     * 
     * @see android.content.ContentProvider
     * @see android.widget.CursorAdapter
     * @see #loadAdapter(android.content.Context, int, Object[])
     */
    public static CursorAdapter loadCursorAdapter(Context context, int id, String uri,
            Object... parameters) {

        XmlCursorAdapter adapter = (XmlCursorAdapter) loadAdapter(context, id, ADAPTER_CURSOR,
                parameters);

        if (uri != null) {
            adapter.setUri(uri);
        }
        adapter.load();

        return adapter;
    }

    /**
     * <p>Loads the {@link android.widget.CursorAdapter} defined in the specified
     * XML resource. The content of the adapter is loaded from the specified cursor.
     * You are responsible for managing the supplied cursor.</p>
     * 
     * <p>The format of the XML definition of the cursor adapter is documented at
     * the top of this page.</p>
     * 
     * @param context The context to load the XML resource from.
     * @param id The identifier of the XML resource declaring the adapter.
     * @param cursor The cursor containing the data for the adapter.
     * @param parameters Optional parameters to pass to the CursorAdapter, used
     *        to substitute values in the selection expression.
     * 
     * @return A {@link android.widget.CursorAdapter}
     * 
     * @throws IllegalArgumentException If the XML resource does not contain
     *         a valid &lt;cursor-adapter /&gt; definition.
     * 
     * @see android.content.ContentProvider
     * @see android.widget.CursorAdapter
     * @see android.database.Cursor
     * @see #loadAdapter(android.content.Context, int, Object[])
     */
    public static CursorAdapter loadCursorAdapter(Context context, int id, Cursor cursor,
            Object... parameters) {

        XmlCursorAdapter adapter = (XmlCursorAdapter) loadAdapter(context, id, ADAPTER_CURSOR,
                parameters);

        if (cursor != null) {
            adapter.changeCursor(cursor);
        }

        return adapter;
    }

    /**
     * <p>Loads the adapter defined in the specified XML resource. The XML definition of
     * the adapter must follow the format definition of one of the supported adapter
     * types described at the top of this page.</p>
     * 
     * <p><strong>Note:</strong> If the loaded adapter is a {@link android.widget.CursorAdapter}
     * and the supplied {@link android.content.Context} is an {@link android.app.Activity},
     * the cursor returned by the content provider will be automatically managed. Otherwise,
     * you are responsible for managing the cursor yourself.</p>
     * 
     * @param context The context to load the XML resource from.
     * @param id The identifier of the XML resource declaring the adapter.
     * @param parameters Optional parameters to pass to the adapter.
     *  
     * @return An adapter instance.
     * 
     * @see #loadCursorAdapter(android.content.Context, int, android.database.Cursor, Object[])
     * @see #loadCursorAdapter(android.content.Context, int, String, Object[])
     */
    public static BaseAdapter loadAdapter(Context context, int id, Object... parameters) {
        final BaseAdapter adapter = loadAdapter(context, id, null, parameters);
        if (adapter instanceof ManagedAdapter) {
            ((ManagedAdapter) adapter).load();
        }
        return adapter;
    }

    /**
     * Loads an adapter from the specified XML resource. The optional assertName can
     * be used to exit early if the adapter defined in the XML resource is not of the
     * expected type.
     * 
     * @param context The context to associate with the adapter.
     * @param id The resource id of the XML document defining the adapter.
     * @param assertName The mandatory name of the adapter in the XML document.
     *        Ignored if null.
     * @param parameters Optional parameters passed to the adapter.
     * 
     * @return An instance of {@link android.widget.BaseAdapter}.
     */
    private static BaseAdapter loadAdapter(Context context, int id, String assertName,
            Object... parameters) {

        XmlResourceParser parser = null;
        try {
            parser = context.getResources().getXml(id);
            return createAdapterFromXml(context, parser, Xml.asAttributeSet(parser),
                    id, parameters, assertName);
        } catch (XmlPullParserException ex) {
            Resources.NotFoundException rnf = new Resources.NotFoundException(
                    "Can't load adapter resource ID " +
                    context.getResources().getResourceEntryName(id));
            rnf.initCause(ex);
            throw rnf;
        } catch (IOException ex) {
            Resources.NotFoundException rnf = new Resources.NotFoundException(
                    "Can't load adapter resource ID " +
                    context.getResources().getResourceEntryName(id));
            rnf.initCause(ex);
            throw rnf;
        } finally {
            if (parser != null) parser.close();
        }
    }

    /**
     * Generates an adapter using the specified XML parser. This method is responsible
     * for choosing the type of the adapter to create based on the content of the
     * XML parser.
     * 
     * This method will generate an {@link IllegalArgumentException} if
     * <code>assertName</code> is not null and does not match the root tag of the XML
     * document.
     */
    private static BaseAdapter createAdapterFromXml(Context c,
            XmlPullParser parser, AttributeSet attrs, int id, Object[] parameters,
            String assertName) throws XmlPullParserException, IOException {

        BaseAdapter adapter = null;

        // Make sure we are on a start tag.
        int type;
        int depth = parser.getDepth();

        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) &&
                type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            String name = parser.getName();
            if (assertName != null && !assertName.equals(name)) {
                throw new IllegalArgumentException("The adapter defined in " +
                        c.getResources().getResourceEntryName(id) + " must be a <" +
                        assertName + " />");
            }

            if (ADAPTER_CURSOR.equals(name)) {
                adapter = createCursorAdapter(c, parser, attrs, id, parameters);
            } else {
                throw new IllegalArgumentException("Unknown adapter name " + parser.getName() +
                        " in " + c.getResources().getResourceEntryName(id));
            }
        }

        return adapter;

    }

    /**
     * Creates an XmlCursorAdapter using an XmlCursorAdapterParser.
     */
    private static XmlCursorAdapter createCursorAdapter(Context c, XmlPullParser parser,
            AttributeSet attrs, int id, Object[] parameters)
            throws IOException, XmlPullParserException {

        return new XmlCursorAdapterParser(c, parser, attrs, id).parse(parameters);
    }

    /**
     * Parser that can generate XmlCursorAdapter instances. This parser is responsible for
     * handling all the attributes and child nodes for a &lt;cursor-adapter /&gt;.
     */
    private static class XmlCursorAdapterParser {
        private static final String ADAPTER_CURSOR_BIND = "bind";
        private static final String ADAPTER_CURSOR_SELECT = "select";
        private static final String ADAPTER_CURSOR_AS_STRING = "string";
        private static final String ADAPTER_CURSOR_AS_IMAGE = "image";
        private static final String ADAPTER_CURSOR_AS_TAG = "tag";
        private static final String ADAPTER_CURSOR_AS_IMAGE_URI = "image-uri";
        private static final String ADAPTER_CURSOR_AS_DRAWABLE = "drawable";
        private static final String ADAPTER_CURSOR_MAP = "map";
        private static final String ADAPTER_CURSOR_TRANSFORM = "transform";

        private final Context mContext;
        private final XmlPullParser mParser;
        private final AttributeSet mAttrs;
        private final int mId;

        private final HashMap<String, CursorBinder> mBinders;
        private final ArrayList<String> mFrom;
        private final ArrayList<Integer> mTo;
        private final CursorTransformation mIdentity;
        private final Resources mResources;

        public XmlCursorAdapterParser(Context c, XmlPullParser parser, AttributeSet attrs, int id) {
            mContext = c;
            mParser = parser;
            mAttrs = attrs;
            mId = id;

            mResources = mContext.getResources();
            mBinders = new HashMap<String, CursorBinder>();
            mFrom = new ArrayList<String>();
            mTo = new ArrayList<Integer>();
            mIdentity = new IdentityTransformation(mContext);
        }

        public XmlCursorAdapter parse(Object[] parameters)
               throws IOException, XmlPullParserException {

            Resources resources = mResources;
            TypedArray a = resources.obtainAttributes(mAttrs, R.styleable.CursorAdapter);

            String uri = a.getString(R.styleable.CursorAdapter_uri);
            String selection = a.getString(R.styleable.CursorAdapter_selection);
            String sortOrder = a.getString(R.styleable.CursorAdapter_sortOrder);
            int layout = a.getResourceId(R.styleable.CursorAdapter_layout, 0);
            if (layout == 0) {
                throw new IllegalArgumentException("The layout specified in " +
                        resources.getResourceEntryName(mId) + " does not exist");
            }

            a.recycle();

            XmlPullParser parser = mParser;
            int type;
            int depth = parser.getDepth();

            while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) &&
                    type != XmlPullParser.END_DOCUMENT) {

                if (type != XmlPullParser.START_TAG) {
                    continue;
                }

                String name = parser.getName();

                if (ADAPTER_CURSOR_BIND.equals(name)) {
                    parseBindTag();
                } else if (ADAPTER_CURSOR_SELECT.equals(name)) {
                    parseSelectTag();
                } else {
                    throw new RuntimeException("Unknown tag name " + parser.getName() + " in " +
                            resources.getResourceEntryName(mId));
                }
            }

            String[] fromArray = mFrom.toArray(new String[mFrom.size()]);
            int[] toArray = new int[mTo.size()];
            for (int i = 0; i < toArray.length; i++) {
                toArray[i] = mTo.get(i);
            }

            String[] selectionArgs = null;
            if (parameters != null) {
                selectionArgs = new String[parameters.length];
                for (int i = 0; i < selectionArgs.length; i++) {
                    selectionArgs[i] = (String) parameters[i];
                }
            }

            return new XmlCursorAdapter(mContext, layout, uri, fromArray, toArray, selection,
                    selectionArgs, sortOrder, mBinders);
        }

        private void parseSelectTag() {
            TypedArray a = mResources.obtainAttributes(mAttrs,
                    R.styleable.CursorAdapter_SelectItem);

            String fromName = a.getString(R.styleable.CursorAdapter_SelectItem_column);
            if (fromName == null) {
                throw new IllegalArgumentException("A select item in " +
                        mResources.getResourceEntryName(mId) +
                        " does not have a 'column' attribute");
            }

            a.recycle();

            mFrom.add(fromName);
            mTo.add(View.NO_ID);
        }

        private void parseBindTag() throws IOException, XmlPullParserException {
            Resources resources = mResources;
            TypedArray a = resources.obtainAttributes(mAttrs,
                    R.styleable.CursorAdapter_BindItem);

            String fromName = a.getString(R.styleable.CursorAdapter_BindItem_from);
            if (fromName == null) {
                throw new IllegalArgumentException("A bind item in " +
                        resources.getResourceEntryName(mId) + " does not have a 'from' attribute");
            }

            int toName = a.getResourceId(R.styleable.CursorAdapter_BindItem_to, 0);
            if (toName == 0) {
                throw new IllegalArgumentException("A bind item in " +
                        resources.getResourceEntryName(mId) + " does not have a 'to' attribute");
            }

            String asType = a.getString(R.styleable.CursorAdapter_BindItem_as);
            if (asType == null) {
                throw new IllegalArgumentException("A bind item in " +
                        resources.getResourceEntryName(mId) + " does not have an 'as' attribute");
            }

            mFrom.add(fromName);
            mTo.add(toName);
            mBinders.put(fromName, findBinder(asType));

            a.recycle();
        }

        private CursorBinder findBinder(String type) throws IOException, XmlPullParserException {
            final XmlPullParser parser = mParser;
            final Context context = mContext;
            CursorTransformation transformation = mIdentity;

            int tagType;
            int depth = parser.getDepth();

            final boolean isDrawable = ADAPTER_CURSOR_AS_DRAWABLE.equals(type);

            while (((tagType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
                    && tagType != XmlPullParser.END_DOCUMENT) {

                if (tagType != XmlPullParser.START_TAG) {
                    continue;
                }

                String name = parser.getName();

                if (ADAPTER_CURSOR_TRANSFORM.equals(name)) {
                    transformation = findTransformation();
                } else if (ADAPTER_CURSOR_MAP.equals(name)) {
                    if (!(transformation instanceof MapTransformation)) {
                        transformation = new MapTransformation(context);
                    }
                    findMap(((MapTransformation) transformation), isDrawable);
                } else {
                    throw new RuntimeException("Unknown tag name " + parser.getName() + " in " +
                            context.getResources().getResourceEntryName(mId));
                }
            }

            if (ADAPTER_CURSOR_AS_STRING.equals(type)) {
                return new StringBinder(context, transformation);
            } else if (ADAPTER_CURSOR_AS_TAG.equals(type)) {
                return new TagBinder(context, transformation);
            } else if (ADAPTER_CURSOR_AS_IMAGE.equals(type)) {
                return new ImageBinder(context, transformation);
            } else if (ADAPTER_CURSOR_AS_IMAGE_URI.equals(type)) {
                return new ImageUriBinder(context, transformation);
            } else if (isDrawable) {
                return new DrawableBinder(context, transformation);
            } else {
                return createBinder(type, transformation);
            }
        }

        private CursorBinder createBinder(String type, CursorTransformation transformation) {
            if (mContext.isRestricted()) return null;

            try {
                final Class<?> klass = Class.forName(type, true, mContext.getClassLoader());
                if (CursorBinder.class.isAssignableFrom(klass)) {
                    final Constructor<?> c = klass.getDeclaredConstructor(
                            Context.class, CursorTransformation.class);
                    return (CursorBinder) c.newInstance(mContext, transformation);
                }
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("Cannot instanciate binder type in " +
                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("Cannot instanciate binder type in " +
                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
            } catch (InvocationTargetException e) {
                throw new IllegalArgumentException("Cannot instanciate binder type in " +
                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
            } catch (InstantiationException e) {
                throw new IllegalArgumentException("Cannot instanciate binder type in " +
                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException("Cannot instanciate binder type in " +
                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
            }

            return null;
        }

        private void findMap(MapTransformation transformation, boolean drawable) {
            Resources resources = mResources;

            TypedArray a = resources.obtainAttributes(mAttrs,
                    R.styleable.CursorAdapter_MapItem);

            String from = a.getString(R.styleable.CursorAdapter_MapItem_fromValue);
            if (from == null) {
                throw new IllegalArgumentException("A map item in " +
                        resources.getResourceEntryName(mId) +
                        " does not have a 'fromValue' attribute");
            }

            if (!drawable) {
                String to = a.getString(R.styleable.CursorAdapter_MapItem_toValue);
                if (to == null) {
                    throw new IllegalArgumentException("A map item in " +
                            resources.getResourceEntryName(mId) +
                            " does not have a 'toValue' attribute");
                }
                transformation.addStringMapping(from, to);
            } else {
                int to = a.getResourceId(R.styleable.CursorAdapter_MapItem_toValue, 0);
                if (to == 0) {
                    throw new IllegalArgumentException("A map item in " +
                            resources.getResourceEntryName(mId) +
                            " does not have a 'toValue' attribute");
                }
                transformation.addResourceMapping(from, to);
            }

            a.recycle();
        }

        private CursorTransformation findTransformation() {
            Resources resources = mResources;
            CursorTransformation transformation = null;
            TypedArray a = resources.obtainAttributes(mAttrs,
                    R.styleable.CursorAdapter_TransformItem);

            String className = a.getString(R.styleable.CursorAdapter_TransformItem_withClass);
            if (className == null) {
                String expression = a.getString(
                        R.styleable.CursorAdapter_TransformItem_withExpression);
                transformation = createExpressionTransformation(expression);
            } else if (!mContext.isRestricted()) {
                try {
                    final Class<?> klas = Class.forName(className, true, mContext.getClassLoader());
                    if (CursorTransformation.class.isAssignableFrom(klas)) {
                        final Constructor<?> c = klas.getDeclaredConstructor(Context.class);
                        transformation = (CursorTransformation) c.newInstance(mContext);
                    }
                } catch (ClassNotFoundException e) {
                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
                } catch (NoSuchMethodException e) {
                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
                } catch (InvocationTargetException e) {
                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
                } catch (InstantiationException e) {
                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
                } catch (IllegalAccessException e) {
                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
                }
            }

            a.recycle();

            if (transformation == null) {
                throw new IllegalArgumentException("A transform item in " +
                    resources.getResourceEntryName(mId) + " must have a 'withClass' or " +
                    "'withExpression' attribute");
            }

            return transformation;
        }

        private CursorTransformation createExpressionTransformation(String expression) {
            return new ExpressionTransformation(mContext, expression);
        }
    }

    /**
     * Interface used by adapters that require to be loaded after creation.
     */
    private static interface ManagedAdapter {
        /**
         * Loads the content of the adapter, asynchronously.
         */
        void load();
    }

    /**
     * Implementation of a Cursor adapter defined in XML. This class is a thin wrapper
     * of a SimpleCursorAdapter. The main difference is the ability to handle CursorBinders.
     */
    private static class XmlCursorAdapter extends SimpleCursorAdapter implements ManagedAdapter {
        private String mUri;
        private final String mSelection;
        private final String[] mSelectionArgs;
        private final String mSortOrder;
        private final String[] mColumns;
        private final CursorBinder[] mBinders;
        private AsyncTask<Void,Void,Cursor> mLoadTask;

        XmlCursorAdapter(Context context, int layout, String uri, String[] from, int[] to,
                String selection, String[] selectionArgs, String sortOrder,
                HashMap<String, CursorBinder> binders) {

            super(context, layout, null, from, to);
            mContext = context;
            mUri = uri;
            mSelection = selection;
            mSelectionArgs = selectionArgs;
            mSortOrder = sortOrder;
            mColumns = new String[from.length + 1];
            // This is mandatory in CursorAdapter
            mColumns[0] = "_id";
            System.arraycopy(from, 0, mColumns, 1, from.length);

            CursorBinder basic = new StringBinder(context, new IdentityTransformation(context));
            final int count = from.length;
            mBinders = new CursorBinder[count];

            for (int i = 0; i < count; i++) {
                CursorBinder binder = binders.get(from[i]);
                if (binder == null) binder = basic;
                mBinders[i] = binder;
            }
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            final int count = mTo.length;
            final int[] from = mFrom;
            final int[] to = mTo;
            final CursorBinder[] binders = mBinders;

            for (int i = 0; i < count; i++) {
                final View v = view.findViewById(to[i]);
                if (v != null) {
                    binders[i].bind(v, cursor, from[i]);
                }
            }
        }

        public void load() {
            if (mUri != null) {
                mLoadTask = new QueryTask().execute();
            }
        }

        void setUri(String uri) {
            mUri = uri;
        }

        @Override
        public void changeCursor(Cursor c) {
            if (mLoadTask != null && mLoadTask.getStatus() != QueryTask.Status.FINISHED) {
                mLoadTask.cancel(true);
                mLoadTask = null;
            }
            super.changeCursor(c);
        }

        class QueryTask extends AsyncTask<Void, Void, Cursor> {
            @Override
            protected Cursor doInBackground(Void... params) {
                if (mContext instanceof Activity) {
                    return ((Activity) mContext).managedQuery(
                            Uri.parse(mUri), mColumns, mSelection, mSelectionArgs, mSortOrder);
                } else {
                    return mContext.getContentResolver().query(
                            Uri.parse(mUri), mColumns, mSelection, mSelectionArgs, mSortOrder);
                }
            }

            @Override
            protected void onPostExecute(Cursor cursor) {
                if (!isCancelled()) {
                    XmlCursorAdapter.super.changeCursor(cursor);
                }
            }
        }
    }

    /**
     * Identity transformation, returns the content of the specified column as a String,
     * without performing any manipulation. This is used when no transformation is specified.
     */
    private static class IdentityTransformation extends CursorTransformation {
        public IdentityTransformation(Context context) {
            super(context);
        }

        @Override
        public String transform(Cursor cursor, int columnIndex) {
            return cursor.getString(columnIndex);
        }
    }

    /**
     * An expression transformation is a simple template based replacement utility.
     * In an expression, each segment of the form <code>{([^}]+)}</code> is replaced
     * with the value of the column of name $1.
     */
    private static class ExpressionTransformation extends CursorTransformation {
        private final ExpressionNode mFirstNode = new ConstantExpressionNode("");
        private final StringBuilder mBuilder = new StringBuilder();

        public ExpressionTransformation(Context context, String expression) {
            super(context);

            parse(expression);
        }

        private void parse(String expression) {
            ExpressionNode node = mFirstNode;
            int segmentStart;
            int count = expression.length();

            for (int i = 0; i < count; i++) {
                char c = expression.charAt(i);
                // Start a column name segment
                segmentStart = i;
                if (c == '{') {
                    while (i < count && (c = expression.charAt(i)) != '}') {
                        i++;
                    }
                    // We've reached the end, but the expression didn't close
                    if (c != '}') {
                        throw new IllegalStateException("The transform expression contains a " +
                                "non-closed column name: " +
                                expression.substring(segmentStart + 1, i));
                    }
                    node.next = new ColumnExpressionNode(expression.substring(segmentStart + 1, i));
                } else {
                    while (i < count && (c = expression.charAt(i)) != '{') {
                        i++;
                    }
                    node.next = new ConstantExpressionNode(expression.substring(segmentStart, i));
                    // Rewind if we've reached a column expression
                    if (c == '{') i--;
                }
                node = node.next;
            }
        }

        @Override
        public String transform(Cursor cursor, int columnIndex) {
            final StringBuilder builder = mBuilder;
            builder.delete(0, builder.length());

            ExpressionNode node = mFirstNode;
            // Skip the first node
            while ((node = node.next) != null) {
                builder.append(node.asString(cursor));
            }

            return builder.toString();
        }

        static abstract class ExpressionNode {
            public ExpressionNode next;

            public abstract String asString(Cursor cursor);
        }

        static class ConstantExpressionNode extends ExpressionNode {
            private final String mConstant;

            ConstantExpressionNode(String constant) {
                mConstant = constant;
            }

            @Override
            public String asString(Cursor cursor) {
                return mConstant;
            }
        }

        static class ColumnExpressionNode extends ExpressionNode {
            private final String mColumnName;
            private Cursor mSignature;
            private int mColumnIndex = -1;

            ColumnExpressionNode(String columnName) {
                mColumnName = columnName;
            }

            @Override
            public String asString(Cursor cursor) {
                if (cursor != mSignature || mColumnIndex == -1) {
                    mColumnIndex = cursor.getColumnIndex(mColumnName);
                    mSignature = cursor;
                }

                return cursor.getString(mColumnIndex);
            }
        }
    }

    /**
     * A map transformation offers a simple mapping between specified String values
     * to Strings or integers.
     */
    private static class MapTransformation extends CursorTransformation {
        private final HashMap<String, String> mStringMappings;
        private final HashMap<String, Integer> mResourceMappings;

        public MapTransformation(Context context) {
            super(context);
            mStringMappings = new HashMap<String, String>();
            mResourceMappings = new HashMap<String, Integer>();
        }

        void addStringMapping(String from, String to) {
            mStringMappings.put(from, to);
        }

        void addResourceMapping(String from, int to) {
            mResourceMappings.put(from, to);
        }

        @Override
        public String transform(Cursor cursor, int columnIndex) {
            final String value = cursor.getString(columnIndex);
            final String transformed = mStringMappings.get(value);
            return transformed == null ? value : transformed;
        }

        @Override
        public int transformToResource(Cursor cursor, int columnIndex) {
            final String value = cursor.getString(columnIndex);
            final Integer transformed = mResourceMappings.get(value);
            try {
                return transformed == null ? Integer.parseInt(value) : transformed;
            } catch (NumberFormatException e) {
                return 0;
            }
        }
    }

    /**
     * Binds a String to a TextView.
     */
    private static class StringBinder extends CursorBinder {
        public StringBinder(Context context, CursorTransformation transformation) {
            super(context, transformation);
        }

        @Override
        public boolean bind(View view, Cursor cursor, int columnIndex) {
            if (view instanceof TextView) {
                final String text = mTransformation.transform(cursor, columnIndex);
                ((TextView) view).setText(text);
                return true;
            }
            return false;
        }
    }

    /**
     * Binds an image blob to an ImageView.
     */
    private static class ImageBinder extends CursorBinder {
        public ImageBinder(Context context, CursorTransformation transformation) {
            super(context, transformation);
        }

        @Override
        public boolean bind(View view, Cursor cursor, int columnIndex) {
            if (view instanceof ImageView) {
                final byte[] data = cursor.getBlob(columnIndex);
                ((ImageView) view).setImageBitmap(BitmapFactory.decodeByteArray(data, 0,
                        data.length));
                return true;
            }
            return false;
        }
    }

    private static class TagBinder extends CursorBinder {
        public TagBinder(Context context, CursorTransformation transformation) {
            super(context, transformation);
        }

        @Override
        public boolean bind(View view, Cursor cursor, int columnIndex) {
            final String text = mTransformation.transform(cursor, columnIndex);
            view.setTag(text);
            return true;
        }
    }

    /**
     * Binds an image URI to an ImageView.
     */
    private static class ImageUriBinder extends CursorBinder {
        public ImageUriBinder(Context context, CursorTransformation transformation) {
            super(context, transformation);
        }

        @Override
        public boolean bind(View view, Cursor cursor, int columnIndex) {
            if (view instanceof ImageView) {
                ((ImageView) view).setImageURI(Uri.parse(
                        mTransformation.transform(cursor, columnIndex)));
                return true;
            }
            return false;
        }
    }

    /**
     * Binds a drawable resource identifier to an ImageView.
     */
    private static class DrawableBinder extends CursorBinder {
        public DrawableBinder(Context context, CursorTransformation transformation) {
            super(context, transformation);
        }

        @Override
        public boolean bind(View view, Cursor cursor, int columnIndex) {
            if (view instanceof ImageView) {
                final int resource = mTransformation.transformToResource(cursor, columnIndex);
                if (resource == 0) return false;

                ((ImageView) view).setImageResource(resource);
                return true;
            }
            return false;
        }
    }
}



//src\com\example\android\xmladapters\ContactPhotoBinder.java

package com.example.android.xmladapters;

import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.ContactsContract;
import android.view.View;
import android.widget.TextView;

import java.io.InputStream;
import java.util.HashMap;

/**
 * This custom cursor binder is used by the adapter defined in res/xml to
 * bind contacts photos to their respective list item. This binder simply
 * queries a contact's photo based on the contact's id and sets the
 * photo as a compound drawable on the TextView used to display the contact's
 * name.
 */
public class ContactPhotoBinder extends Adapters.CursorBinder {
    private static final int PHOTO_SIZE_DIP = 54;
    
    private final Drawable mDefault;
    private final HashMap<Long, Drawable> mCache;
    private final Resources mResources;
    private final int mPhotoSize;

    public ContactPhotoBinder(Context context, Adapters.CursorTransformation transformation) {
        super(context, transformation);

        mResources = mContext.getResources();
        // Default picture used when a contact does not provide one
        mDefault = mResources.getDrawable(R.drawable.ic_contact_picture);
        // Cache used to avoid re-querying contacts photos every time
        mCache = new HashMap<Long, Drawable>();
        // Compute the size of the photo based on the display's density
        mPhotoSize = (int) (PHOTO_SIZE_DIP * mResources.getDisplayMetrics().density + 0.5f);
    }

    @Override
    public boolean bind(View view, Cursor cursor, int columnIndex) {
        final long id = cursor.getLong(columnIndex);
        
        // First check whether we have already cached the contact's photo
        Drawable d = mCache.get(id);
        
        if (d == null) {
            // If the photo wasn't in the cache, ask the contacts provider for
            // an input stream we can use to load the photo
            Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
            InputStream stream = ContactsContract.Contacts.openContactPhotoInputStream(
                    mContext.getContentResolver(), uri);
    
            // Creates the drawable for the contact's photo or use our fallback drawable
            if (stream != null) {
                // decoding the bitmap could be done in a worker thread too.
                Bitmap bitmap = BitmapFactory.decodeStream(stream);
                d = new BitmapDrawable(mResources, bitmap);
            } else {
                d = mDefault;
            }

            d.setBounds(0, 0, mPhotoSize, mPhotoSize);
            ((TextView) view).setCompoundDrawables(d, null, null, null);

            // Remember the photo associated with this contact
            mCache.put(id, d);
        } else {
            ((TextView) view).setCompoundDrawables(d, null, null, null);
        }

        return true;
    }
}



//src\com\example\android\xmladapters\ContactsListActivity.java


package com.example.android.xmladapters;

import android.app.ListActivity;
import android.os.Bundle;

/**
 * This activity demonstrates how to create a complex UI using a ListView
 * and an adapter defined in XML.
 * 
 * The following activity shows a list of contacts, their starred status
 * and their photos, using the adapter defined in res/xml.
 */
public class ContactsListActivity extends ListActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.contacts_list);
        setListAdapter(Adapters.loadAdapter(this, R.xml.contacts));
    }
}



//src\com\example\android\xmladapters\ImageDownloader.java

 */package com.example.android.xmladapters;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * This helper class download images from the Internet and binds those with the provided ImageView.
 *
 * <p>It requires the INTERNET permission, which should be added to your application's manifest
 * file.</p>
 *
 * A local cache of downloaded images is maintained internally to improve performance.
 */
public class ImageDownloader {
    private static final String LOG_TAG = "ImageDownloader";

    private static final int HARD_CACHE_CAPACITY = 40;
    private static final int DELAY_BEFORE_PURGE = 30 * 1000; // in milliseconds

    // Hard cache, with a fixed maximum capacity and a life duration
    private final HashMap<String, Bitmap> sHardBitmapCache =
        new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            if (size() > HARD_CACHE_CAPACITY) {
                // Entries push-out of hard reference cache are transferred to soft reference cache
                sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                return true;
            } else
                return false;
        }
    };

    // Soft cache for bitmap kicked out of hard cache
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
        new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

    private final Handler purgeHandler = new Handler();

    private final Runnable purger = new Runnable() {
        public void run() {
            clearCache();
        }
    };

    /**
     * Download the specified image from the Internet and binds it to the provided ImageView. The
     * binding is immediate if the image is found in the cache and will be done asynchronously
     * otherwise. A null bitmap will be associated to the ImageView if an error occurs.
     *
     * @param url The URL of the image to download.
     * @param imageView The ImageView to bind the downloaded image to.
     */
    public void download(String url, ImageView imageView) {
        download(url, imageView, null);
    }

    /**
     * Same as {@link #download(String, ImageView)}, with the possibility to provide an additional
     * cookie that will be used when the image will be retrieved.
     *
     * @param url The URL of the image to download.
     * @param imageView The ImageView to bind the downloaded image to.
     * @param cookie A cookie String that will be used by the http connection.
     */
    public void download(String url, ImageView imageView, String cookie) {
        resetPurgeTimer();
        Bitmap bitmap = getBitmapFromCache(url);

        if (bitmap == null) {
            forceDownload(url, imageView, cookie);
        } else {
            cancelPotentialDownload(url, imageView);
            imageView.setImageBitmap(bitmap);
        }
    }

    /*
     * Same as download but the image is always downloaded and the cache is not used.
     * Kept private at the moment as its interest is not clear.
       private void forceDownload(String url, ImageView view) {
          forceDownload(url, view, null);
       }
     */

    /**
     * Same as download but the image is always downloaded and the cache is not used.
     * Kept private at the moment as its interest is not clear.
     */
    private void forceDownload(String url, ImageView imageView, String cookie) {
        // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.
        if (url == null) {
            imageView.setImageDrawable(null);
            return;
        }

        if (cancelPotentialDownload(url, imageView)) {
            BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
            imageView.setImageDrawable(downloadedDrawable);
            task.execute(url, cookie);
        }
    }

    /**
     * Clears the image cache used internally to improve performance. Note that for memory
     * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
     */
    public void clearCache() {
        sHardBitmapCache.clear();
        sSoftBitmapCache.clear();
    }

    private void resetPurgeTimer() {
        purgeHandler.removeCallbacks(purger);
        purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
    }

    /**
     * Returns true if the current download has been canceled or if there was no download in
     * progress on this image view.
     * Returns false if the download in progress deals with the same url. The download is not
     * stopped in that case.
     */
    private static boolean cancelPotentialDownload(String url, ImageView imageView) {
        BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

        if (bitmapDownloaderTask != null) {
            String bitmapUrl = bitmapDownloaderTask.url;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                bitmapDownloaderTask.cancel(true);
            } else {
                // The same URL is already being downloaded.
                return false;
            }
        }
        return true;
    }

    /**
     * @param imageView Any imageView
     * @return Retrieve the currently active download task (if any) associated with this imageView.
     * null if there is no such task.
     */
    private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
        if (imageView != null) {
            Drawable drawable = imageView.getDrawable();
            if (drawable instanceof DownloadedDrawable) {
                DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
                return downloadedDrawable.getBitmapDownloaderTask();
            }
        }
        return null;
    }

    /**
     * @param url The URL of the image that will be retrieved from the cache.
     * @return The cached bitmap or null if it was not found.
     */
    private Bitmap getBitmapFromCache(String url) {
        // First try the hard reference cache
        synchronized (sHardBitmapCache) {
            final Bitmap bitmap = sHardBitmapCache.get(url);
            if (bitmap != null) {
                // Bitmap found in hard cache
                // Move element to first position, so that it is removed last
                sHardBitmapCache.remove(url);
                sHardBitmapCache.put(url, bitmap);
                return bitmap;
            }
        }

        // Then try the soft reference cache
        SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
        if (bitmapReference != null) {
            final Bitmap bitmap = bitmapReference.get();
            if (bitmap != null) {
                // Bitmap found in soft cache
                return bitmap;
            } else {
                // Soft reference has been Garbage Collected
                sSoftBitmapCache.remove(url);
            }
        }

        return null;
    }

    /**
     * The actual AsyncTask that will asynchronously download the image.
     */
    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
        private static final int IO_BUFFER_SIZE = 4 * 1024;
        private String url;
        private final WeakReference<ImageView> imageViewReference;

        public BitmapDownloaderTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        /**
         * Actual download method.
         */
        @Override
        protected Bitmap doInBackground(String... params) {
            final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
            url = params[0];
            final HttpGet getRequest = new HttpGet(url);
            String cookie = params[1];
            if (cookie != null) {
                getRequest.setHeader("cookie", cookie);
            }

            try {
                HttpResponse response = client.execute(getRequest);
                final int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode != HttpStatus.SC_OK) {
                    Log.w("ImageDownloader", "Error " + statusCode +
                            " while retrieving bitmap from " + url);
                    return null;
                }

                final HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream inputStream = null;
                    OutputStream outputStream = null;
                    try {
                        inputStream = entity.getContent();
                        final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
                        outputStream = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
                        copy(inputStream, outputStream);
                        outputStream.flush();

                        final byte[] data = dataStream.toByteArray();
                        final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

                        // FIXME : Should use BitmapFactory.decodeStream(inputStream) instead.
                        //final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                        return bitmap;

                    } finally {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        if (outputStream != null) {
                            outputStream.close();
                        }
                        entity.consumeContent();
                    }
                }
            } catch (IOException e) {
                getRequest.abort();
                Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
            } catch (IllegalStateException e) {
                getRequest.abort();
                Log.w(LOG_TAG, "Incorrect URL: " + url);
            } catch (Exception e) {
                getRequest.abort();
                Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
            } finally {
                if (client != null) {
                    client.close();
                }
            }
            return null;
        }

        /**
         * Once the image is downloaded, associates it to the imageView
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }

            // Add bitmap to cache
            if (bitmap != null) {
                synchronized (sHardBitmapCache) {
                    sHardBitmapCache.put(url, bitmap);
                }
            }

            if (imageViewReference != null) {
                ImageView imageView = imageViewReference.get();
                BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                // Change bitmap only if this process is still associated with it
                if (this == bitmapDownloaderTask) {
                    imageView.setImageBitmap(bitmap);
                }
            }
        }

        public void copy(InputStream in, OutputStream out) throws IOException {
            byte[] b = new byte[IO_BUFFER_SIZE];
            int read;
            while ((read = in.read(b)) != -1) {
                out.write(b, 0, read);
            }
        }
    }

    /**
     * A fake Drawable that will be attached to the imageView while the download is in progress.
     *
     * <p>Contains a reference to the actual download task, so that a download task can be stopped
     * if a new binding is required, and makes sure that only the last started download process can
     * bind its result, independently of the download finish order.</p>
     */
    static class DownloadedDrawable extends ColorDrawable {
        private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

        public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
            super(Color.BLACK);
            bitmapDownloaderTaskReference =
                new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
        }

        public BitmapDownloaderTask getBitmapDownloaderTask() {
            return bitmapDownloaderTaskReference.get();
        }
    }
}



//src\com\example\android\xmladapters\PhotosListActivity.java


package com.example.android.xmladapters;

import android.app.ListActivity;
import android.net.Uri;
import android.os.Bundle;

/**
 * This activity uses a custom cursor adapter which fetches a XML photo feed and parses the XML to
 * extract the images' URL and their title.
 */
public class PhotosListActivity extends ListActivity {
    private static final String PICASA_FEED_URL =
        "http://picasaweb.google.com/data/feed/api/featured?max-results=50&thumbsize=144c";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.photos_list);
        setListAdapter(Adapters.loadCursorAdapter(this, R.xml.photos,
                "content://xmldocument/?url=" + Uri.encode(PICASA_FEED_URL)));
    }
}



//src\com\example\android\xmladapters\RssReaderActivity.java


package com.example.android.xmladapters;

import android.app.ListActivity;
import android.content.XmlDocumentProvider;
import android.net.Uri;
import android.os.Bundle;
import android.widget.AdapterView.OnItemClickListener;

/**
 * This example demonstrate the creation of a simple RSS feed reader using the XML adapter syntax.
 * The different elements of the feed are extracted using an {@link XmlDocumentProvider} and are
 * binded to the different views. An {@link OnItemClickListener} is also added, which will open a
 * browser on the associated news item page.
 */
public class RssReaderActivity extends ListActivity {
    private static final String FEED_URI = "http://feeds.nytimes.com/nyt/rss/HomePage";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.rss_feeds_list);
        setListAdapter(Adapters.loadCursorAdapter(this, R.xml.rss_feed,
                "content://xmldocument/?url=" + Uri.encode(FEED_URI)));

        getListView().setOnItemClickListener(new UrlIntentListener());
    }
}



//src\com\example\android\xmladapters\UrlImageBinder.java


package com.example.android.xmladapters;

import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.widget.ImageView;

/**
 * This CursorBinder binds the provided image URL to an ImageView by downloading the image from the
 * Internet.
 */
public class UrlImageBinder extends Adapters.CursorBinder {

    private final ImageDownloader imageDownloader;

    public UrlImageBinder(Context context, Adapters.CursorTransformation transformation) {
        super(context, transformation);
        imageDownloader = new ImageDownloader();
    }

    @Override
    public boolean bind(View view, Cursor cursor, int columnIndex) {
        if (view instanceof ImageView) {
            final String url = mTransformation.transform(cursor, columnIndex);
            imageDownloader.download(url, (ImageView) view);
            return true;
        }

        return false;
    }
}



//src\com\example\android\xmladapters\UrlIntentListener.java


package com.example.android.xmladapters;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;

/**
 * A listener which expects a URL as a tag of the view it is associated with. It then opens the URL
 * in the browser application.
 */
public class UrlIntentListener implements OnItemClickListener {

    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        final String url = view.getTag().toString();
        final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        final Context context = parent.getContext();
        context.startActivity(intent);
    }

}



//
//res\layout\contact_item.xml
<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight">

    <TextView
        android:id="@+id/name"
        android:layout_width="0px"
        android:layout_weight="1.0"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center_vertical"
        android:drawablePadding="6dip"
        android:paddingLeft="6dip"
        android:paddingRight="6dip" />

    <ImageView
        android:id="@+id/star"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />    

</LinearLayout>



//res\layout\contacts_list.xml
<?xml version="1.0" encoding="utf-8"?>


 
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent" 
        android:layout_height="match_parent" />

    <TextView android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/no_contacts"
        android:visibility="gone" />

</merge>    



//res\layout\photo_item.xml
<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight">

    <ImageView
        android:id="@+id/photo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="6dip"
        android:paddingTop="4dip"
        android:paddingBottom="4dip" />

    <TextView
        android:id="@+id/title"
        android:layout_width="0px"
        android:layout_weight="1.0"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:gravity="center_vertical"
        android:paddingLeft="6dip"
        android:paddingRight="6dip" />

</LinearLayout>



//res\layout\photos_list.xml
<?xml version="1.0" encoding="utf-8"?>


<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <TextView android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/no_photos"
        android:visibility="gone" />

</merge>



//res\layout\rss_feed_item.xml
<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:minHeight="?android:attr/listPreferredItemHeight">

    <TextView
        android:id="@+id/title"
        android:layout_width="fill_parent"
        android:layout_weight="1.0"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:gravity="left"
        android:paddingLeft="6dip"
        android:paddingRight="6dip" />

    <TextView
        android:id="@+id/date"
        android:layout_width="fill_parent"
        android:layout_weight="1.0"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:gravity="left"
        android:paddingLeft="6dip"
        android:paddingRight="6dip" />

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:minHeight="?android:attr/listPreferredItemHeight">

       <ImageView
           android:id="@+id/image"
           android:layout_width="wrap_content"
           android:layout_height="match_parent"
           android:gravity="center_vertical"
           android:paddingLeft="6dip" />

       <TextView
           android:id="@+id/description"
           android:layout_width="fill_parent"
           android:layout_weight="1.0"
           android:layout_height="wrap_content"
           android:textAppearance="?android:attr/textAppearanceSmall"
           android:gravity="left"
           android:paddingLeft="6dip"
           android:paddingRight="6dip" />

    </LinearLayout>

</LinearLayout>



//res\layout\rss_feeds_list.xml
<?xml version="1.0" encoding="utf-8"?>


<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <TextView android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/no_rss_feed"
        android:visibility="gone" />

</merge>



//
//res\values\attrs.xml
<?xml version="1.0" encoding="utf-8"?>


<resources>

    <!-- Adapter used to bind cursors. -->
    <declare-styleable name="CursorAdapter">
        <!-- URI to get the cursor from. Optional. -->
        <attr name="uri" format="string" />
        <!-- Selection statement for the query. Optional. -->
        <attr name="selection" format="string" />
        <!-- Sort order statement for the query. Optional. -->
        <attr name="sortOrder" format="string" />
        <!-- Layout resource used to display each row from the cursor. Mandatory. -->
        <attr name="layout" format="reference" />
    </declare-styleable>

    <!-- Attributes used in bind items for XML cursor adapters. -->
    <declare-styleable name="CursorAdapter_BindItem">
        <!-- The name of the column to bind from. Mandatory. -->
        <attr name="from" format="string" />
        <!-- The resource id of the view to bind to. Mandatory. -->
        <attr name="to" format="reference" />
        <!-- The type of binding. If this value is not specified, the type will be
             inferred from the type of the "to" target view. Mandatory.

             The type can be one of:
             <ul>
             <li>string, The content of the column is interpreted as a string.</li>
             <li>image, The content of the column is interpreted as a blob describing an image.</li>
             <li>image-uri, The content of the column is interpreted as a URI to an image.</li>
             <li>drawable, The content of the column is interpreted as a resource id to a drawable.</li>
             <li>A fully qualified class name, corresponding to an implementation of
                 android.widget.Adapters.CursorBinder.</li>
             </ul>
         -->
        <attr name="as" format="string" />
    </declare-styleable>

    <!-- Attributes used in select items for XML cursor adapters.-->
    <declare-styleable name="CursorAdapter_SelectItem">
        <!-- The name of the column to select. Mandatory. -->
        <attr name="column" format="string" />
    </declare-styleable>

    <!-- Attributes used to map values to new values in XML cursor adapters' bind items. -->
    <declare-styleable name="CursorAdapter_MapItem">
        <!-- The original value from the column. Mandatory. -->
        <attr name="fromValue" format="string" />
        <!-- The new value from the column. Mandatory. -->
        <attr name="toValue" format="string" />
    </declare-styleable>

    <!-- Attributes used to map values to new values in XML cursor adapters' bind items. -->
    <declare-styleable name="CursorAdapter_TransformItem">
        <!-- The transformation expression. Mandatory if "withClass" is not specified. -->
        <attr name="withExpression" format="string" />
        <!-- The transformation class, an implementation of
             android.widget.Adapters.CursorTransformation. Mandatory if "withExpression"
             is not specified. -->
        <attr name="withClass" format="string" />
    </declare-styleable>

</resources>



//res\values\strings.xml
<?xml version="1.0" encoding="utf-8"?>


<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="app_name">Xml Adapters</string>
    <string name="contacts_list_activity">Xml Contacts Adapter</string>
    <string name="photos_list_activity">Xml Photos Adapter</string>
    <string name="rss_reader_activity">Xml RSS Reader</string>
    <string name="no_contacts">No contacts available</string>
    <string name="no_photos">Loading photos...</string>
    <string name="no_rss_feed">Loading RSS feed...</string>

</resources>



//
//res\xml\contacts.xml
<?xml version="1.0" encoding="utf-8"?>


<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res/com.example.android.xmladapters"
  app:uri="content://com.android.contacts/contacts"
  app:selection="has_phone_number=1"
  app:layout="@layout/contact_item">

  <bind app:from="display_name" app:to="@id/name" app:as="string" />
  <bind app:from="starred" app:to="@id/star" app:as="drawable">
        <map app:fromValue="0" app:toValue="@android:drawable/star_big_off" />
        <map app:fromValue="1" app:toValue="@android:drawable/star_big_on" />
  </bind>
  <bind app:from="_id" app:to="@id/name" app:as="com.example.android.xmladapters.ContactPhotoBinder" />

</cursor-adapter>



//res\xml\photos.xml
<?xml version="1.0" encoding="utf-8"?>


<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.example.android.xmladapters"
    app:selection="/feed/entry"
    app:layout="@layout/photo_item">

    <bind app:from="/summary" app:to="@id/title" app:as="string" />
    <bind app:from="/media:group/media:thumbnail\@url" app:to="@id/photo"
      app:as="com.example.android.xmladapters.UrlImageBinder" />

</cursor-adapter>



//res\xml\rss_feed.xml
<?xml version="1.0" encoding="utf-8"?>

<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.example.android.xmladapters"
    app:selection="/rss/channel/item"
    app:layout="@layout/rss_feed_item">

    <bind app:from="/title" app:to="@id/title" app:as="string" />
    <bind app:from="/media:content@url" app:to="@id/image" app:as="com.example.android.xmladapters.UrlImageBinder"/>
    <bind app:from="/media:description" app:to="@id/description" app:as="string" />
    <bind app:from="/guid" app:to="@id/item_layout" app:as="tag" />
    <bind app:from="/pubDate" app:to="@id/date" app:as="string">
       <transform app:withExpression="Published on {/pubDate}." />
    </bind>

</cursor-adapter>

Xml Parse

     
//package com.helloandroid.acashboard.list;

import java.util.ArrayList;
import java.util.List;

import android.sax.Element;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.util.Xml;

public class XmlParseList {
//  TODO: do 1 function 2 folowing functon
//  parsing projects

  public List<String> parse(String XMLDOCUMENT,String root2,String itemname,String title,String title2) {
//    final Message currentMessage = new Message(); //TODO unused variable
    RootElement root = new RootElement(root2);
    final List<String> messages = new ArrayList<String>();
    final List<String> id = new ArrayList<String>();
    Element item = root.getChild(itemname);
    item.setEndElementListener(new EndElementListener() {
      public void end() {
        // documentd end listener!
      }
    });
    item.getChild(title).setEndTextElementListener(
        new EndTextElementListener() {
          public void end(String body) {
            // TODO GET CONTENT
            messages.add(body); //get name
          }
        });
    
    item.getChild(title2).setEndTextElementListener(
        new EndTextElementListener() {
          public void end(String body) {
            // TODO GET CONTENT
            messages.add(body); //get id
          }
        });
    
    try {
      Xml.parse(XMLDOCUMENT, root.getContentHandler());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return messages;
  }
  

}

Xml Serializer Uri

     
/**
 *
 */
//package org.alldroid.forum.xml;


import java.net.URL;
import java.util.LinkedList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import android.net.Uri;
import android.util.Log;

/**
 * @author trr4rac
 *
 */
public class XmlSerializer {
  private static final String TAG = XmlSerializer.class.getSimpleName ( );

  private DocumentBuilder builder;
  private Document document;
  /**
   *
   */
  public XmlSerializer ( Uri url ) {
    try {
      builder = DocumentBuilderFactory.newInstance ( ).newDocumentBuilder ( );
      document = builder.parse(new URL(url.toString ( )).openStream ( ));
    } catch ( Exception e ) {
      Log.e(TAG,e.toString ( ));
    }
  }

  public <T extends IXmlSerializable> T deserialize ( Class<T> cls ) throws SAXException {
    T result;

    if ( document == null ) {
      throw new SAXException("Document not available.");
    }

    Element ele = document.getDocumentElement ( );
    try {
      result = cls.newInstance ( );

    } catch ( Exception e ) {
      Log.e(TAG,e.toString ( ));
      return null;
    }

    result.deserialize ( ele );
    return result;
  }
}
interface IXmlSerializable {
  public abstract void deserialize ( Element ele );
}

Get Text Content from Xml Node

     
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


class Dom {
  public static String getTextContent(Node n) {
    StringBuffer sb = new StringBuffer();
    NodeList nl = n.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node child = nl.item(i);
      if (child.getNodeType() == Node.TEXT_NODE)
        sb.append(child.getNodeValue());
    }
    return sb.toString();
  }

}

Escape un escape Xml

     

class Main{

  public static String escapeXMLChars(String s) {
    return s.replaceAll("&",  "&amp;")
         .replaceAll("'",  "&apos;")
         .replaceAll("\"", "&quot;")
         .replaceAll("<",  "&lt;")
         .replaceAll(">",  "&gt;");
  }
  
  public static String unescapeXMLChars(String s) {
    return s.replaceAll("&amp;",  "&")
         .replaceAll("&apos;", "'")
         .replaceAll("&quot;", "\"")
         .replaceAll("&lt;",   "<")
         .replaceAll("&gt;",   ">");
  }
}

Get Xml node value with substring

     
class Main{
  public static String getNodeValue(String XML, String nodeName){
    String tokenValue = null;
    int start = XML.indexOf("<"+nodeName+">") + ("<"+nodeName+">").length();
    int stop = XML.indexOf("</"+nodeName+">");
    if(start>stop || stop<0) return "";
    try{
      tokenValue = XML.substring(start, stop);
    }catch (Exception ex){
      ex.printStackTrace();  
    }    
    return tokenValue;
  }
  

}

Get value from Element

     
import java.util.ArrayList;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

class Main {

  public static Long getLongElementValue(Element el, String child,
      long defaultValue) {
    try {
      String v = ((Element) el.getElementsByTagName(child).item(0))
          .getChildNodes().item(0).getNodeValue();

      return Long.parseLong(v);
    } catch (Exception ex) {
      return defaultValue;
    }
  }

  public static int getIntElementValue(Element el, String child,
      int defaultValue) {
    try {
      String v = ((Element) el.getElementsByTagName(child).item(0))
          .getChildNodes().item(0).getNodeValue();

      return Integer.parseInt(v);
    } catch (Exception ex) {
      return defaultValue;
    }
  }

  public static ArrayList<Element> getElements(Element parent, String children) {
    NodeList nodelist = parent.getElementsByTagName(children);
    ArrayList<Element> elements = new ArrayList<Element>();
    int l = nodelist.getLength();
    for (int i = 0; i < l; i++) {
      Element element = (Element) nodelist.item(i);
      elements.add(element);
    }
    return elements;
  }

  public static Element getElement(Element parent, String name) {
    NodeList nodelist = parent.getElementsByTagName(name);
    if (nodelist.getLength() > 0) {
      return (Element) nodelist.item(0);
    }
    return null;
  }

}

Get attribute value

     

//package org.anddev.andengine.util;

import org.xml.sax.Attributes;


class SAXUtils {
  public static String getAttribute(final Attributes pAttributes, final String pAttributeName, final String pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? value : pDefaultValue;
  }

  public static String getAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    final String value = pAttributes.getValue("", pAttributeName);
    if(value != null) {
      return value;
    } else {
      throw new IllegalArgumentException("No value found for attribute: '" + pAttributeName + "'");
    }
  }

  public static boolean getBooleanAttribute(final Attributes pAttributes, final String pAttributeName, final boolean pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Boolean.parseBoolean(value) : pDefaultValue;
  }

  public static boolean getBooleanAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Boolean.parseBoolean(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }

  public static byte getByteAttribute(final Attributes pAttributes, final String pAttributeName, final byte pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Byte.parseByte(value) : pDefaultValue;
  }

  public static byte getByteAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Byte.parseByte(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static short getShortAttribute(final Attributes pAttributes, final String pAttributeName, final short pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Short.parseShort(value) : pDefaultValue;
  }

  public static short getShortAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Short.parseShort(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static int getIntAttribute(final Attributes pAttributes, final String pAttributeName, final int pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Integer.parseInt(value) : pDefaultValue;
  }

  public static int getIntAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Integer.parseInt(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static long getLongAttribute(final Attributes pAttributes, final String pAttributeName, final long pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Long.parseLong(value) : pDefaultValue;
  }

  public static long getLongAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Long.parseLong(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static float getFloatAttribute(final Attributes pAttributes, final String pAttributeName, final float pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Float.parseFloat(value) : pDefaultValue;
  }

  public static float getFloatAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Float.parseFloat(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static double getDoubleAttribute(final Attributes pAttributes, final String pAttributeName, final double pDefaultValue) {
    final String value = pAttributes.getValue("", pAttributeName);
    return (value != null) ? Double.parseDouble(value) : pDefaultValue;
  }

  public static double getDoubleAttributeOrThrow(final Attributes pAttributes, final String pAttributeName) {
    return Double.parseDouble(SAXUtils.getAttributeOrThrow(pAttributes, pAttributeName));
  }


  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final boolean pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final byte pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final short pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final int pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final long pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final float pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final double pValue) {
    SAXUtils.appendAttribute(pStringBuilder, pName, String.valueOf(pValue));
  }

  public static void appendAttribute(final StringBuilder pStringBuilder, final String pName, final String pValue) {
    pStringBuilder.append(' ').append(pName).append('=').append('\"').append(pValue).append('\"');
  }

  //          
  // Inner and Anonymous Classes
  //          
}

Get field from NamedNodeMap

     

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

public abstract class APIUtil
{
    public static String getField( NamedNodeMap fields, String fieldName )
    {
        Node field = fields.getNamedItem( fieldName );

        if( field != null )
            return field.getNodeValue();
        else
            return new String();
    }
}

get Node Value With Attribute

     
class Main {

  public static String getNodeValueWithAttribute(String XML, String nodeName,
      String attribute) {
    String tokenValue = null;
    int start = XML.indexOf("<" + nodeName + " " + attribute + ">")
        + ("<" + nodeName + " " + attribute + ">").length();
    int stop = XML.indexOf("</" + nodeName + ">");
    try {
      tokenValue = XML.substring(start, stop);
    } catch (Exception ex) {

    }
    return tokenValue;
  }

}

get Character Data From Element

     
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

class Main {

  public static String getCharacterDataFromElement(Element e) {
    Node child = e.getFirstChild();
    if (child instanceof CharacterData) {
      CharacterData cd = (CharacterData) child;
      return cd.getData();
    }
    return "?";
  }
}

update Xml

     
//package it.tava.andbudget.data.parser;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlSerializer;

import android.util.Log;
import android.util.Xml;

public class XmlParser {

  private final static String TAG = "it.tava.andbudget.xml.XmlParser";

  private static final String JAXP = "http://java.sun.com/xml/jaxp/properties/";
  private static final String JAXP_SCHEMA_LANGUAGE = JAXP + "schemaLanguage";
  private static final String JAXP_SCHEMA_SOURCE = JAXP + "schemaSource";
  private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; 


  /**
   * Compila il file XML che viene passato come parametro e restituisce il Document DOM
   * associato, null altrimenti.
   * 
   * @param xml il file XML di cui effettuare il parsing
   * @param xsd il file XSD schema associato all'XML in input
   * @param validation se effettuare o meno la validazione del file ottenuto
   * @return il Document DOM associato al file in ingresso o null altrimenti
   */
  public static Document compileXml(File xml, File xsd, boolean validation) {
    Document document = null;

    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

      factory.setValidating(validation);
      factory.setNamespaceAware(true);

      factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
      factory.setAttribute(JAXP_SCHEMA_SOURCE, xsd);

      DocumentBuilder builder = factory.newDocumentBuilder();
      document = builder.parse(xml);

    } catch (SAXException se) {
      Log.e(TAG, "Error: compilation failed");
    } catch (IOException ioe) {
      Log.e(TAG, "Error: IOException");
    } catch (ParserConfigurationException e) {
      Log.e(TAG, "Error: ParserConfigurationException");
    }

    return document;
  }

  public static void updateXml(File xml, File xsd, Document document) {
    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();

    try {
      //Writer w = new FileWriter(new File("prova.xml"));

      serializer.setOutput(writer);

      serializer.startDocument("UTF-8", true);
      serializer.startTag("", "budget");
      serializer.endTag("", "budget");

      serializer.endDocument();
      
      System.out.println(writer.toString());

    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalStateException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}