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 org.xml.sax.helpers.DefaultHandler; 20 import org.xmlpull.v1.XmlPullParser; 21 import org.xmlpull.v1.XmlPullParserException; 22 import org.xmlpull.v1.XmlPullParserFactory; 23 24 import android.content.ContentValues; 25 import android.net.Uri; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.util.Vector; 30 31 /** 32 * RSSPullParser reads an RSS feed from the Picasa featured pictures site. It uses 33 * several packages from the widely-known XMLPull API. 34 * 35 */ 36 public class RSSPullParser extends DefaultHandler { 37 // Global constants 38 39 // An attribute value indicating that the element contains media content 40 private static final String CONTENT = "media:content"; 41 42 // An attribute value indicating that the element contains a thumbnail 43 private static final String THUMBNAIL = "media:thumbnail"; 44 45 // An attribute value indicating that the element contains an item 46 private static final String ITEM = "item"; 47 48 // Sets the initial size of the vector that stores data. 49 private static final int VECTOR_INITIAL_SIZE = 500; 50 51 // Storage for a single ContentValues for image data 52 private static ContentValues mImage; 53 54 // A vector that will contain all of the images 55 private Vector<ContentValues> mImages; 56 57 /** 58 * A getter that returns the image data Vector 59 * @return A Vector containing all of the image data retrieved by the parser 60 */ getImages()61 public Vector<ContentValues> getImages() { 62 return mImages; 63 } 64 /** 65 * This method parses XML in an input stream and stores parts of the data in memory 66 * 67 * @param inputStream a stream of data containing XML elements, usually a RSS feed 68 * @param progressNotifier a helper class for sending status and logs 69 * @throws XmlPullParserException defined by XMLPullParser; thrown if the thread is cancelled. 70 * @throws IOException thrown if an IO error occurs during parsing 71 */ parseXml(InputStream inputStream, BroadcastNotifier progressNotifier)72 public void parseXml(InputStream inputStream, 73 BroadcastNotifier progressNotifier) 74 throws XmlPullParserException, IOException { 75 76 // Instantiates a parser factory 77 XmlPullParserFactory localXmlPullParserFactory = XmlPullParserFactory 78 .newInstance(); 79 80 // Turns off namespace handling for the XML input 81 localXmlPullParserFactory.setNamespaceAware(false); 82 83 // Instantiates a new pull parser 84 XmlPullParser localXmlPullParser = localXmlPullParserFactory 85 .newPullParser(); 86 87 // Sets the parser's input stream 88 localXmlPullParser.setInput(inputStream, null); 89 90 // Gets the first event in the input sream 91 int eventType = localXmlPullParser.getEventType(); 92 93 // Sets the number of images read to 1 94 int imageCount = 1; 95 96 // Returns if the current event (state) is not START_DOCUMENT 97 if (eventType != XmlPullParser.START_DOCUMENT) { 98 99 throw new XmlPullParserException("Invalid RSS"); 100 101 } 102 103 // Creates a new store for image URL data 104 mImages = new Vector<ContentValues>(VECTOR_INITIAL_SIZE); 105 106 // Loops indefinitely. The exit occurs if there are no more URLs to process 107 while (true) { 108 109 // Gets the next event in the input stream 110 int nextEvent = localXmlPullParser.next(); 111 112 // If the current thread is interrupted, throws an exception and returns 113 if (Thread.currentThread().isInterrupted()) { 114 115 throw new XmlPullParserException("Cancelled"); 116 117 // At the end of the feed, exits the loop 118 } else if (nextEvent == XmlPullParser.END_DOCUMENT) { 119 break; 120 121 // At the beginning of the feed, skips the event and continues 122 } else if (nextEvent == XmlPullParser.START_DOCUMENT) { 123 continue; 124 125 // At the start of a tag, gets the tag's name 126 } else if (nextEvent == XmlPullParser.START_TAG) { 127 String eventName = localXmlPullParser.getName(); 128 129 /* 130 * If this is the start of an individual item, logs it and creates a new 131 * ContentValues 132 */ 133 if (eventName.equalsIgnoreCase(ITEM)) { 134 135 mImage = new ContentValues(); 136 137 // If this isn't an item, then checks for other options 138 } else { 139 140 // Defines keys to store the column names 141 String imageUrlKey; 142 String imageNameKey; 143 144 // Defines a place to store the filename of a URL, 145 String fileName; 146 147 // If it's CONTENT 148 if (eventName.equalsIgnoreCase(CONTENT)) { 149 150 // Stores the image URL and image name column names as keys 151 imageUrlKey = DataProviderContract.IMAGE_URL_COLUMN; 152 imageNameKey = DataProviderContract.IMAGE_PICTURENAME_COLUMN; 153 154 // If it's a THUMBNAIL 155 } else if (eventName.equalsIgnoreCase(THUMBNAIL)) { 156 157 // Stores the thumbnail URL and thumbnail name column names as keys 158 imageUrlKey = DataProviderContract.IMAGE_THUMBURL_COLUMN; 159 imageNameKey = DataProviderContract.IMAGE_THUMBNAME_COLUMN; 160 161 // Otherwise it's some other event that isn't important 162 } else { 163 continue; 164 } 165 166 // It's not an ITEM. Gets the URL attribute from the event 167 String urlValue = localXmlPullParser.getAttributeValue(null, "url"); 168 169 // If the value is null, exits 170 if (urlValue == null) 171 break; 172 173 // Puts the URL and the key into the ContentValues 174 mImage.put(imageUrlKey, urlValue); 175 176 // Gets the filename of the URL and puts it into the ContentValues 177 fileName = Uri.parse(urlValue).getLastPathSegment(); 178 mImage.put(imageNameKey, fileName); 179 } 180 } 181 /* 182 * If it's not an ITEM, and it is an END_TAG, and the current event is an ITEM, and 183 * there is data in the current ContentValues 184 */ 185 else if ((nextEvent == XmlPullParser.END_TAG) 186 && (localXmlPullParser.getName().equalsIgnoreCase(ITEM)) 187 && (mImage != null)) { 188 189 // Adds the current ContentValues to the ContentValues storage 190 mImages.add(mImage); 191 192 // Logs progress 193 progressNotifier.notifyProgress("Parsed Image[" + imageCount + "]:" 194 + mImage.getAsString(DataProviderContract.IMAGE_URL_COLUMN)); 195 196 // Clears out the current ContentValues 197 mImage = null; 198 199 // Increments the count of the number of images stored. 200 imageCount++; 201 } 202 } 203 } 204 } 205