New XmlAdapter sample example.

This sample illustrate the use of the news Xml Adapters definition, as well
as the use on the new XmlDocumentProvider class.

Change-Id: Ie9738b5f0a0f00df2af643890a48573bace19311
This commit is contained in:
Gilles Debunne
2010-06-17 15:02:41 -07:00
parent b606d1bea2
commit 55f4a60aa0
17 changed files with 817 additions and 8 deletions

View File

@@ -109,6 +109,7 @@ development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLiveWa
development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib
development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain
development/samples/XmlAdapters samples/${PLATFORM_NAME}/XmlAdapters
# dx # dx
bin/dx platforms/${PLATFORM_NAME}/tools/dx bin/dx platforms/${PLATFORM_NAME}/tools/dx

View File

@@ -17,15 +17,40 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.xmladapters"> package="com.example.android.xmladapters">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<application android:label="@string/app_name"> <application android:label="@string/app_name">
<activity android:name="ContactsListActivity"> <activity android:name="ContactsListActivity"
android:label="@string/contacts_list_activity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="PhotosListActivity"
android:label="@string/photos_list_activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="RssReaderActivity"
android:label="@string/rss_reader_activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider android:name="android.content.XmlDocumentProvider"
android:authorities="xmldocument" />
</application> </application>
</manifest> </manifest>

View File

@@ -0,0 +1,31 @@
<p>This sample demonstrates the use of XML adapters.</p>
<p>An XML Adapter is an XML file which defines the bindings between the data
retrieved from a
<a href="../../../reference/android/content/ContentProvider.html"><code>ContentProvider</code></a>
and the different views of a layout. They are provided by the
<a href="../../../reference/android/widget/Adapters.html"><code>Adapters</code></a>
class.</p>
Three list activities are provided which illustrate this:
<ul>
<li><a href="src/com/example/android/xmladapters/ContactsListActivity.html"><strong>
ContactsListActivity</strong></a> uses the device's Contacts provider as its input source.
Contacts with a phone number are displayed, their photo being retrieved by a dedicated
<a href="src/com/example/android/xmladapters/ContactPhotoBinder.html"><code>ContactPhotoBinder
</code></a>.</li>
<li><a href="src/com/example/android/xmladapters/PhotosListActivity.html"><strong>
PhotosListActivity</strong></a> retrieves an RSS photo feed and displays the images and their
titles. The images are downloaded from the URL found in the feed using the
<a href="src/com/example/android/xmladapters/ImageDownloader.html"><code>ImageDownloader</code>
</a> helper class.</li>
<li><a href="src/com/example/android/xmladapters/RssReaderActivity.html"><strong>
RssReaderActivity</strong></a> also displays items extracted from an RSS feed. An additional
<a href="src/com/example/android/xmladapters/UrlIntentListener.html"><code>UrlIntentListener
</code></a> is used to open a browser when one of the news item is tapped.</li>
</ul>
<img alt="XmlPhotosAdapter" src="../images/XmlPhotosAdapter.png" />
<img alt="XmlRssReader" src="../images/XmlRssReader.png" />

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<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>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<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>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<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>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<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>

View File

@@ -15,6 +15,12 @@
--> -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Xml Contacts Adapter</string> <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_contacts">No contacts available</string>
<string name="no_photos">Loading photos...</string>
<string name="no_rss_feed">Loading RSS feed...</string>
</resources> </resources>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:selection="/feed/entry"
android:layout="@layout/photo_item">
<bind android:from="/summary" android:to="@id/title" android:as="string" />
<bind android:from="/media:group/media:thumbnail\@url" android:to="@id/photo"
android:as="com.example.android.xmladapters.UrlImageBinder" />
</cursor-adapter>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:selection="/rss/channel/item"
android:layout="@layout/rss_feed_item">
<bind android:from="/title" android:to="@id/title" android:as="string" />
<bind android:from="/media:content@url" android:to="@id/image"
android:as="com.example.android.xmladapters.UrlImageBinder"/>
<bind android:from="/media:description" android:to="@id/description" android:as="string" />
<bind android:from="/guid" android:to="@id/item_layout" android:as="tag" />
<bind android:from="/pubDate" android:to="@id/date" android:as="string">
<transform android:withExpression="Published on {/pubDate}." />
</bind>
</cursor-adapter>

View File

@@ -35,7 +35,7 @@ import java.util.HashMap;
/** /**
* This custom cursor binder is used by the adapter defined in res/xml to * This custom cursor binder is used by the adapter defined in res/xml to
* bin contacts photos to their respective list item. This binder simply * 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 * 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 * photo as a compound drawable on the TextView used to display the contact's
* name. * name.
@@ -54,7 +54,7 @@ public class ContactPhotoBinder extends Adapters.CursorBinder {
mResources = mContext.getResources(); mResources = mContext.getResources();
// Default picture used when a contact does not provide one // Default picture used when a contact does not provide one
mDefault = mResources.getDrawable(R.drawable.ic_contact_picture); mDefault = mResources.getDrawable(R.drawable.ic_contact_picture);
// Cache used to avoid requerying contacts photos every time // Cache used to avoid re-querying contacts photos every time
mCache = new HashMap<Long, Drawable>(); mCache = new HashMap<Long, Drawable>();
// Compute the size of the photo based on the display's density // Compute the size of the photo based on the display's density
mPhotoSize = (int) (PHOTO_SIZE_DIP * mResources.getDisplayMetrics().density + 0.5f); mPhotoSize = (int) (PHOTO_SIZE_DIP * mResources.getDisplayMetrics().density + 0.5f);
@@ -76,6 +76,7 @@ public class ContactPhotoBinder extends Adapters.CursorBinder {
// Creates the drawable for the contact's photo or use our fallback drawable // Creates the drawable for the contact's photo or use our fallback drawable
if (stream != null) { if (stream != null) {
// decoding the bitmap could be done in a worker thread too.
Bitmap bitmap = BitmapFactory.decodeStream(stream); Bitmap bitmap = BitmapFactory.decodeStream(stream);
d = new BitmapDrawable(mResources, bitmap); d = new BitmapDrawable(mResources, bitmap);
} else { } else {

View File

@@ -0,0 +1,346 @@
/*
* 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 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.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);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
final HttpEntity entity = response.getEntity();
if (entity != null) {
final InputStream inputStream = entity.getContent();
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
final OutputStream out =
new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
copy(inputStream, out);
out.flush();
out.close();
inputStream.close();
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;
}
}
} 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(0);
bitmapDownloaderTaskReference =
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 com.example.android.xmladapters;
import android.app.ListActivity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Adapters;
/**
* 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)));
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 com.example.android.xmladapters;
import android.app.ListActivity;
import android.content.XmlDocumentProvider;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Adapters;
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());
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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 com.example.android.xmladapters;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.widget.Adapters;
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;
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 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);
}
}