package com.gettingmobile.goodnews.download; import android.os.Handler; import android.os.Message; import android.util.Log; import com.gettingmobile.goodnews.Application; import com.gettingmobile.google.reader.Item; import com.gettingmobile.google.reader.Resource; import java.util.*; final class ContentDownloadThread extends Thread { private static final String LOG_TAG = "goodnews.ContentDownloadThread"; private final Application app; private final ContentDownloadListener listener; private final MainThreadHandler mainThreadHandler; private ItemDownloader downloader = null; public ContentDownloadThread(Application app, ContentDownloadListener listener) { super(ContentDownloadThread.class.getName()); this.app = app; this.listener = listener; mainThreadHandler = new MainThreadHandler(); } /* * listener handling */ protected void fireDownloadStarted() { listener.onDownloadStarted(); } protected void fireDownloadProgressUpdate(int progress, int max) { listener.onDownloadProgressUpdate(progress, max); } protected void fireDownloadStopped() { listener.onDownloadStopped(); } protected void fireDownloadSkipped() { listener.onDownloadSkipped(); } /* * operations */ public void shutdown() { if (downloader != null) { downloader.shutdown(); } } /* * doing */ protected Queue<Item> readRelevantItems(Set<Long> processedItemKeys) { final boolean readListOnly = app.getSettings().getOfflineStrategy() == OfflineStrategy.READ_LIST; final List<Item> items = readListOnly ? ItemDownloader.itemDownloadAdapter.readItemDownloadInfosRequiringDownloads( app.getDbHelper().getDatabase(), app.getSettings().getLabelReadListId()) : ItemDownloader.itemDownloadAdapter.readItemDownloadInfosRequiringDownloads( app.getDbHelper().getDatabase()); final Queue<Item> q = new LinkedList<Item>(); for (Item item : items) { /* * deciding which items need to be processed */ if (!processedItemKeys.contains(item.getKey())) { final OfflineContentType offlineContentType = app.getSettings().getOfflineContentType(item.getFeedId()); if (offlineContentType != OfflineContentType.NONE) { final Resource alternate = item.getAlternate(); final boolean hasValidAlternate = alternate != null && alternate.getHref() != null && alternate.getHref().length() > 0; final boolean wantsContent = offlineContentType.wantsText(); final boolean wantsImages = offlineContentType.wantsImages(); final boolean hasImages = item.hasImages(); final boolean hasSummary = item.hasSummary(); final boolean hasContent = item.hasContent(); if ((wantsContent && !hasContent && hasValidAlternate) /* requires content */ || (wantsImages && !hasImages && (hasSummary || hasContent || hasValidAlternate)) /* requires images */) { q.add(item); processedItemKeys.add(item.getKey()); } } } } return q; } @Override public void run() { /* * prepare the item queue and cancel if there is nothing to be downloaded */ final Set<Long> processedItemKeys = new HashSet<Long>(); Queue<Item> q = readRelevantItems(processedItemKeys); if (q.isEmpty()) { mainThreadHandler.sendDownloadSkipped(); return; } mainThreadHandler.sendDownloadStarted(); try { while (!q.isEmpty()) { final DownloadProgress progress = new ContentDownloadProgress(q.size()); downloader = new ItemDownloader(app, q, progress); downloader.run(); q = readRelevantItems(processedItemKeys); } } finally { mainThreadHandler.sendDownloadStopped(); } } /* * inner classes */ class MainThreadHandler extends Handler { private static final int MSG_DOWNLOAD_STARTED = 0; private static final int MSG_DOWNLOAD_PROGRESS_UPDATE = 1; private static final int MSG_DOWNLOAD_STOPPED = 2; private static final int MSG_DOWNLOAD_SKIPPED = 3; public void sendDownloadStarted() { sendMessage(obtainMessage(MSG_DOWNLOAD_STARTED)); } public void sendDownloadProgressUpdate(int progress, int max) { sendMessage(obtainMessage(MSG_DOWNLOAD_PROGRESS_UPDATE, progress, max)); } public void sendDownloadStopped() { sendMessage(obtainMessage(MSG_DOWNLOAD_STOPPED)); } public void sendDownloadSkipped() { sendMessage(obtainMessage(MSG_DOWNLOAD_SKIPPED)); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STARTED: fireDownloadStarted(); break; case MSG_DOWNLOAD_PROGRESS_UPDATE: fireDownloadProgressUpdate(msg.arg1, msg.arg2); break; case MSG_DOWNLOAD_STOPPED: fireDownloadStopped(); break; case MSG_DOWNLOAD_SKIPPED: fireDownloadSkipped(); break; default: super.handleMessage(msg); } } } class ContentDownloadProgress implements DownloadProgress { private static final int MIN_UPDATE_DELAY = 1000; private final int max; private int progress = 0; private long latestUpdateTimestamp = 0; public ContentDownloadProgress(int max) { this.max = max; } @Override synchronized public void increase() { ++progress; /* * prevent us to flood the system with too many update messages */ final long timestamp = System.currentTimeMillis(); if (timestamp - latestUpdateTimestamp > MIN_UPDATE_DELAY) { Log.d(LOG_TAG, "Content download progres: " + 100 * progress / max + "%"); mainThreadHandler.sendDownloadProgressUpdate(progress, max); latestUpdateTimestamp = timestamp; } } } }