package com.erakk.lnreader.helper; import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.os.StatFs; import android.util.Log; import com.erakk.lnreader.Constants; import com.erakk.lnreader.LNReaderApplication; import com.erakk.lnreader.R; import com.erakk.lnreader.UIHelper; import com.erakk.lnreader.callback.CallbackEventData; import com.erakk.lnreader.callback.ICallbackNotifier; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.channels.FileChannel; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; public class Util { private static final String TAG = Util.class.toString(); /** * Show date/time difference in words. * * @param date * @return */ public static String formatDateForDisplay(Context context, Date date) { // validate input if (date == null) return "No Date available"; // Setup Date now = new Date(); long dif = now.getTime() - date.getTime(); if (dif < 0) return "Unknown"; dif /= 1000; // convert from milliseconds to seconds if (dif < 60) return context.getResources().getString(R.string.timespan_seconds); // <1 minute ago dif /= 60; // convert from seconds to minutes if (dif < 60) return context.getResources().getQuantityString(R.plurals.timespan_minutes, (int) dif, (int) dif); dif /= 60; // convert from minutes to hours if (dif < 24) return context.getResources().getQuantityString(R.plurals.timespan_hours, (int) dif, (int) dif); dif /= 24; // convert from hours to days if (dif < 7) return context.getResources().getQuantityString(R.plurals.timespan_days, (int) dif, (int) dif); dif /= 7; // convert from days to weeks if (dif < 30) return context.getResources().getQuantityString(R.plurals.timespan_weeks, (int) dif, (int) dif); dif /= 30; // convert from weeks to months if (dif < 12) return context.getResources().getQuantityString(R.plurals.timespan_months, (int) dif, (int) dif); dif /= 12; // convert from months to years return context.getResources().getQuantityString(R.plurals.timespan_years, (int) dif, (int) dif); } public static void copyFile(String src, String dst) throws IOException { copyFile(new File(src), new File(dst)); } /** * Copy file * * @param src * @param dst * @throws IOException */ public static void copyFile(File src, File dst) throws IOException { FileChannel inChannel = null; FileChannel outChannel = null; try { inChannel = new FileInputStream(src).getChannel(); if (!dst.exists()) { Log.w(TAG, "Destination File doesn't exists, try to create dummy file"); boolean result = dst.createNewFile(); if (!result) Log.e(TAG, "Failed ot create file"); } outChannel = new FileOutputStream(dst).getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); } finally { if (inChannel != null) inChannel.close(); if (outChannel != null) outChannel.close(); } } /** * http://stackoverflow.com/questions/6350158/check-arraylist-for-instance- * of-object * * @param arrayList * @param clazz * @return */ public static boolean isInstanceOf(Collection<?> arrayList, Class<?> clazz) { for (Object o : arrayList) { if (o != null && o.getClass() == clazz) { return true; } } return false; } /** * Join collection with given separator into string. * * @param s * @param delimiter * @return */ public static String join(Collection<?> s, String delimiter) { StringBuilder builder = new StringBuilder(); Iterator<?> iter = s.iterator(); while (iter.hasNext()) { builder.append(iter.next()); if (!iter.hasNext()) { break; } builder.append(delimiter); } return builder.toString(); } public static String UrlEncode(String param) throws UnsupportedEncodingException { if (!param.contains("%")) { param = URLEncoder.encode(param, "utf-8"); } return param; } public static boolean isStringNullOrEmpty(String input) { if (input == null || input.length() == 0) return true; return false; } /** * Remove | \ ? * < " : > / from filename * * @param filename * @return */ public static String sanitizeFilename(String filename) { return filename.replaceAll("[\\|\\\\?*<\\\":>]", "_"); } public static int tryParseInt(String input, int def) { try { return Integer.parseInt(input); } catch (NumberFormatException ex) { return def; } } public static List<File> getListFiles(File parentDir, Long[] totalSize, ICallbackNotifier callback) { ArrayList<File> inFiles = new ArrayList<File>(); if (parentDir != null) { try { File[] files = parentDir.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { inFiles.addAll(getListFiles(file, totalSize, callback)); } else { inFiles.add(file); totalSize[0] += file.length(); } } } } catch (Exception ex) { Log.e(TAG, "Failed to get files at " + parentDir.getAbsolutePath(), ex); if (callback != null) callback.onProgressCallback(new CallbackEventData("Failed to get files: " + ex.getMessage(), parentDir.getAbsolutePath())); } } return inFiles; } public static void zipFiles(List<File> filenames, String zipFile, String replacedRootPath, ICallbackNotifier callback) throws IOException { BufferedInputStream origin = null; FileOutputStream dest = new FileOutputStream(zipFile); ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); byte data[] = new byte[Constants.BUFFER]; int fileCount = 1; int total = filenames.size(); Log.d(TAG, "Start zipping to: " + zipFile); for (File file : filenames) { String absPath = file.getAbsolutePath(); String message = LNReaderApplication.getInstance().getApplicationContext().getResources() .getString(R.string.zip_files_task_progress_count, fileCount, total, absPath); Log.d(TAG, message); if (callback != null) callback.onProgressCallback(new CallbackEventData(message, "Util.zipFiles()")); FileInputStream fi = new FileInputStream(file); origin = new BufferedInputStream(fi, Constants.BUFFER); ZipEntry entry = new ZipEntry(absPath.replace(replacedRootPath, "")); out.putNextEntry(entry); int count; while ((count = origin.read(data, 0, Constants.BUFFER)) != -1) { out.write(data, 0, count); } origin.close(); ++fileCount; } out.close(); Log.d(TAG, "Completed zipping to: " + zipFile); } public static void unzipFiles(String zipName, String rootPath, ICallbackNotifier callback) throws FileNotFoundException, IOException { InputStream is = new FileInputStream(zipName); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); ZipEntry ze; byte[] buffer = new byte[Constants.BUFFER]; int count; Log.d(TAG, "Start unzipping: " + zipName + " to: " + rootPath); // create root path File root = new File(rootPath); root.mkdirs(); int fileCount = 1; while ((ze = zis.getNextEntry()) != null) { String filename = rootPath + ze.getName(); // Need to create directories if not exists, or // it will generate an Exception... if (ze.isDirectory()) { Log.d(TAG, "Creating dir1: " + filename); File fmd = new File(filename); fmd.mkdirs(); continue; } // check if target dir exist String dir = filename.substring(0, filename.lastIndexOf("/")); File rootDir = new File(dir); if (!rootDir.exists()) { Log.d(TAG, "Creating dir2: " + dir); rootDir.mkdirs(); } Log.d(TAG, "Unzipping: " + filename); if (callback != null) { String message = LNReaderApplication.getInstance().getApplicationContext().getResources() .getString(R.string.unzip_files_task_progress_count, fileCount, filename); callback.onProgressCallback(new CallbackEventData(message, "Util.unzipFiles()")); } FileOutputStream fout = new FileOutputStream(filename); while ((count = zis.read(buffer)) != -1) { fout.write(buffer, 0, count); } fout.close(); zis.closeEntry(); ++fileCount; } zis.close(); Log.d(TAG, "Completed unzipping to: " + zipName); } public static String calculateCRC32(String input) { CRC32 crc = new CRC32(); crc.reset(); crc.update(input.getBytes()); return Long.toHexString(crc.getValue()); } /** * http://stackoverflow.com/a/3758880 * * @param bytes * @param si * @return */ public static String humanReadableByteCount(long bytes, boolean si) { int unit = si ? 1000 : 1024; if (bytes < unit) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } @SuppressLint("NewApi") public static long getFreeSpace(File path) { long availableSpace = -1L; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { availableSpace = path.getFreeSpace(); Log.d(TAG, "Free Space method 1: " + availableSpace); } if (availableSpace <= 0) { try { StatFs stat = new StatFs(path.getPath()); stat.restat(path.getPath()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { availableSpace = stat.getAvailableBytes(); Log.d(TAG, "Free Space method 2: " + availableSpace); } else { long availableBlock = stat.getAvailableBlocks(); long blockSize = stat.getBlockSize(); availableSpace = availableBlock * blockSize; Log.d(TAG, "Free Space method 3: " + availableBlock + " * " + blockSize + " = " + availableSpace); } } catch (Exception e) { Log.e(TAG, "Failed to get free space.", e); } } return availableSpace; } public static String convertStreamToString(InputStream is) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.toString(); } public static String getStringFromFile(String filePath) throws Exception { File fl = new File(filePath); FileInputStream fin = new FileInputStream(fl); String ret = convertStreamToString(fin); // Make sure you close all streams. fin.close(); return ret; } /** * http://stackoverflow.com/a/7410956 */ public final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; /** * Create a keystore used by baka-tsuki * - 2016-10-15 = Let's Encrypt * * @return * @throws IOException */ public static SSLSocketFactory initMySecureSSL() throws IOException { SSLSocketFactory sslSocketFactory = null; // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted; try { trusted = KeyStore.getInstance("BKS"); } catch (KeyStoreException e1) { throw new IOException("Can't get instance of my keystore: " + e1.getMessage()); } // Get the raw resource, which contains the keystore with // your trusted certificates (root and any intermediate certs) InputStream in = LNReaderApplication.getInstance().getResources().openRawResource(R.raw.keystore); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "com.erakk.lnreader".toCharArray()); String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(trusted); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); sslSocketFactory = context.getSocketFactory(); } catch (KeyManagementException e) { throw new IOException("Can't create my trust manager: " + e.getMessage()); } catch (NoSuchAlgorithmException e) { throw new IOException("Can't create my trust manager: " + e.getMessage()); } catch (CertificateException e) { throw new IOException("Can't create my trust manager: " + e.getMessage()); } catch (KeyStoreException e) { throw new IOException("Can't create my trust manager: " + e.getMessage()); } finally { in.close(); } return sslSocketFactory; } public static String getBaseWacName(String url) { url = SanitizeBaseUrl(url); Log.d(TAG, "Using base url: " + url); return Util.calculateCRC32(url); } public static String SanitizeBaseUrl(String url) { return SanitizeBaseUrl(url, true); } public static String SanitizeBaseUrl(String url, boolean stripAnchor) { if (stripAnchor) { // strip anchor link if (url.contains("#")) { url = url.substring(0, url.indexOf('#')); Log.d(TAG, "Stripping anchor tag"); } } // strip mobile redirect for blogspot if (url.contains(".blogspot.")) { url = url.replace("?m=1", ""); Log.d(TAG, "Stripping mobile redirection parameter for blogspot."); if (!url.contains(".blogspot.com/")) { Log.d(TAG, "Stripping country redirection parameter for blogspot."); url = url.replaceFirst("blogspot.[a-z]+/", "blogspot.com/"); } } return url; } public static String getSavedWacName(String actualUrl) { String path = UIHelper.getImageRoot(LNReaderApplication.getInstance()) + "/wac"; String urlHttps = actualUrl.replace("http://", "https://"); String urlHttp = actualUrl.replace("https://", "http://"); String[] urls = {urlHttp, urlHttps}; for (String url : urls) { String filename = path + "/" + getBaseWacName(url); String extensions[] = {".wac", ".mht"}; for (String ext : extensions) { File temp = new File(filename + ext); if (temp.exists()) { Log.i(TAG, String.format("Web Archive found for %s: %s", url, temp.getAbsolutePath())); return temp.getAbsolutePath(); } } } Log.w(TAG, String.format("Web Archive not found for %s", actualUrl)); return null; } public static <T> int isInList(T item, List<T> list) { for (T i : list) { if (i.equals(item)) { return list.indexOf(i); } } return -1; } public static boolean isWifiConnected() { Context ctx = LNReaderApplication.getInstance().getApplicationContext(); ConnectivityManager connManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); return mWifi.isConnected(); } }