private void processFeed(
      ArticleDao articleDao,
      InputStream is,
      FeedType feedType,
      UpdateType updateType,
      Integer latestID)
      throws XmlPullParserException, IOException {
    // TODO: use parser.require() all over the place?

    XmlPullParser parser = Xml.newPullParser();
    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
    parser.setInput(is, null);

    parser.nextTag();
    parser.require(XmlPullParser.START_TAG, null, "rss");

    goToElement(parser, "channel", true);
    parser.next();

    DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);

    while (parser.next() != XmlPullParser.END_DOCUMENT) {
      if (parser.getEventType() != XmlPullParser.START_TAG) {
        continue;
      }

      if ("item".equals(parser.getName())) {
        if (feedType == FeedType.Main
            || feedType == FeedType.Archive
            || (feedType == FeedType.Favorite && updateType == UpdateType.Full)) {
          // Main: Full, Fast
          // Archive: Full, Fast
          // Favorite: Full

          Item item = parseItem(parser);

          Integer id = getIDFromURL(item.sourceUrl);

          if (updateType == UpdateType.Fast && latestID != null && id != null && latestID >= id)
            break;

          Article article =
              articleDao
                  .queryBuilder()
                  .where(ArticleDao.Properties.ArticleId.eq(id))
                  .build()
                  .unique();

          boolean existing = true;
          if (article == null) {
            article = new Article(null);
            existing = false;
          }

          article.setTitle(item.title);
          article.setContent(item.description);
          article.setUrl(item.link);
          article.setArticleId(id);
          try {
            article.setUpdateDate(dateFormat.parse(item.pubDate));
          } catch (ParseException e) {
            e.printStackTrace();
          }
          if (existing) {
            if (feedType == FeedType.Archive) {
              article.setArchive(true);
            } else if (feedType == FeedType.Favorite) {
              article.setFavorite(true);
            }
          } else {
            article.setArchive(feedType == FeedType.Archive);
            article.setFavorite(feedType == FeedType.Favorite);
          }

          articleDao.insertOrReplace(article);
        } else if (feedType == FeedType.Favorite) {
          // Favorite: Fast (ONLY applicable if Main and Archive feeds are up to date)
          // probably a bit faster then "Favorite: Full"

          Integer id = parseItemID(parser);
          if (id == null) continue;

          Article article =
              articleDao
                  .queryBuilder()
                  .where(ArticleDao.Properties.ArticleId.eq(id))
                  .build()
                  .unique();

          if (article.getFavorite() != null && article.getFavorite()) continue;

          article.setFavorite(true);

          articleDao.update(article);
        }
      } else {
        skipElement(parser);
      }
    }
  }