blob: b8b3e2681288e2660da9f5835c0079e37801bf1d [file] [log] [blame]
/*
* Copyright (C) 2012 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.threadsample;
import android.app.IntentService;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import org.apache.http.HttpStatus;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.Vector;
/**
* This service pulls RSS content from a web site URL contained in the incoming Intent (see
* onHandleIntent()). As it runs, it broadcasts its status using LocalBroadcastManager; any
* component that wants to see the status should implement a subclass of BroadcastReceiver and
* register to receive broadcast Intents with category = CATEGORY_DEFAULT and action
* Constants.BROADCAST_ACTION.
*
*/
public class RSSPullService extends IntentService {
// Used to write to the system log from this class.
public static final String LOG_TAG = "RSSPullService";
// Defines and instantiates an object for handling status updates.
private BroadcastNotifier mBroadcaster = new BroadcastNotifier(this);
/**
* An IntentService must always have a constructor that calls the super constructor. The
* string supplied to the super constructor is used to give a name to the IntentService's
* background thread.
*/
public RSSPullService() {
super("RSSPullService");
}
/**
* In an IntentService, onHandleIntent is run on a background thread. As it
* runs, it broadcasts its current status using the LocalBroadcastManager.
* @param workIntent The Intent that starts the IntentService. This Intent contains the
* URL of the web site from which the RSS parser gets data.
*/
@Override
protected void onHandleIntent(Intent workIntent) {
// Gets a URL to read from the incoming Intent's "data" value
String localUrlString = workIntent.getDataString();
// Creates a projection to use in querying the modification date table in the provider.
final String[] dateProjection = new String[]
{
DataProviderContract.ROW_ID,
DataProviderContract.DATA_DATE_COLUMN
};
// A URL that's local to this method
URL localURL;
// A cursor that's local to this method.
Cursor cursor = null;
/*
* A block that tries to connect to the Picasa featured picture URL passed as the "data"
* value in the incoming Intent. The block throws exceptions (see the end of the block).
*/
try {
// Convert the incoming data string to a URL.
localURL = new URL(localUrlString);
/*
* Tries to open a connection to the URL. If an IO error occurs, this throws an
* IOException
*/
URLConnection localURLConnection = localURL.openConnection();
// If the connection is an HTTP connection, continue
if ((localURLConnection instanceof HttpURLConnection)) {
// Broadcasts an Intent indicating that processing has started.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
// Casts the connection to a HTTP connection
HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;
// Sets the user agent for this request.
localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
/*
* Queries the content provider to see if this URL was read previously, and when.
* The content provider throws an exception if the URI is invalid.
*/
cursor = getContentResolver().query(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateProjection,
null,
null,
null);
// Flag to indicate that new metadata was retrieved
boolean newMetadataRetrieved;
/*
* Tests to see if the table contains a modification date for the URL
*/
if (null != cursor && cursor.moveToFirst()) {
// Find the URL's last modified date in the content provider
long storedModifiedDate =
cursor.getLong(cursor.getColumnIndex(
DataProviderContract.DATA_DATE_COLUMN)
)
;
/*
* If the modified date isn't 0, sets another request property to ensure that
* data is only downloaded if it has changed since the last recorded
* modification date. Formats the date according to the RFC1123 format.
*/
if (0 != storedModifiedDate) {
localHttpURLConnection.setRequestProperty(
"If-Modified-Since",
org.apache.http.impl.cookie.DateUtils.formatDate(
new Date(storedModifiedDate),
org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
}
// Marks that new metadata does not need to be retrieved
newMetadataRetrieved = false;
} else {
/*
* No modification date was found for the URL, so newmetadata has to be
* retrieved.
*/
newMetadataRetrieved = true;
}
// Reports that the service is about to connect to the RSS feed
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);
// Gets a response code from the RSS server
int responseCode = localHttpURLConnection.getResponseCode();
switch (responseCode) {
// If the response is OK
case HttpStatus.SC_OK:
// Gets the last modified data for the URL
long lastModifiedDate = localHttpURLConnection.getLastModified();
// Reports that the service is parsing
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);
/*
* Instantiates a pull parser and uses it to parse XML from the RSS feed.
* The mBroadcaster argument send a broadcaster utility object to the
* parser.
*/
RSSPullParser localPicasaPullParser = new RSSPullParser();
localPicasaPullParser.parseXml(
localURLConnection.getInputStream(),
mBroadcaster);
// Reports that the service is now writing data to the content provider.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);
// Gets image data from the parser
Vector<ContentValues> imageValues = localPicasaPullParser.getImages();
// Stores the number of images
int imageVectorSize = imageValues.size();
// Creates one ContentValues for each image
ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];
imageValuesArray = imageValues.toArray(imageValuesArray);
/*
* Stores the image data in the content provider. The content provider
* throws an exception if the URI is invalid.
*/
getContentResolver().bulkInsert(
DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);
// Creates another ContentValues for storing date information
ContentValues dateValues = new ContentValues();
// Adds the URL's last modified date to the ContentValues
dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);
if (newMetadataRetrieved) {
// No previous metadata existed, so insert the data
getContentResolver().insert(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateValues
);
} else {
// Previous metadata existed, so update it.
getContentResolver().update(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateValues,
DataProviderContract.ROW_ID + "=" +
cursor.getString(cursor.getColumnIndex(
DataProviderContract.ROW_ID)), null);
}
break;
}
// Reports that the feed retrieval is complete.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
}
// Handles possible exceptions
} catch (MalformedURLException localMalformedURLException) {
localMalformedURLException.printStackTrace();
} catch (IOException localIOException) {
localIOException.printStackTrace();
} catch (XmlPullParserException localXmlPullParserException) {
localXmlPullParserException.printStackTrace();
} finally {
// If an exception occurred, close the cursor to prevent memory leaks.
if (null != cursor) {
cursor.close();
}
}
}
}