Android Open Source - holoreader Refresh Feed Service






From Project

Back to project page holoreader.

License

The source code is released under:

GNU General Public License

If you think the Android project holoreader listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package de.hdodenhof.holoreader.services;
//from w  w  w.  j ava  2s.  co  m
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.TimeZone;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;

import com.commonsware.cwac.wakeful.WakefulIntentService;

import de.hdodenhof.holoreader.R;
import de.hdodenhof.holoreader.provider.RSSContentProvider;
import de.hdodenhof.holoreader.provider.SQLiteHelper;
import de.hdodenhof.holoreader.provider.SQLiteHelper.ArticleDAO;
import de.hdodenhof.holoreader.provider.SQLiteHelper.FeedDAO;

public class RefreshFeedService extends WakefulIntentService {

    @SuppressWarnings("unused")
    private static final String TAG = RefreshFeedService.class.getSimpleName();

    public static final String BROADCAST_REFRESHED = "de.hdodenhof.holoreader.FEEDS_REFRESHED";
    public static final String BROADCAST_REFRESHING = "de.hdodenhof.holoreader.FEEDS_REFRESHING";

    private static final String NO_ACTION = "no_action";
    private static final int KEEP_READ_ARTICLES_DAYS = 3;
    private static final int KEEP_UNREAD_ARTICLES_DAYS = 7;

    private static final int SUMMARY_MAXLENGTH = 250;
    private static final String DATE_FORMATS[] = { "EEE, dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss z", "yyyy-MM-dd'T'HH:mm:ssz",
            "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSSZ" };
    private SimpleDateFormat mSimpleDateFormats[] = new SimpleDateFormat[DATE_FORMATS.length];

    private ContentResolver mContentResolver;
    private SharedPreferences mSharedPrefs;
    private HashSet<Integer> mFeedsUpdating;

