package moe.feng.nhentai.cache.file; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.util.Log; import com.google.gson.Gson; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Locale; import moe.feng.nhentai.R; import moe.feng.nhentai.api.BookApi; import moe.feng.nhentai.api.common.NHentaiUrl; import moe.feng.nhentai.cache.common.Constants; import moe.feng.nhentai.model.Book; import moe.feng.nhentai.ui.HomeActivity; import static moe.feng.nhentai.BuildConfig.DEBUG; public class FileCacheManager { private static final String TAG = FileCacheManager.class.getSimpleName(); private static FileCacheManager sInstance; private File mCacheDir, mExternalDir; private FileCacheManager(Context context) { try { mCacheDir = context.getExternalCacheDir(); } catch (Exception e) { e.printStackTrace(); } if (mCacheDir == null) { String cacheAbsDir = "/Android/data" + context.getPackageName() + "/cache/"; mCacheDir = new File(Environment.getExternalStorageDirectory().getPath() + cacheAbsDir); } if (mExternalDir == null) { String externalAbsDir = "/NHBooks/"; mExternalDir = new File(Environment.getExternalStorageDirectory().getPath() + externalAbsDir); } } public static FileCacheManager getInstance(Context context) { if (sInstance == null) { sInstance = new FileCacheManager(context); } return sInstance; } public boolean createCacheFromNetwork(String type, String url, String title) { if (DEBUG) { Log.d(TAG, "requesting cache from " + url); } URL u; try { u = new URL(url); } catch (MalformedURLException e) { return false; } HttpURLConnection conn; try { conn = (HttpURLConnection) u.openConnection(); conn.setRequestProperty("User-Agent",HomeActivity.myContext.getString(R.string.user_agent)); conn.connect(); } catch (IOException e) { return false; } conn.setConnectTimeout(5000); try { if (conn.getResponseCode() != 200) { if (url.contains("jpg")) { try { u = new URL(url.replace("jpg", "png")); } catch (MalformedURLException ex) { return false; } try { conn = (HttpURLConnection) u.openConnection(); conn.setRequestProperty("User-Agent",HomeActivity.myContext.getString(R.string.user_agent)); } catch (IOException ex) { return false; } } else { return false; } } } catch (IOException e) { e.printStackTrace(); } try { return createCacheFromStrem(type, getCacheName(url),title, conn.getInputStream()); } catch (IOException e) { return false; } } public boolean createCacheFromBook(Book book){ String path = mCacheDir.getAbsolutePath() + "/Books/" + book.bookId; File d = new File(path); if (!d.exists()){ if(!d.mkdirs()){ Log.i(TAG, "createCacheFromBook: Error Creating Cache Directory"); } else{ Log.i(TAG, "createCacheFromBook: Cache Directory created succesfully"); } } else if(!d.isDirectory()) { if(!d.mkdirs()){ Log.i(TAG, "createCacheFromBook: Error Creating Cache Directory"); } } File f = new File(path + "/book.json"); if(!f.exists()){ try { OutputStream out = new FileOutputStream(f); out.write(book.toJSONString().getBytes()); out.close(); } catch (IOException e) { e.printStackTrace(); return false; } } return true; } public boolean createCacheFromStrem(String type, String name, String title, InputStream stream) { File f = new File(getCachePath(type, name, title) + "_downloading"); if(f.getParentFile().mkdirs()){ Log.i(TAG, "createCacheFromStrem: Direcotry created correctly"); } if (f.exists()) { if(f.delete()){ Log.i(TAG, "File Deleted Correctly "); } } try { if(!f.createNewFile()){ Log.i(TAG, "createCacheFromStrem: Error creating new file"); } } catch (IOException e) { return false; } FileOutputStream opt; try { opt = new FileOutputStream(f); } catch (FileNotFoundException e) { return false; } byte[] buf = new byte[512]; int len; try { while ((len = stream.read(buf)) != -1) { opt.write(buf, 0, len); } } catch (IOException e) { return false; } try { stream.close(); opt.close(); } catch (IOException e) { Log.d(TAG, "createCacheFromStrem: Cache couldnt be created"); } if(!f.renameTo(new File(getCachePath(type, name, title)))){ Log.d(TAG, "createCacheFromStrem: Error Renaming File"); } return true; } private boolean copy(File src, File dst) { try { InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } public boolean cacheExistsUrl(String type, String url,String title) { return cacheExists(type, getCacheName(url), title); } public boolean cacheExists(String type, String name, String title) { return new File(getCachePath(type, name, title)).isFile(); } public boolean externalPageExists(Book book, int page) { return new File(getExternalPagePath(book, page)).isFile(); } public boolean externalBookExists(Book book) { return new File(mExternalDir.getAbsolutePath()+ "/Books/" + book.bookId + "/book.json").isFile(); } public boolean isExternalBookAllDownloaded(Book source) { return getExternalBookDownloadedCount(source) == source.pageCount; } @SuppressWarnings("unused") public boolean deleteCacheUrl(String type, String url, String title) { return deleteCache(type, getCacheName(url), title); } public boolean deleteCache(String type, String name,String title) { return cacheExists(type, name, title) && new File(getCachePath(type, name, title)).delete(); } public FileInputStream openCacheStream(String type, String name, String title) { try { return new FileInputStream(new File(getCachePath(type, name, title))); } catch (IOException e) { return null; } } @SuppressWarnings("unused") public FileInputStream openCacheStreamUrl(String type, String url, String title) { return openCacheStream(type, getCacheName(url), title); } public Bitmap getBitmapFile(File f) { Bitmap result; FileInputStream ipt= null; try { ipt = new FileInputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); } if (ipt == null) return null; BitmapFactory.Options bounds = new BitmapFactory.Options(); bounds.inJustDecodeBounds=true; BitmapFactory.decodeStream(ipt,null,bounds); bounds.inSampleSize = calculateInSampleSize(bounds,720, 1280); Log.d(TAG, "getBitmap: " + bounds.inSampleSize); FileInputStream iptF; try { iptF = new FileInputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); iptF=null; } bounds.inJustDecodeBounds=false; result=BitmapFactory.decodeStream(iptF,null, bounds); try { ipt.close(); if (iptF!=null) iptF.close(); } catch (IOException e) { Log.d(TAG, "getBitmap: Cleaning error"); e.printStackTrace(); } return result; } @SuppressWarnings("unused") public Bitmap getBitmap(String type, String name, String title) { Bitmap result; FileInputStream ipt= openCacheStream(type, name, title); if (ipt == null) return null; BitmapFactory.Options bounds = new BitmapFactory.Options(); bounds.inJustDecodeBounds=true; BitmapFactory.decodeStream(ipt,null,bounds); bounds.inSampleSize = calculateInSampleSize(bounds,480, 640); FileInputStream iptF=openCacheStream(type,name, title); bounds.inJustDecodeBounds=false; result=BitmapFactory.decodeStream(iptF,null, bounds); try { ipt.close(); iptF.close(); } catch (IOException e) { Log.d(TAG, "getBitmap: Cleaning error"); e.printStackTrace(); } return result; } public File getBitmapAllowingExternalPic(Book book, int page) { File cache = new File(getCachePath(Constants.CACHE_PAGE_IMG, NHentaiUrl.getOriginPictureUrl(book.galleryId, Integer.toString(page)), book.bookId)); File external = new File(getExternalPagePath(book, page)); return external.isFile() ? external : cache; } public Bitmap getBitmapUrl(String type, String url, String title) { return getBitmap(type, getCacheName(url), title); } public Book getExternalBook(Book source) { File destination = new File(mExternalDir.getAbsolutePath()+ "/Books/"+ source.title +"/book.json"); if (destination.exists() && destination.isFile()){ try { FileInputStream ins = new FileInputStream(destination); byte b[] = new byte[(int) destination.length()]; if(ins.read(b)==-1){ Log.d(TAG, "getExternalBook: Error Reading External Book"); } ins.close(); Book book = new Gson().fromJson(new String(b), Book.class); if (book!=null) if (book.bookId.equals(source.bookId)) { Log.d(TAG, "Found bookId External: " + book.bookId); return book; } } catch (Exception e) { e.printStackTrace(); return null; } } else { return null; } return null; } public Book getCacheBook(Book source) { File destination = new File(mCacheDir.getAbsolutePath()+ "/Books/"+ source.title +"/book.json"); if (destination.exists() && destination.isFile()){ try { FileInputStream ins = new FileInputStream(destination); byte b[] = new byte[(int) destination.length()]; if(ins.read(b)==-1){ Log.d(TAG, "getExternalBook: Error Reading Cache Book"); } ins.close(); Book book = new Gson().fromJson(new String(b), Book.class); if (book!=null) if (book.bookId.equals(source.bookId)) { Log.d(TAG, "Found bookId Cache: " + book.bookId); return book; } } catch (Exception e) { e.printStackTrace(); return null; } } else { return null; } return null; } public boolean checkUpdate (){ File updateFile = new File(mExternalDir.getAbsolutePath()+ "/update.txt"); return updateFile.exists(); } public void deleteCache() { try { File dir = new File(mCacheDir.getAbsolutePath()); deleteDir(dir); } catch (Exception e) {} } public static boolean deleteDir(File dir) { if (dir != null && dir.isDirectory()) { String[] children = dir.list(); for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } return dir.delete(); } else if(dir!= null && dir.isFile()) { return dir.delete(); } else { return false; } } public ArrayList<Book> getExternalBooks() { File parentDir = new File(mExternalDir.getAbsolutePath()+ "/Books/"); if (parentDir.isDirectory()) { File[] files = parentDir.listFiles(); ArrayList<Book> result = new ArrayList<>(); for (File file : files) { if (file.isDirectory() && !file.getName().equals("null")) { File bookFile = new File(file.getAbsolutePath() + "/book.json"); if (bookFile.exists() && bookFile.isFile()) { try { FileInputStream ins = new FileInputStream(bookFile); byte b[] = new byte[(int) bookFile.length()]; if(ins.read(b)==-1){ Log.d(TAG, "getExternalBooks: Error reading file"); } ins.close(); Book book = Book.toBookFromJson(new String(b)); Log.d(TAG, "Found external bookId: " + book.bookId); result.add(book); } catch (Exception e) { e.printStackTrace(); } } } } return result; } else { return new ArrayList<>(); } } public void updateExternalBooks() { File parentDir = new File(mExternalDir.getAbsolutePath()+ "/Books/"); File newFolder = new File(mExternalDir.getAbsolutePath()+ "/Books/");; if (parentDir.isDirectory()) { File[] files = parentDir.listFiles(); for (File file : files) { if (file.isDirectory()) { File bookFile = new File(file.getAbsolutePath() + "/book.json"); if (bookFile.exists() && bookFile.isFile()) { try { FileInputStream ins = new FileInputStream(bookFile); byte b[] = new byte[(int) bookFile.length()]; if (ins.read(b) == -1) { Log.d(TAG, "getExternalBooks: Error reading file"); } ins.close(); Book book = Book.toBookFromJson(new String(b)); String id = book.bookId; newFolder = new File(mExternalDir.getAbsolutePath()+ "/Books/" + id); } catch (Exception e) { e.printStackTrace(); } } Boolean result = file.renameTo(newFolder); Log.d(TAG, "updateExternalBooks: "+ result); } } File updateFile = new File(mExternalDir.getAbsolutePath()+ "/update.txt"); try { OutputStream out = new FileOutputStream(updateFile); out.write(String.valueOf("updated").getBytes()); out.close(); } catch (IOException e) { e.printStackTrace(); } } } public int getExternalBookDownloadedCount(Book source) { Book book = getExternalBook(source); if (book != null && externalBookExists(book)) { File parentDir = new File(mExternalDir.getAbsolutePath()+ "/Books/" + book.bookId); String[] pngs = parentDir.list(new FilenameFilter() { @Override public boolean accept(File file, String s) { return s.endsWith(".png"); } }); return pngs != null ? pngs.length : 0; } else { return -1; } } public String getCacheName(String url) { return url.replaceAll("/", ".").replaceAll(":", ""); } public String getCachePath(String type, String name, String title) { return mCacheDir.getAbsolutePath() + "/Books/" +title +"/"+ type + "/" + name + ".cache"; } public String getExternalPath(Book book) { return mExternalDir.getAbsolutePath() + "/Books/" + book.bookId; } public String getExternalPagePath(Book book, int page) { return getExternalPath(book) + "/" + String.format(Locale.getDefault(),"%03d", page) + ".png"; } public boolean saveToExternalPath(Book book, int page) { String path = getExternalPagePath(book, page); String src = getCachePath(Constants.CACHE_PAGE_IMG, getCacheName(NHentaiUrl.getOriginPictureUrl(book.galleryId, Integer.toString(page))), book.bookId); File target = new File(path); File srcFile = new File(src); if (target.exists()&& target.isFile()){ Log.d(TAG, "saveToExternalPath: File Alredy Downloaded"); return true; } File targetParent = new File(mExternalDir.getAbsolutePath() + "/Books/" + book.bookId); if (targetParent.isFile()) { if(!targetParent.delete()){ Log.i(TAG, "saveToExternalPath: Error Removing Past File"); } } if (!targetParent.isDirectory()) { if(!targetParent.mkdirs()){ Log.i(TAG, "saveToExternalPath: Error create Parent Dir"); } } if (target.exists()) { if(!target.delete()){ Log.i(TAG, "saveToExternalPath: Error Deleting target"); } } return srcFile.isFile() && copy(srcFile, target); } public boolean saveBookDataToExternalPath(Book book) { String path = getExternalPath(book); File d = new File(path); if (!d.isDirectory()) { if(!d.delete()){ Log.i(TAG, "saveBookDataToExternalPath: Error deleting old file"); } if(!d.mkdirs()){ Log.i(TAG, "saveBookDataToExternalPath: Error creating New Folder"); } } File f = new File(path + "/book.json"); if(!f.exists()){ try { OutputStream out = new FileOutputStream(f); out.write(book.toJSONString().getBytes()); out.close(); Log.d(TAG, "saveBookDataToExternalPath: Wroted"); } catch (IOException e) { e.printStackTrace(); return false; } } return true; } public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } }