package com.duckduckgo.mobile.android.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.util.Log; import android.view.Window; import android.widget.Toast; import ch.boye.httpclientandroidlib.HttpEntity; import com.duckduckgo.mobile.android.DDGApplication; import com.duckduckgo.mobile.android.R; import com.duckduckgo.mobile.android.download.FileCache; import com.duckduckgo.mobile.android.fragment.AboutFragment; import com.duckduckgo.mobile.android.fragment.FavoriteFragment; import com.duckduckgo.mobile.android.fragment.FeedFragment; import com.duckduckgo.mobile.android.fragment.HelpFeedbackFragment; import com.duckduckgo.mobile.android.fragment.PrefFragment; import com.duckduckgo.mobile.android.fragment.RecentsFragment; import com.duckduckgo.mobile.android.fragment.SearchFragment; import com.duckduckgo.mobile.android.fragment.SourcesFragment; import com.duckduckgo.mobile.android.fragment.WebFragment; import com.duckduckgo.mobile.android.network.DDGHttpException; import com.duckduckgo.mobile.android.network.DDGNetworkConstants; import com.duckduckgo.mobile.android.views.webview.DDGWebView; /** * This class contains utility static methods, such as loading preferences as an array or decoding bitmaps. */ public final class DDGUtils { public static DisplayStats displayStats; public static boolean saveArray(SharedPreferences prefs, String[] array, String arrayName) { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(arrayName +"_size", array.length); for(int i=0;i<array.length;i++) editor.putString(arrayName + "_" + i, array[i]); return editor.commit(); } public static boolean saveList(SharedPreferences prefs, List<String> list, String listName) { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(listName +"_size", list.size()); int i=0; for(String s : list) { editor.putString(listName + "_" + i, s); ++i; } return editor.commit(); } public static boolean saveSet(SharedPreferences prefs, Set<String> set, String setName) { SharedPreferences.Editor editor = prefs.edit(); final int setSize = set.size(); editor.putInt(setName +"_size", setSize); int i=0; for(String s : set) { editor.putString(setName + "_" + i, s); ++i; } return editor.commit(); } public static String[] loadArray(SharedPreferences prefs, String arrayName) { int size = prefs.getInt(arrayName + "_size", 0); String array[] = new String[size]; for(int i=0;i<size;i++) array[i] = prefs.getString(arrayName + "_" + i, null); return array; } public static LinkedList<String> loadList(SharedPreferences prefs, String listName) { int size = prefs.getInt(listName + "_size", 0); LinkedList<String> list = new LinkedList<String>(); for(int i=0;i<size;i++) { list.add(prefs.getString(listName + "_" + i, null)); } return list; } public static Set<String> loadSet(SharedPreferences prefs, String setName) { final int size = prefs.getInt(setName + "_size", 0); Set<String> set = new HashSet<String>(size); for(int i=0;i<size;i++) set.add(prefs.getString(setName + "_" + i, null)); return set; } public static boolean existsSet(SharedPreferences prefs, String setName) { return prefs.contains(setName + "_size"); } public static void deleteSet(SharedPreferences prefs, String setName) { final int size = prefs.getInt(setName + "_size", 0); Editor editor = prefs.edit(); for(int i=0;i<size;i++) editor.remove(setName + "_" + i); editor.remove(setName + "_size"); editor.commit(); } static int calculateInSampleSize(BitmapFactory.Options bitmapOptions, int reqWidth, int reqHeight) { final int height = bitmapOptions.outHeight; final int width = bitmapOptions.outWidth; int sampleSize = 1; if (height > reqHeight || width > reqWidth) { final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); sampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return sampleSize; } public static Bitmap decodeImage(String filePath) { //Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, o); int scale=calculateInSampleSize(o, displayStats.maxItemWidthHeight, displayStats.maxItemWidthHeight); BitmapFactory.Options options=new BitmapFactory.Options(); //Decode with inSampleSize options.inSampleSize=scale; options.inPurgeable = true; options.inInputShareable = true; synchronized (DDGControlVar.DECODE_LOCK) { Bitmap result = BitmapFactory.decodeFile(filePath, options); return result; } } public static boolean downloadAndSaveBitmapToCache(AsyncTask<?, ?, ?> task, String url, String targetName) { final String TAG = "downloadAndSaveBitmapToCache"; FileCache fileCache = DDGApplication.getFileCache(); try { if (task.isCancelled()) return false; HttpEntity entity = null; try { entity = DDGNetworkConstants.mainClient.doGet(url); if (entity != null) { Log.v("SAVE", "Saving stream to internal file: " + url); fileCache.saveHttpEntityToCache(targetName, entity); return true; } } catch(DDGHttpException conex) { Log.e(TAG, "Http Call Returned Bad Status. " + conex.getHttpStatus()); throw conex; } } catch (DDGHttpException conException) { Log.e(TAG, conException.getMessage(), conException); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); } return false; } public static String readStream(InputStream is) { try { ByteArrayOutputStream bo = new ByteArrayOutputStream(); int i = is.read(); while(i != -1) { bo.write(i); i = is.read(); } return bo.toString(); } catch (IOException e) { return ""; } } public static Intent newEmailIntent(String toAddress, String subject, String body, String cc) { Intent intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse("mailto:" + Uri.encode(toAddress) + "?subject=" + Uri.encode(subject) + "&body=" + Uri.encode(body) + "&cc=" + Uri.encode(cc))); return intent; } public static Intent newTelIntent(String telurl) { Intent intent = new Intent(Intent.ACTION_DIAL); // FIXME : need to check XXX is really a short number in tel:XXX intent.setData(Uri.parse(telurl)); return intent; } public static String getBuildInfo(Context context) { // get app version info String appVersion = ""; PackageInfo pInfo; try { pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); appVersion = pInfo.versionName + " (" + pInfo.versionCode + ")\n"; } catch (NameNotFoundException e) {} String board = "Board: " + Build.BOARD + "\n"; String bootloader = "Bootloader: " + Build.BOOTLOADER + "\n"; String brand = "Brand: " + Build.BRAND + "\n"; String device = "Device: " + Build.DEVICE + "\n"; String display = "Display: " + Build.DISPLAY + "\n"; String product = "Product: " + Build.PRODUCT + "\n"; String model = "Model: " + Build.MODEL + "\n"; String manufacturer = "Manufacturer: " + Build.MANUFACTURER + "\n"; return appVersion + board + bootloader + brand + device + display + product + model + manufacturer; } public static Bitmap roundCorners(Bitmap bitmap, float radius) { Paint paint = new Paint(); paint.setAntiAlias(true); Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, radius, radius, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } public static List<AppShortInfo> getInstalledComponents(Context context) { final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mainIntent.removeCategory(Intent.CATEGORY_TEST); final List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities( mainIntent, 0); HashSet<String> packageNameSet = new HashSet<String>(); ArrayList<AppShortInfo> labels = new ArrayList<AppShortInfo>(); // Log.v("APP","al: ...start..."); for(ResolveInfo rInfo : pkgAppsList) { String packageName = rInfo.activityInfo.packageName; String label = ""; label += rInfo.loadLabel(context.getPackageManager()); if(!packageNameSet.contains(label + "-" + packageName)) { // Log.v("APP", "al: " + label + " " + packageName); labels.add(new AppShortInfo(label, packageName)); packageNameSet.add(label + "-" + packageName); } } // Log.v("APP","al: ...end..."); return labels; } public static void launchApp(Context context, String packageName) { Intent mIntent = context.getPackageManager().getLaunchIntentForPackage( packageName); if (mIntent != null) { try { context.startActivity(mIntent); } catch (ActivityNotFoundException err) { Toast t = Toast.makeText(context, R.string.ErrorAppNotFound, Toast.LENGTH_SHORT); t.show(); } } } /** * Checks to see if URL is DuckDuckGo SERP * Returns the query if it's a SERP, otherwise null * * @param url * @return */ static public String getQueryIfSerp(String url) { if(!isSerpUrl(url)) { return null; } Uri uri = Uri.parse(url); String query = uri.getQueryParameter("q"); if(query != null) return query; String lastPath = uri.getLastPathSegment(); if(lastPath == null) return null; if(!lastPath.contains(".html")) { return lastPath.replace("_", " "); } return null; } public static boolean isSerpUrl(String url) { return url.contains("duckduckgo.com"); } /** * Read cached sources from file cache * * @return Cached source set, if not readable from cache returns null */ static public Set<String> getCachedSources() { String body = DDGApplication.getFileCache().getStringFromInternal(DDGConstants.SOURCE_JSON_PATH); if(body == null) return null; try { JSONArray json = new JSONArray(body); Set<String> cachedSources = new HashSet<String>(); for (int i = 0; i < json.length(); i++) { JSONObject nextObj = json.getJSONObject(i); if (nextObj != null) { String id = nextObj.getString("id"); if(id != null && !id.equals("null")){ cachedSources.add(id); } } } return cachedSources; } catch (JSONException e) { return null; } } private static boolean isIntentSafe(Context context, Intent intent) { // Verify it resolves PackageManager packageManager = context.getPackageManager(); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); return activities.size() > 0; } public static void execIntentIfSafe(Context context, Intent intent) { if(DDGUtils.isIntentSafe(context, intent)) { context.startActivity(intent); } else { Toast.makeText(context, R.string.ErrorActivityNotFound, Toast.LENGTH_SHORT).show(); } } public static String getVersionName(Context context) { String versionName = ""; try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); versionName = packageInfo.versionName; } catch(NameNotFoundException e) { e.printStackTrace(); } return versionName; } public static int getVersionCode(Context context) { int versionCode = 0; try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); versionCode = packageInfo.versionCode; } catch(NameNotFoundException e) { e.printStackTrace(); } return versionCode; } public static void searchExternal(Context context, String term) { String url = DDGControlVar.mDuckDuckGoContainer.torIntegration.isTorSettingEnabled() ? DDGConstants.SEARCH_URL_ONION : DDGConstants.SEARCH_URL; if (DDGControlVar.regionString.equals("wt-wt")) { // default url = url.replace("ko=-1&", "") + URLEncoder.encode(term); } else { url = url.replace("ko=-1&", "") + URLEncoder.encode(term) + "&kl=" + URLEncoder.encode(DDGControlVar.regionString); } Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); execIntentIfSafe(context, browserIntent); } public static String getUrlToDisplay(String url) { if(url==null || url.length()==0) { return ""; } if (url.startsWith("https://")) { url = url.replace("https://", ""); } else if (url.startsWith("http://")) { url = url.replace("http://", ""); } if (url.startsWith("www.")) { url = url.replace("www.", ""); } return url; } public static SCREEN getScreenByTag(String tag) { if(tag.equals(RecentsFragment.TAG)) { return SCREEN.SCR_RECENTS; } else if(tag.equals(FavoriteFragment.TAG)) { return SCREEN.SCR_FAVORITE; } else if(tag.equals(WebFragment.TAG)) { return SCREEN.SCR_WEBVIEW; } else if(tag.equals(SearchFragment.TAG)) { return SCREEN.SCR_SEARCH; } else if(tag.equals(AboutFragment.TAG)) { return SCREEN.SCR_ABOUT; } else if(tag.equals(HelpFeedbackFragment.TAG)) { return SCREEN.SCR_HELP; } else if(tag.equals(PrefFragment.TAG)) { return SCREEN.SCR_SETTINGS; } else if(tag.equals(SearchFragment.TAG_HOME_PAGE)) { return SCREEN.SCR_SEARCH_HOME_PAGE; } else if(tag.equals(SourcesFragment.TAG)) { return SCREEN.SCR_SOURCES; } return SCREEN.SCR_STORIES; } public static String getTagByScreen(SCREEN screen) { switch(screen) { case SCR_STORIES: return FeedFragment.TAG; case SCR_RECENTS: return RecentsFragment.TAG; case SCR_FAVORITE: return FavoriteFragment.TAG; case SCR_WEBVIEW: return WebFragment.TAG; case SCR_SEARCH: return SearchFragment.TAG; case SCR_ABOUT: return AboutFragment.TAG; case SCR_HELP: return HelpFeedbackFragment.TAG; case SCR_SETTINGS: return PrefFragment.TAG; case SCR_SEARCH_HOME_PAGE: return SearchFragment.TAG_HOME_PAGE; case SCR_SOURCES: return SourcesFragment.TAG; default: return FeedFragment.TAG; } } public static int getStatusBarHeight(Activity activity) { Rect rect = new Rect(); Window window = activity.getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rect); return rect.top; } public static int getNavigationBarHeight(Context context) { int id = context.getResources().getIdentifier( context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android"); if (id > 0) { return context.getResources().getDimensionPixelSize(id); } return 0; } public static boolean isValidIpAddress(String input) { String ipv4Regex = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])"; String ipv6Regex = "([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}"; Pattern ipv4Pattern = Pattern.compile(ipv4Regex); Pattern ipv6Pattern = Pattern.compile(ipv6Regex); Matcher ipv4Matcher = ipv4Pattern.matcher(input); if(ipv4Matcher.matches()) return true; Matcher ipv6Matcher = ipv6Pattern.matcher(input); return ipv6Matcher.matches(); } }