    public RefreshFeedService() {
        super("RefreshFeedService");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        for (int i = 0; i < DATE_FORMATS.length; i++) {
            mSimpleDateFormats[i] = new SimpleDateFormat(DATE_FORMATS[i], Locale.US);
            mSimpleDateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT"));
        }

        mContentResolver = getContentResolver();
        mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mFeedsUpdating = new HashSet<Integer>();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int feedID = intent.getIntExtra("feedid", -1);

        if (mFeedsUpdating.contains(feedID)) {
            intent.setAction(NO_ACTION);
        } else {
            if (mFeedsUpdating.size() == 0) {
                mSharedPrefs.edit().putBoolean("refreshing", true).commit();

                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction(BROADCAST_REFRESHING);
                LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
            }
            mFeedsUpdating.add(feedID);
        }

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void doWakefulWork(Intent intent) {
        if (intent.getAction() == NO_ACTION) {
            return;
        }
        int feedID = intent.getIntExtra("feedid", -1);

        ArrayList<ContentValues> contentValuesArrayList = new ArrayList<ContentValues>();

        boolean isArticle = false;
        boolean linkOverride = false;
        String title = null;
        String summary = null;
        String content = null;
        String guid = null;
        Date pubdate = null;
        Date updated = null;
        String link = null;

        try {
            deleteOldArticles(feedID);

            ArrayList<String> existingArticles = queryArticles(feedID);
            Date minimumDate = getMinimumDate(feedID);

            InputStream inputStream = getURLInputStream(queryURL(feedID));
            XmlPullParser pullParser = getParser(inputStream);

            int eventType = pullParser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    String currentTag = pullParser.getName();
                    String currentPrefix = pullParser.getPrefix() != null ? pullParser.getPrefix() : "";

                    if (isItemTag(currentTag, currentPrefix)) {
                        isArticle = true;
                    } else if (isTitleTag(currentTag, currentPrefix) && isArticle) {
                        title = safeNextText(pullParser);
                    } else if (isSummaryTag(currentTag, currentPrefix) && isArticle) {
                        summary = safeNextText(pullParser);
                    } else if (isContentTag(currentTag, currentPrefix) && isArticle) {
                        content = extractContent(pullParser);
                    } else if (isGuidTag(currentTag, currentPrefix) && isArticle) {
                        guid = safeNextText(pullParser);
                    } else if (isDateTag(currentTag, currentPrefix) && isArticle) {
                        pubdate = parsePubdate(safeNextText(pullParser));
                    } else if (isUpdatedTag(currentTag, currentPrefix) && isArticle) {
                        updated = parsePubdate(safeNextText(pullParser));
                    } else if (isLinkTag(currentTag, currentPrefix) && isArticle) {
                        if (!linkOverride) {
                            link = extractLink(pullParser);
                        }
                    } else if (isOrigLinkTag(currentTag, currentPrefix) && isArticle) {
                        link = safeNextText(pullParser);
                        linkOverride = true;
                    }

                } else if (eventType == XmlPullParser.END_TAG && isItemTag(pullParser.getName(), "")) {
                    pubdate = pubdate != null ? pubdate : updated;
                    guid = guid != null ? guid : link;

                    if (pubdate.before(minimumDate)) {
                        break;
                    }

                    if (!existingArticles.contains(guid)) {
                        ContentValues newArticle = prepareArticle(feedID, guid, link, pubdate, title, summary, content);
                        if (newArticle != null) {
                            contentValuesArrayList.add(newArticle);
                        }
                    }

                    isArticle = false;
                    title = null;
                    summary = null;
                    content = null;
                    guid = null;
                    pubdate = null;
                    updated = null;
                    link = null;

                }
                eventType = pullParser.next();
            }

            inputStream.close();
            storeArticles(contentValuesArrayList);

        } catch (IOException e) {
        } catch (XmlPullParserException e) {
        } catch (Exception e) {
        } finally {
            mFeedsUpdating.remove(feedID);
            if (mFeedsUpdating.size() == 0) {
                SharedPreferences.Editor editor = mSharedPrefs.edit();
                editor.putBoolean("refreshing", false);
                editor.putLong("lastRefreshed", SystemClock.elapsedRealtime());
                editor.commit();

                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction(BROADCAST_REFRESHED);
                LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
            }
        }
    }

    private boolean isItemTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("item") || currentTag.equalsIgnoreCase("entry");
    }

    private boolean isTitleTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("title") && currentPrefix.equalsIgnoreCase("");
    }

    private boolean isSummaryTag(String currentTag, String currentPrefix) {
        return (currentTag.equalsIgnoreCase("summary") || currentTag.equalsIgnoreCase("description")) && currentPrefix.equalsIgnoreCase("");
    }

    private boolean isContentTag(String currentTag, String currentPrefix) {
        return (currentTag.equalsIgnoreCase("encoded") && currentPrefix.equalsIgnoreCase("content"))
                || (currentTag.equalsIgnoreCase("content") && currentPrefix.equalsIgnoreCase(""));
    }

    private boolean isGuidTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("guid") || currentTag.equalsIgnoreCase("id");
    }

    private boolean isDateTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("pubdate") || currentTag.equalsIgnoreCase("published") || currentTag.equalsIgnoreCase("date");
    }

    private boolean isUpdatedTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("updated");
    }

    private boolean isLinkTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("link");
    }

    private boolean isOrigLinkTag(String currentTag, String currentPrefix) {
        return currentTag.equalsIgnoreCase("origLink");
    }

    private XmlPullParser getParser(InputStream inputStream) throws XmlPullParserException {
        XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
        parserFactory.setNamespaceAware(true);
        XmlPullParser pullParser = parserFactory.newPullParser();
        pullParser.setInput(inputStream, null);
        return pullParser;
    }

    private InputStream getURLInputStream(String feedURL) throws IOException, MalformedURLException {
        URLConnection connection = new URL(feedURL).openConnection();
        connection.setRequestProperty("User-agent", getResources().getString(R.string.AppName) + "/" + getResources().getString(R.string.AppVersionName));
        connection.connect();
        return connection.getInputStream();
    }

    private Date getMinimumDate(int feedID) {
        Date newestArticleDate = queryNewestArticleDate(feedID);
        Date articleNotOlderThan = pastDate(KEEP_UNREAD_ARTICLES_DAYS);

        if (newestArticleDate == null) {
            return articleNotOlderThan;
        } else {
            return articleNotOlderThan.before(newestArticleDate) ? newestArticleDate : articleNotOlderThan;
        }
    }

    private void deleteOldArticles(int feedID) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(ArticleDAO.ISDELETED, 1);
        mContentResolver.update(RSSContentProvider.URI_ARTICLES, contentValues, ArticleDAO.FEEDID + " = ? AND " + ArticleDAO.PUBDATE + " < ? AND "
                + ArticleDAO.READ + " IS NOT NULL", new String[] { String.valueOf(feedID), SQLiteHelper.fromDate(pastDate(KEEP_READ_ARTICLES_DAYS)) });

        mContentResolver.delete(RSSContentProvider.URI_ARTICLES, ArticleDAO.FEEDID + " = ? AND " + ArticleDAO.PUBDATE + " < ?",
                new String[] { String.valueOf(feedID), SQLiteHelper.fromDate(pastDate(KEEP_UNREAD_ARTICLES_DAYS)) });
    }

    private void storeArticles(ArrayList<ContentValues> contentValuesArrayList) {
        ContentValues[] contentValuesArray = new ContentValues[contentValuesArrayList.size()];
        contentValuesArray = contentValuesArrayList.toArray(contentValuesArray);
        mContentResolver.bulkInsert(RSSContentProvider.URI_ARTICLES, contentValuesArray);
    }

    private String extractLink(XmlPullParser pullParser) throws XmlPullParserException, IOException {
        String link = null;
        if (pullParser.getAttributeCount() > 0) {
            for (int i = 0; i < pullParser.getAttributeCount(); i++) {
                if (pullParser.getAttributeName(i).equals("href")) {
                    link = pullParser.getAttributeValue(i);
                    break;
                }
            }
        }
        if (link == null) {
            pullParser.next();
            link = pullParser.getText();
        }
        pullParser.next();
        return link;
    }

    private String extractContent(XmlPullParser pullParser) throws XmlPullParserException, IOException {
        String content = "";

        if (pullParser.getAttributeCount() > 0) {
            boolean isEncodedContent = false;
            for (int i = 0; i < pullParser.getAttributeCount(); i++) {
                if (pullParser.getAttributeName(i).equals("type")) {
                    isEncodedContent = (pullParser.getAttributeValue(i).equals("html") || pullParser.getAttributeValue(i).equals("xhtml"));
                    break;
                }
            }
            if (isEncodedContent) {
                content = parseEncodedContent(pullParser);
            }
        } else {
            content = safeNextText(pullParser);
        }

        return content;
    }

    private String parseEncodedContent(XmlPullParser pullParser) throws XmlPullParserException, IOException {
        StringBuilder sb = new StringBuilder();

        pullParser.next();
        int eventType = pullParser.getEventType();

        if (eventType == XmlPullParser.TEXT) {
            String txt = cleanText(pullParser.getText());
            if (txt.length() > 0) {
                pullParser.next();
                return txt;
            } else {
                eventType = pullParser.next();
            }
        }

        if (eventType == XmlPullParser.START_TAG) {
            while (!isContentEnd(pullParser)) {
                if (eventType == XmlPullParser.START_TAG) {
                    if (pullParser.isEmptyElementTag()) {
                        String tag = pullParser.getName();
                        sb.append("<");
                        sb.append(tag);
                        if (tag.equals("img")) {
                            sb.append(getAttribute(pullParser, "src"));
                        }
                        sb.append("/>");
                        pullParser.next();
                    } else {
                        String tag = pullParser.getName();
                        sb.append("<");
                        sb.append(pullParser.getName());
                        if (tag.equals("a")) {
                            sb.append(getAttribute(pullParser, "href"));
                        }
                        sb.append(">");
                    }
                } else if (eventType == XmlPullParser.TEXT) {
                    sb.append(cleanText(pullParser.getText()));
                } else if (eventType == XmlPullParser.END_TAG) {
                    sb.append("</");
                    sb.append(pullParser.getName());
                    sb.append(">");
                }
                eventType = pullParser.next();
            }
        }
        return sb.toString();
    }

    private String cleanText(String text) {
        return text.replace("\n", "").replace("\t", "").replace("\r", "").trim();
    }

    private String getAttribute(XmlPullParser pullParser, String name) {
        StringBuilder sb = new StringBuilder();
        sb.append(" ");
        sb.append(name);
        sb.append("=\"");
        for (int i = 0; i < pullParser.getAttributeCount(); i++) {
            if (pullParser.getAttributeName(i).equals(name)) {
                sb.append(pullParser.getAttributeValue(i));
                break;
            }
        }
        sb.append("\"");
        return sb.toString();
    }

    private boolean isContentEnd(XmlPullParser pullParser) throws XmlPullParserException {
        if (pullParser.getEventType() != XmlPullParser.END_TAG) {
            return false;
        }

        if (pullParser.getName().equals("content")) {
            return true;
        }

        return false;
    }

    /*
     * Work around a bug in early XMLPullParser versions, see http://android-developers.blogspot.de/2011/12/watch-out-for-xmlpullparsernexttext.html
     */
    private String safeNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
        String result = parser.nextText();
        if (parser.getEventType() != XmlPullParser.END_TAG) {
            parser.nextTag();
        }
        return result;
    }

    private ArrayList<String> queryArticles(int feedID) {
        ArrayList<String> articles = new ArrayList<String>();

        Cursor cursor = mContentResolver.query(RSSContentProvider.URI_ARTICLES, new String[] { ArticleDAO._ID, ArticleDAO.GUID, ArticleDAO.PUBDATE },
                ArticleDAO.FEEDID + " = ?", new String[] { String.valueOf(feedID) }, ArticleDAO.PUBDATE + " DESC");

        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            do {
                articles.add(cursor.getString(cursor.getColumnIndex(ArticleDAO.GUID)));
            } while (cursor.moveToNext());
        }
        cursor.close();

        return articles;
    }

    private Date queryNewestArticleDate(int feedID) {
        Date maxDate = null;
        Cursor cursor = mContentResolver.query(RSSContentProvider.URI_ARTICLES, new String[] { ArticleDAO._ID, ArticleDAO.PUBDATE },
                ArticleDAO.FEEDID + " = ?", new String[] { String.valueOf(feedID) }, ArticleDAO.PUBDATE + " DESC");

        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            maxDate = SQLiteHelper.toDate(cursor.getString(cursor.getColumnIndex(ArticleDAO.PUBDATE)));
        }
        cursor.close();

        return maxDate;
    }

    private Date pastDate(int interval) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -interval);
        return calendar.getTime();
    }

    private String queryURL(int feedID) {
        String feedURL = "";
        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(RSSContentProvider.URI_FEEDS, new String[] { FeedDAO._ID, FeedDAO.URL }, FeedDAO._ID + " = ?",
                new String[] { String.valueOf(feedID) }, null);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            feedURL = cursor.getString(cursor.getColumnIndex(FeedDAO.URL));
        }
        cursor.close();
        return feedURL;
    }

    private Date parsePubdate(String rawDate) throws XmlPullParserException {
        Date parsedDate = null;
        for (int j = 0; j < DATE_FORMATS.length; j++) {
            try {
                parsedDate = mSimpleDateFormats[j].parse(rawDate.replaceAll("([\\+\\-]\\d\\d):(\\d\\d)", "$1$2"));
                break;
            } catch (ParseException mParserException) {
                if (j == DATE_FORMATS.length - 1) {
                    throw new XmlPullParserException(mParserException.getMessage());
                }
            }
        }
        return parsedDate;
    }

    private ContentValues prepareArticle(int feedID, String guid, String link, Date pubdate, String title, String summary, String content) {
        boolean missingContent = false;
        boolean missingSummary = false;

        if (content == null) {
            missingContent = true;
        }
        if (summary == null) {
            missingSummary = true;
        }

        if (missingContent && missingSummary) {
            return null;
        }

        if (missingContent) {
            content = summary;
        } else if (missingSummary) {
            summary = content;
        }

        Document parsedContent = Jsoup.parse(content);
        Elements iframes = parsedContent.getElementsByTag("iframe");
        TextNode placeholder = new TextNode("(video removed)", null);
        for (Element mIframe : iframes) {
            mIframe.replaceWith(placeholder);
        }
        content = parsedContent.html();

        Document parsedSummary = Jsoup.parse(summary);
        Elements pics = parsedSummary.getElementsByTag("img");
        for (Element pic : pics) {
            pic.remove();
        }
        summary = parsedSummary.text();

        if (summary.length() > SUMMARY_MAXLENGTH) {
            summary = summary.substring(0, SUMMARY_MAXLENGTH) + "...";
        }

        Element image = parsedContent.select("img").first();

        ContentValues contentValues = new ContentValues();

        contentValues.put(ArticleDAO.FEEDID, feedID);
        contentValues.put(ArticleDAO.GUID, guid);
        contentValues.put(ArticleDAO.LINK, link);
        contentValues.put(ArticleDAO.PUBDATE, SQLiteHelper.fromDate(pubdate));
        contentValues.put(ArticleDAO.TITLE, title);
        contentValues.put(ArticleDAO.SUMMARY, summary);
        contentValues.put(ArticleDAO.CONTENT, content);

        if (image != null) {
            contentValues.put(ArticleDAO.IMAGE, image.absUrl("src"));
        }

        contentValues.put(ArticleDAO.ISDELETED, 0);

        return contentValues;
    }

}




