• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.threadsample;
18 
19 import android.app.IntentService;
20 import android.content.ContentValues;
21 import android.content.Intent;
22 import android.database.Cursor;
23 
24 import org.apache.http.HttpStatus;
25 import org.xmlpull.v1.XmlPullParserException;
26 
27 import java.io.IOException;
28 import java.net.HttpURLConnection;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.net.URLConnection;
32 import java.util.Date;
33 import java.util.Vector;
34 
35 /**
36  * This service pulls RSS content from a web site URL contained in the incoming Intent (see
37  * onHandleIntent()). As it runs, it broadcasts its status using LocalBroadcastManager; any
38  * component that wants to see the status should implement a subclass of BroadcastReceiver and
39  * register to receive broadcast Intents with category = CATEGORY_DEFAULT and action
40  * Constants.BROADCAST_ACTION.
41  *
42  */
43 public class RSSPullService extends IntentService {
44     // Used to write to the system log from this class.
45     public static final String LOG_TAG = "RSSPullService";
46 
47     // Defines and instantiates an object for handling status updates.
48     private BroadcastNotifier mBroadcaster = new BroadcastNotifier(this);
49 
50     /**
51      * An IntentService must always have a constructor that calls the super constructor. The
52      * string supplied to the super constructor is used to give a name to the IntentService's
53      * background thread.
54      */
RSSPullService()55     public RSSPullService() {
56 
57         super("RSSPullService");
58     }
59 
60     /**
61      * In an IntentService, onHandleIntent is run on a background thread.  As it
62      * runs, it broadcasts its current status using the LocalBroadcastManager.
63      * @param workIntent The Intent that starts the IntentService. This Intent contains the
64      * URL of the web site from which the RSS parser gets data.
65      */
66     @Override
onHandleIntent(Intent workIntent)67     protected void onHandleIntent(Intent workIntent) {
68         // Gets a URL to read from the incoming Intent's "data" value
69         String localUrlString = workIntent.getDataString();
70 
71         // Creates a projection to use in querying the modification date table in the provider.
72         final String[] dateProjection = new String[]
73         {
74             DataProviderContract.ROW_ID,
75             DataProviderContract.DATA_DATE_COLUMN
76         };
77 
78         // A URL that's local to this method
79         URL localURL;
80 
81         // A cursor that's local to this method.
82         Cursor cursor = null;
83 
84         /*
85          * A block that tries to connect to the Picasa featured picture URL passed as the "data"
86          * value in the incoming Intent. The block throws exceptions (see the end of the block).
87          */
88         try {
89 
90             // Convert the incoming data string to a URL.
91             localURL = new URL(localUrlString);
92 
93             /*
94              * Tries to open a connection to the URL. If an IO error occurs, this throws an
95              * IOException
96              */
97             URLConnection localURLConnection = localURL.openConnection();
98 
99             // If the connection is an HTTP connection, continue
100             if ((localURLConnection instanceof HttpURLConnection)) {
101 
102                 // Broadcasts an Intent indicating that processing has started.
103                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
104 
105                 // Casts the connection to a HTTP connection
106                 HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;
107 
108                 // Sets the user agent for this request.
109                 localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
110 
111                 /*
112                  * Queries the content provider to see if this URL was read previously, and when.
113                  * The content provider throws an exception if the URI is invalid.
114                  */
115                 cursor = getContentResolver().query(
116                         DataProviderContract.DATE_TABLE_CONTENTURI,
117                         dateProjection,
118                         null,
119                         null,
120                         null);
121 
122                 // Flag to indicate that new metadata was retrieved
123                 boolean newMetadataRetrieved;
124 
125                 /*
126                  * Tests to see if the table contains a modification date for the URL
127                  */
128                 if (null != cursor && cursor.moveToFirst()) {
129 
130                     // Find the URL's last modified date in the content provider
131                     long storedModifiedDate =
132                             cursor.getLong(cursor.getColumnIndex(
133                                     DataProviderContract.DATA_DATE_COLUMN)
134                             )
135                     ;
136 
137                     /*
138                      * If the modified date isn't 0, sets another request property to ensure that
139                      * data is only downloaded if it has changed since the last recorded
140                      * modification date. Formats the date according to the RFC1123 format.
141                      */
142                     if (0 != storedModifiedDate) {
143                         localHttpURLConnection.setRequestProperty(
144                                 "If-Modified-Since",
145                                 org.apache.http.impl.cookie.DateUtils.formatDate(
146                                         new Date(storedModifiedDate),
147                                         org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
148                     }
149 
150                     // Marks that new metadata does not need to be retrieved
151                     newMetadataRetrieved = false;
152 
153                 } else {
154 
155                     /*
156                      * No modification date was found for the URL, so newmetadata has to be
157                      * retrieved.
158                      */
159                     newMetadataRetrieved = true;
160 
161                 }
162 
163                 // Reports that the service is about to connect to the RSS feed
164                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);
165 
166                 // Gets a response code from the RSS server
167                 int responseCode = localHttpURLConnection.getResponseCode();
168 
169                 switch (responseCode) {
170 
171                     // If the response is OK
172                     case HttpStatus.SC_OK:
173 
174                         // Gets the last modified data for the URL
175                         long lastModifiedDate = localHttpURLConnection.getLastModified();
176 
177                         // Reports that the service is parsing
178                         mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);
179 
180                         /*
181                          * Instantiates a pull parser and uses it to parse XML from the RSS feed.
182                          * The mBroadcaster argument send a broadcaster utility object to the
183                          * parser.
184                          */
185                         RSSPullParser localPicasaPullParser = new RSSPullParser();
186 
187                         localPicasaPullParser.parseXml(
188                             localURLConnection.getInputStream(),
189                             mBroadcaster);
190 
191                         // Reports that the service is now writing data to the content provider.
192                         mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);
193 
194                         // Gets image data from the parser
195                         Vector<ContentValues> imageValues = localPicasaPullParser.getImages();
196 
197                         // Stores the number of images
198                         int imageVectorSize = imageValues.size();
199 
200                         // Creates one ContentValues for each image
201                         ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];
202 
203                         imageValuesArray = imageValues.toArray(imageValuesArray);
204 
205                         /*
206                          * Stores the image data in the content provider. The content provider
207                          * throws an exception if the URI is invalid.
208                          */
209                         getContentResolver().bulkInsert(
210                                 DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);
211 
212                         // Creates another ContentValues for storing date information
213                         ContentValues dateValues = new ContentValues();
214 
215                         // Adds the URL's last modified date to the ContentValues
216                         dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);
217 
218                         if (newMetadataRetrieved) {
219 
220                             // No previous metadata existed, so insert the data
221                             getContentResolver().insert(
222                                 DataProviderContract.DATE_TABLE_CONTENTURI,
223                                 dateValues
224                             );
225 
226                         } else {
227 
228                             // Previous metadata existed, so update it.
229                             getContentResolver().update(
230                                     DataProviderContract.DATE_TABLE_CONTENTURI,
231                                     dateValues,
232                                     DataProviderContract.ROW_ID + "=" +
233                                             cursor.getString(cursor.getColumnIndex(
234                                                             DataProviderContract.ROW_ID)), null);
235                         }
236                         break;
237 
238                 }
239 
240                 // Reports that the feed retrieval is complete.
241                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
242             }
243 
244         // Handles possible exceptions
245         } catch (MalformedURLException localMalformedURLException) {
246 
247             localMalformedURLException.printStackTrace();
248 
249         } catch (IOException localIOException) {
250 
251             localIOException.printStackTrace();
252 
253         } catch (XmlPullParserException localXmlPullParserException) {
254 
255             localXmlPullParserException.printStackTrace();
256 
257         } finally {
258 
259             // If an exception occurred, close the cursor to prevent memory leaks.
260             if (null != cursor) {
261                 cursor.close();
262             }
263         }
264     }
265 
266 }
267