ComicReader/ComicReader/src/com/blogspot/applications4android/comicreader/core/Comic.java

807 lines
19 KiB
Java
Executable File

package com.blogspot.applications4android.comicreader.core;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.client.ClientProtocolException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.blogspot.applications4android.comicreader.exceptions.ComicException;
import com.blogspot.applications4android.comicreader.exceptions.ComicSDCardFull;
/**
* Base class for all comic series
*/
// TODO: add unit-tests
public abstract class Comic extends ComicParser {
/** properties folder name */
public static final String PROPS = "props";
/** previous session */
public static final int TYPE_PREV_SESSION = 1;
/** latest comics */
public static final int TYPE_LATEST = 2;
/** favorite comics */
public static final int TYPE_FAVORITE = 3;
/** preview comics */
public static final int TYPE_PREVIEW = 4;
/** background caching */
public static final int TYPE_CACHING = 5;
/** navigate to latest strip */
public static final int NAV_LATEST = 11;
/** navigate to latest strip forcefully */
public static final int NAV_LATEST_FORCE = 12;
/** navigate to first strip */
public static final int NAV_FIRST = 13;
/** navigate to next strip */
public static final int NAV_NEXT = 14;
/** navigate to previous strip */
public static final int NAV_PREVIOUS = 15;
/** navigate to random strip */
public static final int NAV_RANDOM = 16;
/** navigate to current strip */
public static final int NAV_CURRENT = 17;
/** navigate to previous session */
public static final int NAV_PREV_SESSION = 18;
/** use date picker dialog */
public static final int DIALOG_DATE = 51;
/** use number picker dialog */
public static final int DIALOG_NUMBER = 52;
/** for logging purposes only */
private static final String TAG = "Comic";
/** uid for latest comic */
protected String mLatestUid;
/** uid for first comic */
protected String mFirstUid;
/** uid for the previous session comic */
protected String mPrevSessionUid;
/** name of this comic */
private String mName;
/** english name of this comic */
private String mComicName;
/** list of all strips */
private HashMap<String, Strip> mStrips;
/** cache manager for this comic */
private Cache mCache;
/** default value of zoom */
private float mDefZoom;
/** current strip */
private Strip mCurrent;
/** launch type */
private int mType;
/** favorites array */
private ArrayList<String> mFavs;
/** for traversing on favorites array */
private int mFavIdx;
/** background caching enabled */
private boolean mCacheEnabled;
/**
* Constructor
*/
public Comic() {
mName = getClass().getSimpleName();
mComicName = null;
mCache = new Cache(mName);
mLatestUid = null;
mFirstUid = null;
mPrevSessionUid = null;
mCurrent = null;
mDefZoom = 1.0f;
mStrips = new HashMap<String, Strip>();
mType = TYPE_LATEST;
mFavs = new ArrayList<String>();
mFavIdx = 0;
mCacheEnabled = true;
}
/**
* Tell to this comic whether caching is enabled or not. This'll be used to
* decide whether to forcefully download the latest comic or not.
*
* @param cache
* true if it is, else false
*/
public void setCacheEnabled(boolean cache) {
mCacheEnabled = cache;
}
/**
* Set the launch type for this comic
*
* @param type
* launch type
*/
public void setLaunchType(int type) {
mType = type;
}
/**
* get the launch type
*
* @return launch type
*/
public int getLaunchType() {
return mType;
}
/**
* Get the comic name
*
* @return name
*/
public String getName() {
return mName;
}
/**
* Gets the current strip
*
* @return current strip
*/
public Strip getCurrentStrip() {
return mCurrent;
}
/**
* Get the english name for this comic
*
* @return name
*/
public String getComicName() {
return mComicName;
}
/**
* Set the english name for this comic
*
* @param name
* name to be set
*/
public void setComicName(String name) {
mComicName = name;
}
/**
* Reads unread strips count for the current comic
*
* @return unread strips count
* @throws FileNotFoundException
* @throws IOException
* @throws JSONException
*/
public int readOnlyUnread() throws FileNotFoundException, IOException, JSONException {
File json = _getJsonFile();
if (!json.exists()) {
return 0;
}
JSONObject root = JsonUtils.jsonRoot(new FileInputStream(json));
if (root.has("mUnread")) {
return root.getInt("mUnread");
}
return 0;
}
/**
* Helper function to read the properties of this comic from its json file
*
* @throws FileNotFoundException
* @throws IOException
* @throws JSONException
*/
public void readProperties() throws FileNotFoundException, IOException, JSONException {
File json = _getJsonFile();
if (!json.exists()) {
_readOldFavs();
_readOldPrevSession();
return;
}
Log.d(TAG, "Reading comic properties from " + json.getPath());
JSONObject root = JsonUtils.jsonRoot(new FileInputStream(json));
if (root.has("mLatestUid")) {
mLatestUid = root.getString("mLatestUid");
}
if (root.has("mFirstUid")) {
mFirstUid = root.getString("mFirstUid");
}
if (root.has("mPrevSessionUid")) {
mPrevSessionUid = root.getString("mPrevSessionUid");
}
if (root.has("mDefZoom")) {
mDefZoom = (float) root.getDouble("mDefZoom");
}
if (root.has("mStrips")) {
JSONArray arr = root.getJSONArray("mStrips");
int len = arr.length();
for (int i = 0; i < len; ++i) {
Strip s = Strip.readFromJsonObject(arr.getJSONObject(i));
mStrips.put(s.uid(), s);
if (s.isFavorite()) {
mFavs.add(s.uid());
}
}
}
Log.d(TAG, "Successfully read comic properties ...");
}
/**
* For backwards compatibility
*
* @throws IOException
*/
private void _readOldFavs() throws IOException {
File old = new File(FileUtils.getComicRoot(), mName);
if (!old.exists()) {
return;
}
Log.d(TAG, "Getting old favorites from " + old.getPath());
BufferedReader br = new BufferedReader(new FileReader(old));
String line = br.readLine();
mDefZoom = Float.parseFloat(line);
while ((line = br.readLine()) != null) {
Strip s = new Strip(line, mCache.cachePath());
s.setAsFavorite(true);
Log.d(TAG, line);
mStrips.put(line, s);
line = br.readLine(); // no more required
}
br.close();
old.delete(); // this file is no longer required!
}
/**
* For backwards compatibility
*
* @throws IOException
*/
private void _readOldPrevSession() throws IOException {
File old = new File(FileUtils.getComicRoot(), mName + "_lsu");
if (!old.exists()) {
return;
}
Log.d(TAG, "Getting old previous-session from " + old.getPath());
BufferedReader br = new BufferedReader(new FileReader(old));
String line = br.readLine();
if (line != null) {
mPrevSessionUid = line;
}
br.close();
old.delete(); // this file is no longer required!
}
/**
* Helper function to write the properties of this comic into its json file
*
* @throws IOException
* @throws ComicSDCardFull
*/
public void writeProperties() throws IOException, ComicSDCardFull {
if (mType == TYPE_PREVIEW) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append("{\n");
{
int unread = 0;
Iterator<Entry<String, Strip>> itr1 = mStrips.entrySet().iterator();
while (itr1.hasNext()) {
Map.Entry<String, Strip> e = itr1.next();
if (!e.getValue().isRead()) {
unread++;
}
}
sb.append("\"mUnread\":" + unread + ",\n");
if (mLatestUid != null) {
sb.append("\"mLatestUid\":\"" + mLatestUid + "\",\n");
}
if (mFirstUid != null) {
sb.append("\"mFirstUid\":\"" + mFirstUid + "\",\n");
}
if (mPrevSessionUid != null) {
sb.append("\"mPrevSessionUid\":\"" + mPrevSessionUid + "\",\n");
}
sb.append("\"mDefZoom\":\"" + mDefZoom + "\",\n");
sb.append("\"mStrips\": [\n");
Iterator<Entry<String, Strip>> itr = mStrips.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, Strip> e = itr.next();
e.getValue().toJsonString(sb);
if (itr.hasNext()) {
sb.append(",");
}
sb.append("\n");
}
sb.append("]\n");
}
sb.append("}\n");
FileUtils.storeString(sb.toString(), _getJsonFile());
Log.d(TAG, "Stored comic properties to the file '" + _getJsonFile().getPath() + "'");
}
/**
* Returns the default value of zoom
*
* @return zoom value
*/
public float getDefaultZoom() {
return mDefZoom;
}
/**
* Set the default value of zoom
*
* @param zoom
* zoom value
*/
public void setDefaultZoom(float zoom) {
Log.d(TAG, "Setting default zoom to " + zoom);
mDefZoom = zoom;
}
/**
* Tells whether the comic has any previous sessions or not
*
* @return true if it has, else false
*/
public boolean hasPreviousSession() {
return (mPrevSessionUid != null);
}
/**
* Sets the current strip as favorite or not
*
* @param val
* true if it is to be set as favorite
*/
public void setCurrentAsFavorite(boolean val) {
if (mCurrent != null) {
mCurrent.setAsFavorite(val);
if (val) {
mFavs.add(mCurrent.uid());
} else {
mFavs.remove(mCurrent.uid());
}
}
}
/**
* Whether the current comic is favorite or not
*
* @return true if it is
*/
public boolean isCurrentFavorite() {
if (mCurrent == null) {
return false;
}
return mCurrent.isFavorite();
}
/**
* Whether the current comic has image text or not
*
* @return true if it is
*/
public boolean currentHasImageText() {
if (mCurrent == null) {
return false;
}
return mCurrent.hasText();
}
public boolean containsFavs(){
return mFavs.size() > 0 ? true : false;
}
/**
* Creates a valid file name out of the strip's title
*
* @return valid file name
*/
public String currentTitleAsValidFilename() {
if (mCurrent == null) {
return null;
}
return mCurrent.currentTitleAsValidFilename();
}
/**
* Helper function to clear the cache
*/
public void clearCache() {
mCache.clearCache();
}
/**
* Clears all the history associated with this comic
*/
public void clearHistory() {
mStrips.clear();
mLatestUid = null;
}
// //// Strip navigation //////
/**
* Helper function to provide unified interface for navigation among the
* strips
*
* @param type
* navigation type
* @return strip
* @throws ComicException
*/
public Strip navigateStrip(int type) throws ComicException {
mCache.makeSpace();
if (mType == TYPE_FAVORITE) {
switch (type) {
case NAV_LATEST:
mFavIdx = mFavs.size() - 1;
return _getFavoriteStrip();
case NAV_FIRST:
mFavIdx = 0;
return _getFavoriteStrip();
case NAV_NEXT:
++mFavIdx;
mFavIdx = (mFavIdx >= mFavs.size()) ? 0 : mFavIdx;
return _getFavoriteStrip();
case NAV_PREVIOUS:
--mFavIdx;
mFavIdx = (mFavIdx < 0) ? mFavs.size() - 1 : mFavIdx;
return _getFavoriteStrip();
case NAV_RANDOM:
mFavIdx = RandUtils.getPositiveInt(mFavs.size(), 0);
return _getFavoriteStrip();
case NAV_CURRENT:
return mCurrent;
default:
ComicException ce = new ComicException("Bad navigation-type passed: " + type);
throw ce;
}
}
switch (type) {
case NAV_LATEST:
return getLatestStrip();
case NAV_LATEST_FORCE:
return getLatestStripForcefully();
case NAV_FIRST:
return getFirstStrip();
case NAV_NEXT:
return getNextStrip();
case NAV_PREVIOUS:
return getPreviousStrip();
case NAV_RANDOM:
return getRandomStrip();
case NAV_CURRENT:
return mCurrent;
case NAV_PREV_SESSION:
return getPreviousSessionStrip();
default:
ComicException ce = new ComicException("Bad navigation-type passed: " + type);
throw ce;
}
}
/**
* Gets latest strip
*
* @return latest strip
*/
public Strip getLatestStrip() {
if ((mLatestUid == null) || !mCacheEnabled) {
Log.d(TAG, "Latest uid is null (or bg-caching is disabled) , calling getlatestStripUrl...");
mLatestUid = getLatestStripUrl();
}
return _querySetCurrentUid(mLatestUid);
}
/**
* Gets latest strip forcefully (used during background caching)
*
* @return latest strip
*/
public Strip getLatestStripForcefully() {
mLatestUid = getLatestStripUrl();
return _querySetCurrentUid(mLatestUid);
}
/**
* Gets the first strip
*
* @return first strip
*/
public Strip getFirstStrip() {
mFirstUid = getFirstStripUrl();
return _querySetCurrentUid(mFirstUid);
}
/**
* Gets the random strip
*
* @return random strip
*/
public Strip getRandomStrip() {
String r = getRandomStripUrl();
return _querySetCurrentUid(r);
}
/**
* Gets the next strip
*
* @return next strip
*/
public Strip getNextStrip() {
if (isCurrentLatestStrip()) {
return mCurrent;
}
if (!mCurrent.hasNext()) {
mCurrent.setNext(getNextStripUrl());
}
String uid = mCurrent.uid();
Strip s = _querySetCurrentUid(mCurrent.getNext());
s.setPrevious(uid);
return s;
}
/**
* Gets the previous strip
*
* @return previous strip
*/
public Strip getPreviousStrip() {
if (isCurrentFirstStrip()) {
return mCurrent;
}
if (!mCurrent.hasPrevious()) {
mCurrent.setPrevious(getPreviousStripUrl());
}
String uid = mCurrent.uid();
Strip s = _querySetCurrentUid(mCurrent.getPrevious());
s.setNext(uid);
return s;
}
/**
* Gets the previous session strip
*
* @return previous session strip
*/
public Strip getPreviousSessionStrip() {
if (mPrevSessionUid == null) {
return null;
}
return _querySetCurrentUid(mPrevSessionUid);
}
/**
* Gets the strip from the given url
*
* @param url
* url
* @return strip
* @throws ComicSDCardFull
* @throws IOException
* @throws URISyntaxException
* @throws ClientProtocolException
*/
public Strip getStripFromUrl(String url) throws ClientProtocolException, URISyntaxException, IOException, ComicSDCardFull {
Strip s = _querySetCurrentUid(url);
s.downloadImage(this);
return s;
}
/**
* Download the current strip
*
* @throws ClientProtocolException
* @throws URISyntaxException
* @throws IOException
* @throws ComicSDCardFull
*/
public void downloadCurrentStrip() throws ClientProtocolException, URISyntaxException, IOException, ComicSDCardFull {
if (mCurrent == null) {
return;
}
mCurrent.downloadImage(this);
}
// //// Strip navigation //////
// //// List of all abstract methods of this class //////
/**
* Get the main page url for the current comic series
*
* @return desired url
*/
public abstract String getComicWebPageUrl();
/**
* Returns the bound for the current comic
*
* @return bound
*/
public abstract Bound getBound();
/**
* Type of dialog to be used for choosing comics
*
* @return int
*/
public abstract int dialogType();
/**
* Gets the url for the latest strip
*
* @return url
*/
protected abstract String getLatestStripUrl();
/**
* Gets the url for the first strip
*
* @return url
*/
protected abstract String getFirstStripUrl();
/**
* Gets the url for the next strip
*
* @return url
*/
protected abstract String getNextStripUrl();
/**
* Gets the url for the previous strip
*
* @return url
*/
protected abstract String getPreviousStripUrl();
/**
* Gets the url for the previous strip
*
* @return url
*/
protected abstract String getRandomStripUrl();
/**
* Gives out the list of urls (can be regexes) which are not supposed to be
* cached using 'mStrips'
*
* @return list
*/
protected abstract String[] urlsNotForCaching();
// //// List of all abstract methods of this class //////
// //// protected methods //////
/**
* Checks whether the current strip is latest strip or not
*
* @return true if it is
*/
protected boolean isCurrentLatestStrip() {
if (mLatestUid == null) {
mLatestUid = getLatestStripUrl();
}
return mLatestUid.equals(mCurrent.uid());
}
/**
* Checks whether the current strip is first strip or not
*
* @return true if it is
*/
protected boolean isCurrentFirstStrip() {
if (mFirstUid == null) {
mFirstUid = getFirstStripUrl();
}
return mFirstUid.equals(mCurrent.uid());
}
/**
* Query whether the uid already exists in the history
*
* @param uid
* uid
* @return true if it is, else false
*/
protected boolean hasUid(String uid) {
return mStrips.containsKey(uid);
}
/**
* Helper function to query for a strip and create if it doesn't exist
*
* @param uid
* key to be queried
* @return strip
*/
protected Strip queryUid(String uid) {
if (mStrips.containsKey(uid)) {
return mStrips.get(uid);
}
if (uid == null) {
}
Strip s = new Strip(uid, mCache.cachePath());
String[] urls = urlsNotForCaching();
if (urls != null) {
for (String url : urls) {
if (uid.matches(url) || uid.equals(url)) {
Log.d(TAG, "UID=" + uid + " matches with the regex/url=" + url + ". So not caching this strip");
File f = new File(s.getImagePath());
if (f.exists()) {
f.delete();
}
return s;
}
}
}
if (uid != null) {
mStrips.put(uid, s);
}
return s;
}
// //// protected methods //////
// //// private methods //////
/**
* Helper function to query for a strip and set it as current
*
* @param uid
* key to be queried
* @return strip
*/
private Strip _querySetCurrentUid(String uid) {
mCurrent = queryUid(uid);
if (mType != TYPE_CACHING) {
mPrevSessionUid = uid;
}
Log.d(TAG, "Current UID = " + uid);
return mCurrent;
}
/**
* Returns the current favorite strip
*
* @return strip
*/
private Strip _getFavoriteStrip() {
String uid = mFavs.get(mFavIdx);
return _querySetCurrentUid(uid);
}
/**
* Helper function to return the properties json file associated with this
* comic
*
* @return json file
*/
private File _getJsonFile() {
File f = new File(FileUtils.getComicRoot(), PROPS);
f.mkdirs();
return new File(f, mName + ".json");
}
// //// private methods //////
}