/* * Copyright 2016 Hippo Seven * * 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 com.hippo.ehviewer.spider; import android.content.Context; import android.graphics.BitmapFactory; import android.support.annotation.Nullable; import android.webkit.MimeTypeMap; import com.hippo.beerbelly.SimpleDiskCache; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhCacheKeyFactory; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.gallery.GalleryProvider2; import com.hippo.io.UniFileInputStreamPipe; import com.hippo.io.UniFileOutputStreamPipe; import com.hippo.streampipe.InputStreamPipe; import com.hippo.streampipe.OutputStreamPipe; import com.hippo.unifile.FilenameFilter; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.Utilities; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Locale; public final class SpiderDen { @Nullable private final UniFile mDownloadDir; private volatile int mMode = SpiderQueen.MODE_READ; private final long mGid; @Nullable private static SimpleDiskCache sCache; public static void initialize(Context context) { sCache = new SimpleDiskCache(new File(context.getCacheDir(), "image"), MathUtils.clamp(Settings.getReadCacheSize(), 40, 640) * 1024 * 1024); } private static class StartWithFilenameFilter implements FilenameFilter { private final String mPrefix; public StartWithFilenameFilter(String prefix) { mPrefix = prefix; } @Override public boolean accept(UniFile dir, String filename) { return filename.startsWith(mPrefix); } } public static UniFile getGalleryDownloadDir(GalleryInfo galleryInfo) { UniFile dir = Settings.getDownloadLocation(); if (dir != null) { // Read from DB String dirname = EhDB.getDownloadDirname(galleryInfo.gid); if (null != dirname) { // Some dirname may be invalid in some version dirname = FileUtils.sanitizeFilename(dirname); EhDB.putDownloadDirname(galleryInfo.gid, dirname); } // Find it if (null == dirname) { UniFile[] files = dir.listFiles(new StartWithFilenameFilter(galleryInfo.gid + "-")); if (null != files) { // Get max-length-name dir int maxLength = -1; for (UniFile file : files) { if (file.isDirectory()) { String name = file.getName(); int length = name.length(); if (length > maxLength) { maxLength = length; dirname = name; } } } if (null != dirname) { EhDB.putDownloadDirname(galleryInfo.gid, dirname); } } } // Create it if (null == dirname) { dirname = FileUtils.sanitizeFilename(galleryInfo.gid + "-" + EhUtils.getSuitableTitle(galleryInfo)); EhDB.putDownloadDirname(galleryInfo.gid, dirname); } return dir.subFile(dirname); } else { return null; } } public SpiderDen(GalleryInfo galleryInfo) { mGid = galleryInfo.gid; mDownloadDir = getGalleryDownloadDir(galleryInfo); } public void setMode(@SpiderQueen.Mode int mode) { mMode = mode; if (mode == SpiderQueen.MODE_DOWNLOAD) { ensureDownloadDir(); } } private boolean ensureDownloadDir() { return mDownloadDir != null && mDownloadDir.ensureDir(); } public boolean isReady() { switch (mMode) { case SpiderQueen.MODE_READ: return sCache != null; case SpiderQueen.MODE_DOWNLOAD: return mDownloadDir != null && mDownloadDir.isDirectory(); default: return false; } } @Nullable public UniFile getDownloadDir() { return mDownloadDir != null && mDownloadDir.isDirectory() ? mDownloadDir : null; } private boolean containInCache(int index) { if (sCache == null) { return false; } String key = EhCacheKeyFactory.getImageKey(mGid, index); return sCache.contain(key); } /** * @param extension with dot */ public static String generateImageFilename(int index, String extension) { return String.format(Locale.US, "%08d%s", index + 1, extension); } @Nullable private static UniFile findImageFile(UniFile dir, int index) { for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { String filename = generateImageFilename(index, extension); UniFile file = dir.findFile(filename); if (file != null) { return file; } } return null; } private boolean containInDownloadDir(int index) { UniFile dir = getDownloadDir(); if (dir == null) { return false; } // Find image file in download dir return findImageFile(dir, index) != null; } /** * @param extension with dot */ private String fixExtension(String extension) { if (Utilities.contain(GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS, extension)) { return extension; } else { return GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS[0]; } } private boolean copyFromCacheToDownloadDir(int index) { if (sCache == null) { return false; } UniFile dir = getDownloadDir(); if (dir == null) { return false; } // Find image file in cache String key = EhCacheKeyFactory.getImageKey(mGid, index); InputStreamPipe pipe = sCache.getInputStreamPipe(key); if (pipe == null) { return false; } OutputStream os = null; try { // Get extension String extension; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; pipe.obtain(); BitmapFactory.decodeStream(pipe.open(), null, options); pipe.close(); extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(options.outMimeType); if (extension != null) { extension = '.' + extension; } else { return false; } // Fix extension extension = fixExtension(extension); // Copy from cache to download dir UniFile file = dir.createFile(generateImageFilename(index, extension)); if (file == null) { return false; } os = file.openOutputStream(); IOUtils.copy(pipe.open(), os); return true; } catch (IOException e) { return false; } finally { IOUtils.closeQuietly(os); pipe.close(); pipe.release(); } } public boolean contain(int index) { if (mMode == SpiderQueen.MODE_READ) { return containInCache(index) || containInDownloadDir(index); } else if (mMode == SpiderQueen.MODE_DOWNLOAD) { return containInDownloadDir(index) || copyFromCacheToDownloadDir(index); } else { return false; } } private boolean removeFromCache(int index) { if (sCache == null) { return false; } String key = EhCacheKeyFactory.getImageKey(mGid, index); return sCache.remove(key); } private boolean removeFromDownloadDir(int index) { UniFile dir = getDownloadDir(); if (dir == null) { return false; } boolean result = false; for (int i = 0, n = GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS.length; i < n; i++) { String filename = generateImageFilename(index, GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS[i]); UniFile file = dir.subFile(filename); if (file != null) { result |= file.delete(); } } return result; } public boolean remove(int index) { boolean result = removeFromCache(index); result |= removeFromDownloadDir(index); return result; } @Nullable private OutputStreamPipe openCacheOutputStreamPipe(int index) { if (sCache == null) { return null; } String key = EhCacheKeyFactory.getImageKey(mGid, index); return sCache.getOutputStreamPipe(key); } /** * @param extension without dot */ @Nullable private OutputStreamPipe openDownloadOutputStreamPipe(int index, @Nullable String extension) { UniFile dir = getDownloadDir(); if (dir == null) { return null; } extension = fixExtension('.' + extension); UniFile file = dir.createFile(generateImageFilename(index, extension)); if (file != null) { return new UniFileOutputStreamPipe(file); } else { return null; } } @Nullable public OutputStreamPipe openOutputStreamPipe(int index, @Nullable String extension) { if (mMode == SpiderQueen.MODE_READ) { return openCacheOutputStreamPipe(index); } else if (mMode == SpiderQueen.MODE_DOWNLOAD) { return openDownloadOutputStreamPipe(index, extension); } else { return null; } } @Nullable private InputStreamPipe openCacheInputStreamPipe(int index) { if (sCache == null) { return null; } String key = EhCacheKeyFactory.getImageKey(mGid, index); return sCache.getInputStreamPipe(key); } @Nullable public InputStreamPipe openDownloadInputStreamPipe(int index) { UniFile dir = getDownloadDir(); if (dir == null) { return null; } for (int i = 0; i < 2; i++) { UniFile file = findImageFile(dir, index); if (file != null) { return new UniFileInputStreamPipe(file); } else if (!copyFromCacheToDownloadDir(index)) { return null; } } return null; } @Nullable public InputStreamPipe openInputStreamPipe(int index) { if (mMode == SpiderQueen.MODE_READ) { InputStreamPipe pipe = openCacheInputStreamPipe(index); if (pipe == null) { pipe = openDownloadInputStreamPipe(index); } return pipe; } else if (mMode == SpiderQueen.MODE_DOWNLOAD) { return openDownloadInputStreamPipe(index); } else { return null; } } }