/*******************************************************************************
* BBC News Reader
* Released under the BSD License. See README or LICENSE.
* Copyright (c) 2011, Digital Lizard (Oscar Key, Thomas Boby)
* All rights reserved.
******************************************************************************/
package com.digitallizard.bbcnewsreader.resource.web;
import java.net.URL;
import java.util.Iterator;
import java.util.concurrent.PriorityBlockingQueue;
import com.digitallizard.bbcnewsreader.ReaderActivity;
import com.digitallizard.bbcnewsreader.ResourceInterface;
public class WebManager implements Runnable {
/* constants */
public static final int ITEM_TYPE_HTML = 2;
public static final int ITEM_TYPE_THUMB = 1;
public static final int ITEM_TYPE_IMAGE = 0;
public static final int ERROR_FAIL_THRESHOLD = 4;
/* variables */
PriorityBlockingQueue<QueueItem> downloadQueue;
ResourceInterface handler;
private boolean keepDownloading;
Thread downloadThread;
private volatile boolean noError;
private volatile int numErrors;
public synchronized boolean isQueueEmpty() {
return downloadQueue.isEmpty();
}
synchronized boolean shouldKeepDownloading() {
return keepDownloading;
}
synchronized void setKeepDownloading(boolean keepDownloading) {
this.keepDownloading = keepDownloading;
}
private void downloadItem(QueueItem item) {
switch (item.getType()) {
case ITEM_TYPE_HTML:
downloadHtml(item);
break;
case ITEM_TYPE_THUMB:
downloadThumbnail(item);
break;
case ITEM_TYPE_IMAGE:
downloadImage(item);
break;
}
}
private void downloadHtml(QueueItem item) {
try {
byte[] html = HtmlParser.getPage(item.getUrl()); // load the page
// before we report this download, check if it was a specific request
if (item.wasSpecificallyRequested()) {
handler.itemDownloadComplete(true, item.getItemId(), item.getType(), html);
}
else {
handler.itemDownloadComplete(false, item.getItemId(), item.getType(), html);
}
} catch (Exception e) {
numErrors++; // increment the number of errors
if (numErrors > ERROR_FAIL_THRESHOLD) {
// report the error
// FIXME need to work out if internet error or not
handler.reportError(ReaderActivity.ERROR_TYPE_INTERNET, "There was an error retrieving articles.", e.toString());
stopDownload(); // give up loading
}
}
}
private void downloadThumbnail(QueueItem item) {
try {
URL url = new URL(item.getUrl());
byte[] thumb = ImageDownloader.getImage(url); // load the image
handler.itemDownloadComplete(false, item.getItemId(), item.getType(), thumb);
} catch (Exception e) {
numErrors++; // increment the number of errors
if (numErrors > ERROR_FAIL_THRESHOLD) {
// report the error
// FIXME need to work out if internet error or not
handler.reportError(ReaderActivity.ERROR_TYPE_INTERNET, "There was an error retrieving thumbnails.", e.toString());
stopDownload(); // give up loading
}
}
}
private void downloadImage(QueueItem item) {
try {
URL url = new URL(item.getUrl());
byte[] image = ImageDownloader.getImage(url); // load the image
handler.itemDownloadComplete(false, item.getItemId(), item.getType(), image);
} catch (Exception e) {
numErrors++; // increment the number of errors
if (numErrors > ERROR_FAIL_THRESHOLD) {
// report the error
handler.reportError(ReaderActivity.ERROR_TYPE_INTERNET, "There was an error retrieving images.", e.toString());
e.printStackTrace();
}
}
}
private void itemQueued() {
// check if we need to start the download thread
if (!shouldKeepDownloading()) {
// start the download thread
setKeepDownloading(true);
noError = true;
downloadThread = new Thread(this);
downloadThread.start();
}
}
public void addToQueue(String url, int type, int itemId) {
// just call the main function with the type as the priority
addToQueue(url, type, itemId, type);
}
public void addToQueue(String url, int type, int itemId, int priority) {
QueueItem queueItem = new QueueItem(url, type, itemId, priority);
downloadQueue.add(queueItem);
itemQueued();
}
public void loadNow(String url, int type, int itemId) {
// check if a load is in progress
if (shouldKeepDownloading()) {
// loop through the queue to find the item we want, then boost its priority
boolean itemExists = false; // set to true if the item was actually in the queue
// FIXME looping efficient? probably doesn't matter as only on user command
Iterator<QueueItem> iterator = downloadQueue.iterator();
while (iterator.hasNext()) {
// check the id of this item
QueueItem item = iterator.next();
if (item.getItemId() == itemId) {
// boost the priority of this item
item.setPriority(QueueItem.PRIORITY_DOWNLOAD_NOW);
itemExists = true; // we found the item
}
}
// if the item wasn't found, create it and set its priority high
if (!itemExists) {
addToQueue(url, type, itemId, QueueItem.PRIORITY_DOWNLOAD_NOW);
}
}
else {
// clear the queue, just in case
emptyQueue();
// add the item to the queue, this will automatically start the download
addToQueue(url, type, itemId, QueueItem.PRIORITY_DOWNLOAD_NOW);
}
}
public void emptyQueue() {
// check if a download is in progress
if (shouldKeepDownloading()) {
stopDownload(); // first stop downloading
}
downloadQueue.clear(); // empty the queue
}
public void stopDownload() {
// check if the download is going
if (shouldKeepDownloading()) {
// try and stop the download
noError = false;
setKeepDownloading(false); // this will stop it after the current file
emptyQueue(); // empty the queue
}
else {
// as a load isn't in progress we can report that we have finished
handler.fullLoadComplete(false);
}
}
public void run() {
// check this hasn't been called in error
if (!isQueueEmpty()) {
// keep downloading if we should
while (shouldKeepDownloading()) {
// retrieve the head of the queue and load it
downloadItem(downloadQueue.poll());
// check if the queue is empty now
if (isQueueEmpty()) {
setKeepDownloading(false); // stop the loop
}
}
handler.fullLoadComplete(noError); // report that the load is complete
}
}
public WebManager(ResourceInterface handler) {
this.handler = handler;
downloadQueue = new PriorityBlockingQueue<QueueItem>();
numErrors = 0; // no errors yet
}
}