From 03f0292744094ec107ffce71301c394503a31ded Mon Sep 17 00:00:00 2001
From: Gilles Debunne A XPath-like selection pattern is used to select some nodes in the XML document. Each such
+ * node will create a row in the {@link Cursor} result. To add this provider in your application, you should add its declaration to your application
+ * manifest:
+ * Accepts the following URI schemes:
*
@@ -1341,7 +1340,7 @@ public abstract class ContentResolver {
}
private final class CursorWrapperInner extends CursorWrapper {
- private IContentProvider mContentProvider;
+ private final IContentProvider mContentProvider;
public static final String TAG="CursorWrapperInner";
private boolean mCloseFlag = false;
@@ -1370,7 +1369,7 @@ public abstract class ContentResolver {
}
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
- private IContentProvider mContentProvider;
+ private final IContentProvider mContentProvider;
public static final String TAG="ParcelFileDescriptorInner";
private boolean mReleaseProviderFlag = false;
diff --git a/core/java/android/content/XmlDocumentProvider.java b/core/java/android/content/XmlDocumentProvider.java
new file mode 100644
index 0000000000000..153ad38b8bfdb
--- /dev/null
+++ b/core/java/android/content/XmlDocumentProvider.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.content.ContentResolver.OpenResourceIdResult;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.net.http.AndroidHttpClient;
+import android.util.Log;
+import android.widget.CursorAdapter;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.BitSet;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+/**
+ * A read-only content provider which extracts data out of an XML document.
+ *
+ *
+ * <provider android:name="android.content.XmlDocumentProvider" android:authorities="xmldocument" />
+ *
+ *
/node_name node selection patterns.
+ *
+ * The /root/child1/child2 pattern will for instance match all nodes named
+ * child2 which are children of a node named child1 which are themselves
+ * children of a root node named root.
/ separator in the previous expression can be replaced by a //
+ * separator instead, which indicated a descendant instead of a child.
+ *
+ * The //node1//node2 pattern will for instance match all nodes named
+ * node2 which are descendant of a node named node1 located anywhere in
+ * the document hierarchy.
namespace:node.
+ *
+ * Use a syntax similar to the selection syntax described above to select the text associated
+ * with a child of the selected node. The implicit root of this projection pattern is the selected
+ * node. / will hence refer to the text of the selected node, while
+ * /child1 will fetch the text of its child named child1 and
+ * //child1 will match any descendant named child1. If several
+ * nodes match the projection pattern, their texts are appended as a result.
@attribute_name
+ * pattern to the previously described syntax. //child1@price will for instance match
+ * the attribute price of any child1 descendant.
+ *
+ * If a projection does not match any node/attribute, its associated value will be an empty + * string.
+ * + *+ * <library> + * <book id="EH94"> + * <title>The Old Man and the Sea</title> + * <author>Ernest Hemingway</author> + * </book> + * <book id="XX10"> + * <title>The Arabian Nights: Tales of 1,001 Nights</title> + * </book> + * <no-id> + * <book> + * <title>Animal Farm</title> + * <author>George Orwell</author> + * </book> + * </no-id> + * </library> + *+ * A selection pattern of
/library//book will match the three book entries (while
+ * /library/book will only match the first two ones).
+ *
+ * Defining the projections as /title, /author and @id
+ * will retrieve the associated data. Note that the author of the second book as well as the id of
+ * the third are empty strings.
+ */
+public class XmlDocumentProvider extends ContentProvider {
+ /*
+ * Ideas for improvement:
+ * - Expand XPath-like syntax to allow for [nb] child number selector
+ * - Address the starting . bug in AbstractCursor which prevents a true XPath syntax.
+ * - Provide an alternative to concatenation when several node match (list-like).
+ * - Support namespaces in attribute names.
+ * - Incremental Cursor creation, pagination
+ */
+ private static final String LOG_TAG = "XmlDocumentProvider";
+ private AndroidHttpClient mHttpClient;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ /**
+ * Query data from the XML document referenced in the URI.
+ *
+ *
The XML document can be a local resource or a file that will be downloaded from the + * Internet. In the latter case, your application needs to request the INTERNET permission in + * its manifest.
+ * + * The URI will be of the formcontent://xmldocument/?resource=R.xml.myFile for a
+ * local resource. xmldocument should match the authority declared for this
+ * provider in your manifest. Internet documents are referenced using
+ * content://xmldocument/?url= followed by an encoded version of the URL of your
+ * document (see {@link Uri#encode(String)}).
+ *
+ * The number of columns of the resulting Cursor is equal to the size of the projection
+ * array plus one, named _id which will contain a unique row id (allowing the
+ * Cursor to be used with a {@link CursorAdapter}). The other columns' names are the projection
+ * patterns.
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 @@ -80,7 +79,8 @@ import static com.android.internal.R.*; * {@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}. * This attribute is optional. *
android:uri: URI of the content provider to query to retrieve a cursor.
- * Specifying this attribute is equivalent to calling {@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
+ * Specifying this attribute is equivalent to calling
+ * {@link android.widget.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.android:column: Name of the column to select in the cursor during the
* query operationNote: The column named _id is always implicitely
+ *
Note: The column named _id is always implicitly
* selected.
The <bind /> tag supports the following attributes:
android:from: The name of the column to bind from.
- * This attribute is mandatory.@ which are not used to reference resources
+ * should be backslash protected as in \@.
* android:to: The id of the view to bind to. This attribute is mandatory.android:as: The data type
* of the binding. This attribute is mandatory.In addition, a <bind /> can contain zero or more instances of
- * data transformations chilren
+ * data transformations children
* tags.
drawable: The content of the column is interpreted as a resource id to a
* drawable and must be bound to an {@link android.widget.ImageView}tag: 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.android:withClass: A fully qualified class name corresponding to an
* implementation of {@link android.widget.Adapters.CursorTransformation}. Custom
- * transformationscannot be used with
+ * transformations cannot be used with
* {@link android.content.Context#isRestricted() restricted contexts}, for instance in
* an app widget This attribute is mandatory if android:withExpression is
* not specifiedLoads the {@link android.widget.CursorAdapter} defined in the specified
* XML resource. The content of the adapter is loaded from the specified cursor.
@@ -488,7 +492,7 @@ public class Adapters {
* @return An instance of {@link android.widget.BaseAdapter}.
*/
private static BaseAdapter loadAdapter(Context context, int id, String assertName,
- Object... parameters) {
+ Object... parameters) {
XmlResourceParser parser = null;
try {
@@ -498,13 +502,13 @@ public class Adapters {
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf = new Resources.NotFoundException(
"Can't load adapter resource ID " +
- context.getResources().getResourceEntryName(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));
+ context.getResources().getResourceEntryName(id));
rnf.initCause(ex);
throw rnf;
} finally {
@@ -524,9 +528,9 @@ public class Adapters {
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();
@@ -543,7 +547,7 @@ public class Adapters {
throw new IllegalArgumentException("The adapter defined in " +
c.getResources().getResourceEntryName(id) + " must be a <" + name + " />");
}
-
+
if (ADAPTER_CURSOR.equals(name)) {
adapter = createCursorAdapter(c, parser, attrs, id, parameters);
} else {
@@ -551,7 +555,7 @@ public class Adapters {
" in " + c.getResources().getResourceEntryName(id));
}
}
-
+
return adapter;
}
@@ -575,6 +579,7 @@ public class Adapters {
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";
@@ -605,45 +610,45 @@ public class Adapters {
}
public XmlCursorAdapter parse(Object[] parameters)
- throws IOException, XmlPullParserException {
+ throws IOException, XmlPullParserException {
Resources resources = mResources;
- TypedArray a = resources.obtainAttributes(mAttrs, styleable.CursorAdapter);
-
- String uri = a.getString(styleable.CursorAdapter_uri);
- String selection = a.getString(styleable.CursorAdapter_selection);
- String sortOrder = a.getString(styleable.CursorAdapter_sortOrder);
- int layout = a.getResourceId(styleable.CursorAdapter_layout, 0);
+ TypedArray a = resources.obtainAttributes(mAttrs, android.R.styleable.CursorAdapter);
+
+ String uri = a.getString(android.R.styleable.CursorAdapter_uri);
+ String selection = a.getString(android.R.styleable.CursorAdapter_selection);
+ String sortOrder = a.getString(android.R.styleable.CursorAdapter_sortOrder);
+ int layout = a.getResourceId(android.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));
+ 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++) {
@@ -661,70 +666,73 @@ public class Adapters {
return new XmlCursorAdapter(mContext, layout, uri, fromArray, toArray, selection,
selectionArgs, sortOrder, mBinders);
}
-
+
private void parseSelectTag() {
- TypedArray a = mResources.obtainAttributes(mAttrs, styleable.CursorAdapter_SelectItem);
-
- String fromName = a.getString(styleable.CursorAdapter_SelectItem_column);
+ TypedArray a = mResources.obtainAttributes(mAttrs,
+ android.R.styleable.CursorAdapter_SelectItem);
+
+ String fromName = a.getString(android.R.styleable.CursorAdapter_SelectItem_column);
if (fromName == null) {
throw new IllegalArgumentException("A select item in " +
- mResources.getResourceEntryName(mId) + " does not have a 'column' attribute");
+ 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, styleable.CursorAdapter_BindItem);
-
- String fromName = a.getString(styleable.CursorAdapter_BindItem_from);
+ TypedArray a = resources.obtainAttributes(mAttrs,
+ android.R.styleable.CursorAdapter_BindItem);
+
+ String fromName = a.getString(android.R.styleable.CursorAdapter_BindItem_from);
if (fromName == null) {
throw new IllegalArgumentException("A bind item in " +
- resources.getResourceEntryName(mId) + " does not have a 'from' attribute");
+ resources.getResourceEntryName(mId) + " does not have a 'from' attribute");
}
-
- int toName = a.getResourceId(styleable.CursorAdapter_BindItem_to, 0);
+
+ int toName = a.getResourceId(android.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");
+ resources.getResourceEntryName(mId) + " does not have a 'to' attribute");
}
-
- String asType = a.getString(styleable.CursorAdapter_BindItem_as);
+
+ String asType = a.getString(android.R.styleable.CursorAdapter_BindItem_as);
if (asType == null) {
throw new IllegalArgumentException("A bind item in " +
- resources.getResourceEntryName(mId) + " does not have an 'as' attribute");
+ 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)) {
@@ -734,12 +742,14 @@ public class Adapters {
findMap(((MapTransformation) transformation), isDrawable);
} else {
throw new RuntimeException("Unknown tag name " + parser.getName() + " in " +
- context.getResources().getResourceEntryName(mId));
+ 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)) {
@@ -763,19 +773,19 @@ public class Adapters {
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot instanciate binder type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Cannot instanciate binder type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Cannot instanciate binder type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instanciate binder type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot instanciate binder type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
}
return null;
@@ -784,26 +794,30 @@ public class Adapters {
private void findMap(MapTransformation transformation, boolean drawable) {
Resources resources = mResources;
- TypedArray a = resources.obtainAttributes(mAttrs, styleable.CursorAdapter_MapItem);
+ TypedArray a = resources.obtainAttributes(mAttrs,
+ android.R.styleable.CursorAdapter_MapItem);
- String from = a.getString(styleable.CursorAdapter_MapItem_fromValue);
+ String from = a.getString(android.R.styleable.CursorAdapter_MapItem_fromValue);
if (from == null) {
throw new IllegalArgumentException("A map item in " +
- resources.getResourceEntryName(mId) + " does not have a 'fromValue' attribute");
+ resources.getResourceEntryName(mId) +
+ " does not have a 'fromValue' attribute");
}
if (!drawable) {
- String to = a.getString(styleable.CursorAdapter_MapItem_toValue);
+ String to = a.getString(android.R.styleable.CursorAdapter_MapItem_toValue);
if (to == null) {
throw new IllegalArgumentException("A map item in " +
- resources.getResourceEntryName(mId) + " does not have a 'toValue' attribute");
+ resources.getResourceEntryName(mId) +
+ " does not have a 'toValue' attribute");
}
transformation.addStringMapping(from, to);
} else {
- int to = a.getResourceId(styleable.CursorAdapter_MapItem_toValue, 0);
+ int to = a.getResourceId(android.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");
+ resources.getResourceEntryName(mId) +
+ " does not have a 'toValue' attribute");
}
transformation.addResourceMapping(from, to);
}
@@ -814,40 +828,41 @@ public class Adapters {
private CursorTransformation findTransformation() {
Resources resources = mResources;
CursorTransformation transformation = null;
- TypedArray a = resources.obtainAttributes(mAttrs, styleable.CursorAdapter_TransformItem);
-
- String className = a.getString(styleable.CursorAdapter_TransformItem_withClass);
+ TypedArray a = resources.obtainAttributes(mAttrs,
+ android.R.styleable.CursorAdapter_TransformItem);
+
+ String className = a.getString(android.R.styleable.CursorAdapter_TransformItem_withClass);
if (className == null) {
String expression = a.getString(
- styleable.CursorAdapter_TransformItem_withExpression);
+ android.R.styleable.CursorAdapter_TransformItem_withExpression);
transformation = createExpressionTransformation(expression);
} else if (!mContext.isRestricted()) {
try {
- final Class> klass = Class.forName(className, true, mContext.getClassLoader());
- if (CursorTransformation.class.isAssignableFrom(klass)) {
- final Constructor> c = klass.getDeclaredConstructor(Context.class);
+ 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);
+ mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Cannot instanciate transform type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Cannot instanciate transform type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instanciate transform type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+ mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot instanciate transform type in " +
- mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+ 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 " +
@@ -877,7 +892,6 @@ public class Adapters {
* of a SimpleCursorAdapter. The main difference is the ability to handle CursorBinders.
*/
private static class XmlCursorAdapter extends SimpleCursorAdapter implements ManagedAdapter {
- private final Context mContext;
private String mUri;
private final String mSelection;
private final String[] mSelectionArgs;
@@ -911,14 +925,14 @@ public class Adapters {
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) {
@@ -926,7 +940,7 @@ public class Adapters {
}
}
}
-
+
public void load() {
if (mUri != null) {
mLoadTask = new QueryTask().execute();
@@ -984,16 +998,16 @@ public class Adapters {
/**
* An expression transformation is a simple template based replacement utility.
- * In an expression, each segment of the form {(^[}]+)} is replaced
+ * In an expression, each segment of the form {([^}]+)} 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);
}
@@ -1033,7 +1047,7 @@ public class Adapters {
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) {
@@ -1042,13 +1056,13 @@ public class Adapters {
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;
@@ -1061,7 +1075,7 @@ public class Adapters {
return mConstant;
}
}
-
+
static class ColumnExpressionNode extends ExpressionNode {
private final String mColumnName;
private Cursor mSignature;
@@ -1134,8 +1148,12 @@ public class Adapters {
@Override
public boolean bind(View view, Cursor cursor, int columnIndex) {
- ((TextView) view).setText(mTransformation.transform(cursor, columnIndex));
- return true;
+ if (view instanceof TextView) {
+ final String text = mTransformation.transform(cursor, columnIndex);
+ ((TextView) view).setText(text);
+ return true;
+ }
+ return false;
}
}
@@ -1149,8 +1167,25 @@ public class Adapters {
@Override
public boolean bind(View view, Cursor cursor, int columnIndex) {
- final byte[] data = cursor.getBlob(columnIndex);
- ((ImageView) view).setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
+ 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;
}
}
@@ -1165,9 +1200,12 @@ public class Adapters {
@Override
public boolean bind(View view, Cursor cursor, int columnIndex) {
- ((ImageView) view).setImageURI(Uri.parse(
- mTransformation.transform(cursor, columnIndex)));
- return true;
+ if (view instanceof ImageView) {
+ ((ImageView) view).setImageURI(Uri.parse(
+ mTransformation.transform(cursor, columnIndex)));
+ return true;
+ }
+ return false;
}
}
@@ -1181,11 +1219,14 @@ public class Adapters {
@Override
public boolean bind(View view, Cursor cursor, int columnIndex) {
- final int resource = mTransformation.transformToResource(cursor, columnIndex);
- if (resource == 0) return false;
+ if (view instanceof ImageView) {
+ final int resource = mTransformation.transformToResource(cursor, columnIndex);
+ if (resource == 0) return false;
- ((ImageView) view).setImageResource(resource);
- return true;
+ ((ImageView) view).setImageResource(resource);
+ return true;
+ }
+ return false;
}
}
}
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 2313f4cb30a48..5394530c08828 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -81,7 +81,7 @@ public class BitmapFactory {
/**
* The pixel density to use for the bitmap. This will always result
* in the returned bitmap having a density set for it (see
- * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)). In addition,
+ * {@link Bitmap#setDensity(int) Bitmap.setDensity(int))}. In addition,
* if {@link #inScaled} is set (which it is by default} and this
* density does not match {@link #inTargetDensity}, then the bitmap
* will be scaled to the target density before being returned.
@@ -507,9 +507,7 @@ public class BitmapFactory {
*
* @param is The input stream that holds the raw data to be decoded into a
* bitmap.
- * @return The decoded bitmap, or null if the image data could not be
- * decoded, or, if opts is non-null, if opts requested only the
- * size be returned (in opts.outWidth and opts.outHeight)
+ * @return The decoded bitmap, or null if the image data could not be decoded.
*/
public static Bitmap decodeStream(InputStream is) {
return decodeStream(is, null, null);