Java Source Code List

de.hdodenhof.holoreader.Config.java
de.hdodenhof.holoreader.activities.DisplayArticleActivity.java
de.hdodenhof.holoreader.activities.DisplayFeedActivity.java
de.hdodenhof.holoreader.activities.EditFeedsActivity.java
de.hdodenhof.holoreader.activities.HoloReaderActivity.java
de.hdodenhof.holoreader.activities.HomeActivity.java
de.hdodenhof.holoreader.fragments.ArticleFragment.java
de.hdodenhof.holoreader.fragments.ArticleListFragment.java
de.hdodenhof.holoreader.fragments.CustomListFragment.java
de.hdodenhof.holoreader.fragments.DynamicDialogFragment.java
de.hdodenhof.holoreader.fragments.EditFeedsFragment.java
de.hdodenhof.holoreader.fragments.FeedListFragment.java
de.hdodenhof.holoreader.gcm.GCMIntentService.java
de.hdodenhof.holoreader.gcm.GCMReceiver.java
de.hdodenhof.holoreader.gcm.GCMServerUtilities.java
de.hdodenhof.holoreader.gcm.VOFeed.java
de.hdodenhof.holoreader.listadapters.EditFeedAdapter.java
de.hdodenhof.holoreader.listadapters.RSSAdapter.java
de.hdodenhof.holoreader.listadapters.RSSArticleAdapter.java
de.hdodenhof.holoreader.listadapters.RSSFeedAdapter.java
de.hdodenhof.holoreader.misc.ArticleViewPager.java
de.hdodenhof.holoreader.misc.ChangelogDialog.java
de.hdodenhof.holoreader.misc.CheckableLinearLayout.java
de.hdodenhof.holoreader.misc.CustomScrollView.java
de.hdodenhof.holoreader.misc.DiskLruImageCache.java
de.hdodenhof.holoreader.misc.FragmentCallback.java
de.hdodenhof.holoreader.misc.Helpers.java
de.hdodenhof.holoreader.misc.MarkReadRunnable.java
de.hdodenhof.holoreader.misc.OnArticleChangedListener.java
de.hdodenhof.holoreader.provider.RSSContentProvider.java
de.hdodenhof.holoreader.provider.SQLiteHelper.java
de.hdodenhof.holoreader.services.RefreshFeedListener.java
de.hdodenhof.holoreader.services.RefreshFeedService.java
de.hdodenhof.holoreader.tasks.AddFeedTask.java