/******************************************************************************* * Copyright 2009 Robot Media SL * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package net.robotmedia.acv.comic; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.TreeMap; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import net.robotmedia.acv.Constants; import net.robotmedia.acv.logic.TrackingManager; import net.robotmedia.acv.utils.FileUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.Config; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; public class ZipComic extends Comic { private ZipFile mZip; private ArrayList<String> screens; protected TreeMap<String, String> unorderedScreens = new TreeMap<String, String>(); protected ZipComic(String comicPath) { super(comicPath); init(comicPath); } public void destroy() { try { if (this.mZip != null) { this.mZip.close(); } } catch (IOException e) {} } protected File extract(ZipEntry entry, String name) { BufferedInputStream in = null; FileOutputStream out = null; File file = null; try { InputStream zipInputStream = getInputStream(entry); in = new BufferedInputStream(zipInputStream, Constants.BUFFER_SIZE); file = createTempFile(name); out = new FileOutputStream(file); int count; byte[] buffer = new byte[Constants.BUFFER_SIZE]; while ((count = in.read(buffer, 0, Constants.BUFFER_SIZE)) != -1) { out.write(buffer, 0, count); } out.flush(); in.close(); } catch (IOException e) { e.printStackTrace(); if (in != null) { try { in.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e1) { e1.printStackTrace(); } } TrackingManager.trackError("ZipComic.extract", e); } return file; } private synchronized Bitmap getBitmapFromEntryIfNeeded(String entryName, boolean recycle) { Bitmap bitmap = null; ImageState status = imageState.get(entryName); if (status == null || status.equals(ImageState.UNKNOWN)) { ZipEntry entry = mZip.getEntry(entryName); try { File file = extract(entry, entry.getName()); BitmapFactory.decodeFile(file.getPath(), bounds); if (bounds.outWidth == -1) { // TODO: Error } int width = bounds.outWidth; int height = bounds.outHeight; boolean landscape = height > width; int maxHeight = getMaxHeight(landscape); int maxWidth = getMaxWidth(landscape); boolean withinBounds = width <= maxWidth && height <= maxHeight; if (withinBounds) { imageState.put(entryName, ImageState.ORIGINAL); } else { bitmap = resampleAndSave(entryName, width, height); } } catch (Exception e) { e.printStackTrace(); TrackingManager.trackError("ZipComic.getBitmapFromEntryIfNeeded", e); } } if (bitmap != null && recycle) { bitmap.recycle(); return null; } else { return bitmap; } } protected InputStream getInputStream(String name) throws IOException { ZipEntry entry = mZip.getEntry(name); return getInputStream(entry); } protected InputStream getInputStream(ZipEntry entry) throws IOException { return mZip.getInputStream(entry); } public int getLength() { return screens != null ? screens.size() : 0; } protected Drawable getDrawable(String entryName) { ImageState status = imageState.get(entryName); if (status == null) status = ImageState.UNKNOWN; String filePath = getTempFilePath(entryName); switch (status) { case MODIFIED: case ORIGINAL: if (filePath != null) { return Drawable.createFromPath(filePath); } else { // HACK: The temp file was deleted, so we're back to square one. Unknown is not the best description for this case. imageState.put(entryName, ImageState.UNKNOWN); } default: Bitmap bitmap = getBitmapFromEntryIfNeeded(entryName, false); if (bitmap == null) { status = imageState.get(entryName); if (status == null) status = ImageState.UNKNOWN; // This shouldn't happen filePath = getTempFilePath(entryName); switch (status) { case MODIFIED: case ORIGINAL: if (filePath != null) { return Drawable.createFromPath(filePath); } default: error(); return null; } } else { return new BitmapDrawable(bitmap); } } } public Drawable getScreen(final int position) { if(position < 0 || position >= screens.size()) { return null; } final String entryName = screens.get(position); return getDrawable(entryName); } public Drawable getThumbnail(int position) { return null; } @Override public Uri getUri(int position) { final String entryName = screens.get(position); String filePath = getTempFilePath(entryName); return filePath != null ? Uri.fromFile(new File(filePath)) : null; } private void init(String comicPath) { try { this.setZip(new ZipFile(comicPath)); Enumeration<? extends ZipEntry> entries = mZip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { this.processEntry(entry); } } } catch (Exception e) { e.printStackTrace(); error(); } { ArrayList<String> orderedScreenKeys = new ArrayList<String>(unorderedScreens.keySet()); screens = new ArrayList<String>(orderedScreenKeys.size()); for (int i = 0; i < orderedScreenKeys.size(); i++) { screens.add(unorderedScreens.get(orderedScreenKeys.get(i))); } } } public void prepareScreen(int position) { if (position >= 0 && position < this.getLength()) { final String entryName = screens.get(position); ImageState status = imageState.get(entryName); if (status == null || status.equals(ImageState.UNKNOWN)) { try { getBitmapFromEntryIfNeeded(entryName, true); } catch (Exception e) { e.printStackTrace(); TrackingManager.trackError("ZipComic.prepareScreen", e); } } } } protected void setZip(ZipFile zip) { this.mZip = zip; } protected void processEntry(ZipEntry entry) { String entryName = entry.getName(); String extension = FileUtils.getFileExtension(entryName); if (FileUtils.isImage(extension)) { final String entryNameWithLeadingZeroes = this.addLeadingZeroes(entryName); unorderedScreens.put(entryNameWithLeadingZeroes, entryName); } } private Bitmap resample(String filePath, int sampleSize) { BitmapFactory.Options resample = new BitmapFactory.Options(); resample.inPreferredConfig = Config.RGB_565; resample.inSampleSize = sampleSize; return BitmapFactory.decodeFile(filePath, resample); } private Bitmap resampleAndSave(String entryName, int width, int height) { String filePath = getTempFilePath(entryName); Bitmap bitmap = null; int sampleSize = calculateSampleSize(width, height); if (filePath != null) { bitmap = resample(filePath, sampleSize); filePath = saveBitmap(entryName, bitmap); if (filePath != null) { imageState.put(entryName, ImageState.MODIFIED); } } return bitmap; } }