package io.github.mthli.Ninja.Unit; import android.app.DownloadManager; import android.content.*; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.preference.PreferenceManager; import android.webkit.*; import io.github.mthli.Ninja.Browser.AdBlock; import io.github.mthli.Ninja.Database.Record; import io.github.mthli.Ninja.Database.RecordAction; import io.github.mthli.Ninja.R; import io.github.mthli.Ninja.View.NinjaToast; import java.io.*; import java.net.URLEncoder; import java.util.*; import java.util.regex.Pattern; public class BrowserUnit { public static final int PROGRESS_MAX = 100; public static final int PROGRESS_MIN = 0; public static final String SUFFIX_HTML = ".html"; public static final String SUFFIX_PNG = ".png"; public static final String SUFFIX_TXT = ".txt"; public static final int FLAG_BOOKMARKS = 0x100; public static final int FLAG_HISTORY = 0x101; public static final int FLAG_HOME = 0x102; public static final int FLAG_NINJA = 0x103; public static final String MIME_TYPE_TEXT_HTML = "text/html"; public static final String MIME_TYPE_TEXT_PLAIN = "text/plain"; public static final String MIME_TYPE_IMAGE = "image/*"; public static final String BASE_URL = "file:///android_asset/"; public static final String BOOKMARK_TYPE = "<DT><A HREF=\"{url}\" ADD_DATE=\"{time}\">{title}</A>"; public static final String BOOKMARK_TITLE = "{title}"; public static final String BOOKMARK_URL = "{url}"; public static final String BOOKMARK_TIME = "{time}"; public static final String INTRODUCTION_EN = "ninja_introduction_en.html"; public static final String INTRODUCTION_ZH = "ninja_introduction_zh.html"; public static final String SEARCH_ENGINE_GOOGLE = "https://www.google.com/search?q="; public static final String SEARCH_ENGINE_DUCKDUCKGO = "https://duckduckgo.com/?q="; public static final String SEARCH_ENGINE_STARTPAGE = "https://startpage.com/do/search?query="; public static final String SEARCH_ENGINE_BING = "http://www.bing.com/search?q="; public static final String SEARCH_ENGINE_BAIDU = "http://www.baidu.com/s?wd="; public static final String UA_DESKTOP = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"; public static final String URL_ENCODING = "UTF-8"; public static final String URL_ABOUT_BLANK = "about:blank"; public static final String URL_SCHEME_ABOUT = "about:"; public static final String URL_SCHEME_MAIL_TO = "mailto:"; public static final String URL_SCHEME_FILE = "file://"; public static final String URL_SCHEME_FTP = "ftp://"; public static final String URL_SCHEME_HTTP = "http://"; public static final String URL_SCHEME_HTTPS = "https://"; public static final String URL_SCHEME_INTENT = "intent://"; public static final String URL_PREFIX_GOOGLE_PLAY = "www.google.com/url?q="; public static final String URL_SUFFIX_GOOGLE_PLAY = "&sa"; public static final String URL_PREFIX_GOOGLE_PLUS = "plus.url.google.com/url?q="; public static final String URL_SUFFIX_GOOGLE_PLUS = "&rct"; public static boolean isURL(String url) { if (url == null) { return false; } url = url.toLowerCase(Locale.getDefault()); if (url.startsWith(URL_ABOUT_BLANK) || url.startsWith(URL_SCHEME_MAIL_TO) || url.startsWith(URL_SCHEME_FILE)) { return true; } String regex = "^((ftp|http|https|intent)?://)" // support scheme + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" // ftp的user@ + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" // IP形式的URL -> 199.194.52.184 + "|" // 允许IP和DOMAIN(域名) + "([0-9a-z_!~*'()-]+\\.)*" // 域名 -> www. + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." // 二级域名 + "[a-z]{2,6})" // first level domain -> .com or .museum + "(:[0-9]{1,4})?" // 端口 -> :80 + "((/?)|" // a slash isn't required if there is no file name + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"; Pattern pattern = Pattern.compile(regex); return pattern.matcher(url).matches(); } public static String queryWrapper(Context context, String query) { // Use prefix and suffix to process some special links String temp = query.toLowerCase(Locale.getDefault()); if (temp.contains(URL_PREFIX_GOOGLE_PLAY) && temp.contains(URL_SUFFIX_GOOGLE_PLAY)) { int start = temp.indexOf(URL_PREFIX_GOOGLE_PLAY) + URL_PREFIX_GOOGLE_PLAY.length(); int end = temp.indexOf(URL_SUFFIX_GOOGLE_PLAY); query = query.substring(start, end); } else if (temp.contains(URL_PREFIX_GOOGLE_PLUS) && temp.contains(URL_SUFFIX_GOOGLE_PLUS)) { int start = temp.indexOf(URL_PREFIX_GOOGLE_PLUS) + URL_PREFIX_GOOGLE_PLUS.length(); int end = temp.indexOf(URL_SUFFIX_GOOGLE_PLUS); query = query.substring(start, end); } if (isURL(query)) { if (query.startsWith(URL_SCHEME_ABOUT) || query.startsWith(URL_SCHEME_MAIL_TO)) { return query; } if (!query.contains("://")) { query = URL_SCHEME_HTTP + query; } return query; } try { query = URLEncoder.encode(query, URL_ENCODING); } catch (UnsupportedEncodingException u) {} SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); String custom = sp.getString(context.getString(R.string.sp_search_engine_custom), SEARCH_ENGINE_GOOGLE); final int i = Integer.valueOf(sp.getString(context.getString(R.string.sp_search_engine), "0")); switch (i) { case 0: return SEARCH_ENGINE_GOOGLE + query; case 1: return SEARCH_ENGINE_DUCKDUCKGO + query; case 2: return SEARCH_ENGINE_STARTPAGE + query; case 3: return SEARCH_ENGINE_BING + query; case 4: return SEARCH_ENGINE_BAIDU + query; case 5: return custom + query; default: return SEARCH_ENGINE_GOOGLE + query; } } public static String urlWrapper(String url) { if (url == null) { return null; } String green500 = "<font color='#4CAF50'>{content}</font>"; String gray500 = "<font color='#9E9E9E'>{content}</font>"; if (url.startsWith(BrowserUnit.URL_SCHEME_HTTPS)) { String scheme = green500.replace("{content}", BrowserUnit.URL_SCHEME_HTTPS); url = scheme + url.substring(8); } else if (url.startsWith(BrowserUnit.URL_SCHEME_HTTP)) { String scheme = gray500.replace("{content}", BrowserUnit.URL_SCHEME_HTTP); url = scheme + url.substring(7); } return url; } public static boolean bitmap2File(Context context, Bitmap bitmap, String filename) { try { FileOutputStream fileOutputStream = context.openFileOutput(filename, Context.MODE_PRIVATE); bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream); fileOutputStream.flush(); fileOutputStream.close(); } catch (Exception e) { return false; } return true; } public static Bitmap file2Bitmap(Context context, String filename) { try { FileInputStream fileInputStream = context.openFileInput(filename); return BitmapFactory.decodeStream(fileInputStream); } catch (Exception e) { return null; } } public static void copyURL(Context context, String url) { ClipboardManager manager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); ClipData data = ClipData.newPlainText(null, url.trim()); manager.setPrimaryClip(data); NinjaToast.show(context, R.string.toast_copy_successful); } public static void download(Context context, String url, String contentDisposition, String mimeType) { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); String filename = URLUtil.guessFileName(url, contentDisposition, mimeType); // Maybe unexpected filename. request.allowScanningByMediaScanner(); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setTitle(filename); request.setMimeType(mimeType); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(request); NinjaToast.show(context, R.string.toast_start_download); } public static String screenshot(Context context, Bitmap bitmap, String name) { if (bitmap == null) { return null; } File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); if (name == null || name.trim().isEmpty()) { name = String.valueOf(System.currentTimeMillis()); } name = name.trim(); int count = 0; File file = new File(dir, name + SUFFIX_PNG); while (file.exists()) { count++; file = new File(dir, name + "." + count + SUFFIX_PNG); } try { FileOutputStream stream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); stream.flush(); stream.close(); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); return file.getAbsolutePath(); } catch (Exception e) { return null; } } public static String exportBookmarks(Context context) { RecordAction action = new RecordAction(context); action.open(false); List<Record> list = action.listBookmarks(); action.close(); String filename = context.getString(R.string.bookmarks_filename); File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename + SUFFIX_HTML); int count = 0; while (file.exists()) { count++; file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename + "." + count + SUFFIX_HTML); } try { BufferedWriter writer = new BufferedWriter(new FileWriter(file, false)); for (Record record : list) { String type = BOOKMARK_TYPE; type = type.replace(BOOKMARK_TITLE, record.getTitle()); type = type.replace(BOOKMARK_URL, record.getURL()); type = type.replace(BOOKMARK_TIME, String.valueOf(record.getTime())); writer.write(type); writer.newLine(); } writer.close(); return file.getAbsolutePath(); } catch (Exception e) { return null; } } public static String exportWhitelist(Context context) { RecordAction action = new RecordAction(context); action.open(false); List<String> list = action.listDomains(); action.close(); String filename = context.getString(R.string.whilelist_filename); File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename + SUFFIX_TXT); int count = 0; while (file.exists()) { count++; file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename + "." + count + SUFFIX_TXT); } try { BufferedWriter writer = new BufferedWriter(new FileWriter(file, false)); for (String domain : list) { writer.write(domain); writer.newLine(); } writer.close(); return file.getAbsolutePath(); } catch (Exception e) { return null; } } public static int importBookmarks(Context context, File file) { if (file == null) { return -1; } List<Record> list = new ArrayList<>(); try { RecordAction action = new RecordAction(context); action.open(true); BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (!((line.startsWith("<dt><a ") && line.endsWith("</a>")) || (line.startsWith("<DT><A ") && line.endsWith("</A>")))) { continue; } String title = getBookmarkTitle(line); String url = getBookmarkURL(line); if (title.trim().isEmpty() || url.trim().isEmpty()) { continue; } Record record = new Record(); record.setTitle(title); record.setURL(url); record.setTime(System.currentTimeMillis()); if (!action.checkBookmark(record)) { list.add(record); } } reader.close(); Collections.sort(list, new Comparator<Record>() { @Override public int compare(Record first, Record second) { return first.getTitle().compareTo(second.getTitle()); } }); for (Record record : list) { action.addBookmark(record); } action.close(); } catch (Exception e) {} return list.size(); } public static int importWhitelist(Context context, File file) { if (file == null) { return -1; } AdBlock adBlock = new AdBlock(context); int count = 0; try { RecordAction action = new RecordAction(context); action.open(true); BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine().trim()) != null) { if (!action.checkDomain(line)) { adBlock.addDomain(line); count++; } } reader.close(); action.close(); } catch (Exception e) {} return count; } public static void clearBookmarks(Context context) { RecordAction action = new RecordAction(context); action.open(true); action.clearBookmarks(); action.close(); } public static boolean clearCache(Context context) { try { File dir = context.getCacheDir(); if (dir != null && dir.isDirectory()) { deleteDir(dir); } return true; } catch (Exception exception) { return false; } } // CookieManager.removeAllCookies() must be called on a thread with a running Looper. public static void clearCookie(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CookieManager cookieManager = CookieManager.getInstance(); cookieManager.flush(); cookieManager.removeAllCookies(new ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean value) {} }); } else { CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeAllCookie(); } } public static void clearFormData(Context context) { WebViewDatabase.getInstance(context).clearFormData(); } public static void clearHistory(Context context) { RecordAction action = new RecordAction(context); action.open(true); action.clearHistory(); action.close(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { WebIconDatabase.getInstance().removeAllIcons(); } } public static void clearPasswords(Context context) { WebViewDatabase.getInstance(context).clearHttpAuthUsernamePassword(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { WebViewDatabase.getInstance(context).clearUsernamePassword(); } } private static boolean deleteDir(File dir) { if (dir != null && dir.isDirectory()) { String[] children = dir.list(); for (String aChildren : children) { boolean success = deleteDir(new File(dir, aChildren)); if (!success) { return false; } } } return dir != null && dir.delete(); } private static String getBookmarkTitle(String line) { line = line.substring(0, line.length() - 4); // Remove last </a> int index = line.lastIndexOf(">"); return line.substring(index + 1, line.length()); } private static String getBookmarkURL(String line) { for (String string : line.split(" +")) { if (string.startsWith("href=\"") || string.startsWith("HREF=\"")) { return string.substring(6, string.length() - 1); // Remove href=\" and \" } } return ""; } }