Back to project page Resonos-Android-Framework.
The source code is released under:
Apache License
If you think the Android project Resonos-Android-Framework listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.resonos.apps.library.util; /*from w ww . j a v a 2 s . c o m*/ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.RectF; import android.util.FloatMath; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.resonos.app.library.R; import com.resonos.apps.library.App; import com.resonos.apps.library.file.AltFileHandle; import com.resonos.apps.library.model.Coord; /** * This class is a grouping of common, mostly math-related functions. * A name like 'MathUtils' would have been more informative, but due * to certain functions' frequent usage, a single-character was chosen * instead. * @author Chris * */ public class M { /** Easy acces PI float */ public static float PI = (float)Math.PI; /** * Suppress default constructor for noninstantiability */ private M() { throw new AssertionError(); } /** * Convert dips (density-independent pixels) into pixels using app density * @param Size in dp * @return Size in pixels */ public static int getPxFromDp(int dp) { return (int) (dp * App.DENSITY + 0.5); } /** * Bound a float, n, inside a range using one function call instead of two * Does no checking to ensure the minimum is below the maximum * @param n, the number to bound * @param min, the minimum of the range * @param max, the maximum of the range * @return */ public static float fit(float n, float min, float max) { return (n < min) ? min : ((n > max) ? max : n); } /** * Bound an int, n, inside a range using one function call instead of two * Does no checking to ensure the minimum is below the maximum * @param n, the number to bound * @param min, the minimum of the range * @param max, the maximum of the range * @return */ public static int fit(int n, int min, int max) { return (n < min) ? min : ((n > max) ? max : n); } /** * Use this function to safely recycle a bitmap and remove its reference as follows: * bmp = M.freeBitmap(bmp); * @param bmp, the bitmap to be recycled. No worries if null or already recycled; * @return always returns null */ public static Bitmap freeBitmap(Bitmap bmp) { if (bmp != null) if (!bmp.isRecycled()) bmp.recycle(); return null; } /** * Returns the distance between two points given as 4 floats, without any double conversion * @param x1 * @param y1 * @param x2 * @param y2 * @return the pythagorean distance */ public static float dist(float x1, float y1, float x2, float y2) { return FloatMath.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); } /** * Generates a string describing the distance between two unix timestamps in readable text * @param t, the older timestamp * @param now, the newer timestamp * @param cx, a context used to access String resources * @return a string describing the date / time difference */ public static String dateDiff(long t, long now, Context cx) { long diff = now - t; diff /= 1000; long diffSeconds = diff % 60; diff /= 60; long diffMinutes = diff % 60; diff /= 60; long diffHours = diff % 24; diff /= 24; long diffDays = diff; long diffWeeks = diff / 7; long diffMonths = diff / 30; long diffYears = diff / 365; if (diffYears > 0) { long years = (diffMonths > 6) ? (diffYears + 1) : diffYears; return (years == 1) ? cx.getString(R.string.time_year) : (cx.getString(R.string.time_years, years)); } else if (diffMonths > 0) { long months = ((diffDays % 30) >= 15) ? (diffMonths + 1) : diffMonths; return (months == 1) ? cx.getString(R.string.time_month) : (cx.getString(R.string.time_months, months)); } else if (diffWeeks > 0) { long weeks = (diffDays > 4) ? (diffWeeks + 1) : diffWeeks; return (weeks == 1) ? cx.getString(R.string.time_week) : (cx.getString(R.string.time_weeks, weeks)); } else if (diffDays > 0) { long days = (diffHours > 12) ? (diffDays + 1) : diffDays; return (days == 1) ? cx.getString(R.string.time_day) : (cx.getString(R.string.time_days, days)); } else if (diffHours > 0) { long hours = (diffMinutes > 30) ? (diffHours + 1) : diffHours; return (hours == 24) ? cx.getString(R.string.time_day) : ((hours == 1) ? cx.getString(R.string.time_hour) : (cx.getString(R.string.time_hours, hours))); } else if (diffMinutes > 0) { long minutes = (diffSeconds > 30) ? (diffMinutes + 1) : diffMinutes; return (minutes == 60) ? cx.getString(R.string.time_hour) : ((minutes == 1) ? cx.getString(R.string.time_minute) : (cx.getString(R.string.time_minutes, minutes))); } else { return cx.getString(R.string.time_less_than_minute); } } /** * Generates a string describing the distance between a unix timestamps and now in readable text * @param time, the older timestamp in String form * @param cx, a context used to access String resources * @return a string describing the date / time difference */ public static String dateDiff(String time, Context cx) { long t; try { t = Long.parseLong(time)*1000; } catch (NumberFormatException ex) { return ""; } return dateDiff(t, System.currentTimeMillis(), cx); } /** * Blends two colors a specified amount (no bounds checking on parameters) * @param c1, the first color * @param c2, the second color * @param p, from 0 to 1, specifies the percent of each color * @return the blended color */ public static int blend(int c1, int c2, float p) { return Color.argb((int)((c1 >>> 24) + p*((c2 >>> 24)-(c1 >>> 24))), (int)(((c1 >> 16) & 0xFF) + p*(((c2 >> 16) & 0xFF)-((c1 >> 16) & 0xFF))), (int)(((c1 >> 8) & 0xFF) + p*(((c2 >> 8) & 0xFF)-((c1 >> 8) & 0xFF))), (int)((c1 & 0xFF) + p*((c2 & 0xFF)-(c1 & 0xFF))) ); } /** * Gets a contrasting color for a given color using the YIQ colorspace. * @param color : an input color * @return a contrasting color, always either black or white */ public static int getContrastColorYIQ(int color){ int yiq = ((Color.red(color)*299)+(Color.green(color)*587)+(Color.blue(color)*114))/1000; return (yiq >= 128) ? Color.BLACK : Color.WHITE; } /** * Fit a specified rectangle to the aspect ratio of another, unrelated rectangle * This does not scale the rectangle, only changes 2 sides in order to make the same shape * @param The size of the window to fit to in a Coord object * @param The coordinates of the rectangle to adjust * @return The supplied rectangle, available for chaining */ public static RectF fitRectInWindow(Coord windowSize, RectF coords) { float sizex = coords.width(); float sizey = coords.height(); float centerx = coords.centerX(); float centery = coords.centerY(); float targetRatio = sizex/sizey; float availRatio = windowSize.x/windowSize.y; if (targetRatio < availRatio) { float extra = (availRatio * sizey - sizex)/2f; coords.set(centerx - sizex/2f - extra, centery - sizey/2f, centerx + sizex/2f + extra, centery + sizey/2f); } else { float extra = (sizex / availRatio - sizey)/2f; coords.set(centerx - sizex/2f, centery - sizey/2f - extra, centerx + sizex/2f, centery + sizey/2f + extra); } return coords; } /** * Generates a string describing a unix timestamps in readable text * @param time, the unix timestamp * @param cx, a context used to access String resources * @return a string describing the date / time */ public static String formatTime(long time, Context cx) { long diffSeconds = (time / 1000) % 60; long diffMinutes = (time / (60 * 1000)) % 60; long diffHours = (time / (60 * 60 * 1000)) % 24; long diffDays = time / (24 * 60 * 60 * 1000); long diffWeeks = time / (7 * 24 * 60 * 60 * 1000); long diffMonths = time / (30 * 24 * 60 * 60 * 1000); long diffYears = time / (365 * 24 * 60 * 60 * 1000); if (diffYears > 0) { long years = (diffMonths > 6) ? (diffYears + 1) : diffYears; long months = (diffWeeks > 2) ? (diffMonths + 1) : diffMonths; return ((years == 1) ? cx.getString(R.string.time_year) : ("" + cx.getString(R.string.time_years, years))) + ((months == 1) ? (", " + cx.getString(R.string.time_month)) : (", " + cx.getString(R.string.time_months, months))); } else if (diffMonths > 0) { long months = (diffWeeks > 2) ? (diffMonths + 1) : diffMonths; long days = (diffHours > 12) ? (diffDays + 1) : diffDays; return ((months == 1) ? cx.getString(R.string.time_month) : ("" + cx.getString(R.string.time_months, months))) + ((days == 1) ? (", " + cx.getString(R.string.time_day)) : (", " + cx.getString(R.string.time_days, days))); } else if (diffDays > 0) { long days = (diffHours > 12) ? (diffDays + 1) : diffDays; long hours = (diffMinutes > 30) ? (diffHours + 1) : diffHours; return ((days == 1) ? cx.getString(R.string.time_day) : ("" + cx.getString(R.string.time_days, days))) + ((hours == 1) ? (", " + cx.getString(R.string.time_hour)) : (", " + cx.getString(R.string.time_hours, hours))); } else if (diffHours > 0) { long hours = (diffMinutes > 30) ? (diffHours + 1) : diffHours; long minutes = (diffSeconds > 30) ? (diffMinutes + 1) : diffMinutes; return (hours == 24) ? cx.getString(R.string.time_day) : (((hours == 1) ? cx.getString(R.string.time_hour) : ("" + cx.getString(R.string.time_hours, hours))) + ((minutes == 1) ? (", " + cx.getString(R.string.time_minute)) : (", " + cx.getString(R.string.time_minutes, minutes)))); } else if (diffMinutes > 0) { long minutes = (diffSeconds > 30) ? (diffMinutes + 1) : diffMinutes; return (minutes == 60) ? cx.getString(R.string.time_hour) : ((minutes == 1) ? cx.getString(R.string.time_minute) : ("" + cx.getString(R.string.time_minutes, minutes))); } else if (diffSeconds > 0) { return cx.getString(R.string.time_little); } else { return cx.getString(R.string.time_none); } } /** * Format a number to add separator commas, non localized * @param number : the input number * @return the output string */ public static String formatNumber(int number) { return formatNumber(String.valueOf(number)); } /** * Format a number to add separator commas, non localized * @param n : the input number, already converted to a string * @return the output string */ public static String formatNumber(String n) { StringBuilder sb = new StringBuilder(n.length() + n.length()/3); int m = n.length() % 3; for (int i = 0; i < n.length(); i++) { if (i != 0 && (i % 3) == m) sb.append(","); sb.append(n.charAt(i)); } return sb.toString(); } /** * A simple float comparison function with set delta of 0.001f * Not suitable for very large or small floats * @param a * @param b * @return true if equal, false if not */ public static boolean floatEquals(float a, float b) { return (Math.abs(a - b) < 0.001f); } /** * Rotate a point around the origin, returning the x component * @param x * @param y * @param a * @return newX */ public static float rotateX(float x, float y, float a) { return x * (float)Math.cos(a) - y * (float)Math.sin(a); } /** * Rotate a point around the origin, returning the y component * @param x * @param y * @param a * @return newY */ public static float rotateY(float x, float y, float a) { return x * (float)Math.sin(a) + y * (float)Math.cos(a); } /** * Checks if an integer is a power of two * @param value * @return true if power of two */ public static boolean isPowerOfTwo(int value) { if (value != 0) { return (value & -value) == value; } else { return false; } } /** * Calculates the next higher power of 2, given an integer * (or that integer if it is a power of 2) * @param x * @return closest power of 2 (higher only) */ static final public int nextPowerOfTwo(int x) { double val = (double) x; return (int) Math.pow(2, Math.ceil(log2(val))); } /** * log base 2 of a double * @param x * @return log base 2 */ static final public double log2(double x) { return Math.log(x) / Math.log(2); } /** * Converts a float to a String with the specified number of decimals * @param val * @param decimals * @return A String representation of the float */ public static String printFloat(float val, int decimals) { float f = (float)Math.pow(10, decimals); float rounded = (Math.round(f*val)/f); String r; if (floatEquals(rounded, Math.round(rounded))) r = String.valueOf((int)Math.round(rounded)); else r = String.valueOf(rounded); if (r.indexOf('.') == -1 && decimals > 0) r += '.'; int hasdec = r.length() - (r.indexOf('.') + 1); for (int i = 0; i < (decimals - hasdec); i++) r += '0'; return r; } /** * Use reflection to call a method (access checks still used); * @param class * @param method name * @param target object, null for static * @param parameters * @return */ @SuppressWarnings("rawtypes") public static Object callMethod(Class<?> c, String mn, Object object, Object ... params) { try { Class[] cs = new Class[params.length]; for (int i = 0; i< params.length; i++) cs[i] = params[i].getClass(); Method m = c.getDeclaredMethod(mn, cs); return m.invoke(object, params); //use null if the method is static } catch (Exception e) { M.log("callMethod", "callMethod failed on method " + mn + " of class " + c.getSimpleName()); return null; } } /** * Use reflection to get a field from a class (access checks still used) * @param class * @param field name * @param target object, null for static * @return */ public static Object getVar(Class<?> c, String fn, Object object) { try { Field f = c.getDeclaredField(fn); return f.get(object); //use null if the method is static } catch (Exception e) { M.log("getVar", "getVar failed on var " + fn + " of class " + c.getSimpleName()); return null; } } /** * Easy log function, only works in debug mode * @param tag * @param message */ public static void log(String tag, String msg) { if (App.DEBUG) Log.v(tag, msg); } /** * Easy log function for long strings, only works in debug mode * @param tag * @param message */ public static void logLong(String tag, String msg) { if (App.DEBUG) { int maxLogSize = 100; for(int i = 0; i <= msg.length() / maxLogSize; i++) { int start = i * maxLogSize; int end = (i+1) * maxLogSize; end = end > msg.length() ? msg.length() : end; Log.v(tag, msg.substring(start, end)); } } } /** * Easy log function, only works in debug mode * @param tag * @param message */ public static void loge(String tag, String msg) { if (App.DEBUG) Log.e(tag, msg); } /** * Kill the process this app is running in */ public static void killProcess() { android.os.Process.killProcess(android.os.Process.myPid()); } /** * Safely load a bitmap and scale it down to screen size * using the LibGDX FileHandle * @param the application object * @param libgdx file handle * @return */ public static Bitmap loadBitmapScreen(App app, AltFileHandle f) { return loadBitmapToSize(app, App.SCREEN_WIDTH, App.SCREEN_HEIGHT, f, null); } /** * Safely load a bitmap and scale it down to screen size * @param the application object * @param file object * @return */ public static Bitmap loadBitmapScreen(App app, File file) { return loadBitmapToSize(app, App.SCREEN_WIDTH, App.SCREEN_HEIGHT, null, file); } /** * Safely load a bitmap and scale it down to a target size * using the LibGDX FileHandle * @param the application object * @param width * @param height * @param libgdx file handle * @return */ public static Bitmap loadBitmapToSize(App app, int w, int h, AltFileHandle f) { return loadBitmapToSize(app, w, h, f, null); } /** * Safely load a bitmap and scale it down to a target size * @param the application object * @param width * @param height * @param file object * @return */ public static Bitmap loadBitmapToSize(App app, int w, int h, File file) { return loadBitmapToSize(app, w, h, null, file); } /** * Safely load a bitmap and scale it down to a target size * using the LibGDX FileHandle or a regular File object * @param the application object * @param width * @param height * @param libgdx file handle, or null * @param file object, or null * @return */ public static Bitmap loadBitmapToSize(App app, int scrW, int scrH, AltFileHandle f, File file) { Bitmap ret = null; InputStream fis1 = null, fis2 = null; try { fis1 = (f == null) ? new FileInputStream(file) : f.read(); //Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(fis1, null, o); int imgW = Math.max(o.outWidth, o.outHeight); int imgH = Math.min(o.outWidth, o.outHeight); try { fis1.close(); fis1 = null; } catch (Exception e) { } float scale = Math.max((float)scrW/(float)imgW, (float)scrH/(float)imgH); scale = (scale > 1) ? 1 : scale; // M.log("LoadBitmapScreen", "load bmp 1 (decode sizes): "+imgW+"x"+imgH+", screen: "+scrW+"x"+scrH+", scale: "+scale); // sampleSize must be a power of 2 int sampleSize=1; while ((sampleSize*2) <= (1/scale)) sampleSize *= 2; fis2 = (f == null) ? new FileInputStream(file) : f.read(); // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = sampleSize; o2.inPurgeable = true; o2.inInputShareable = true; o2.inTempStorage = new byte[16 * 1024]; Bitmap b = BitmapFactory.decodeStream(fis2, null, o2); // M.log("LoadBitmapScreen", "load bmp 2 (loaded) sampleSize: "+sampleSize+", inSize: "+b.getWidth()+"x"+b.getHeight()); ret = resizeBitmap(b, scrW, scrH); if (ret == null) ret = b; else if (ret != b) b.recycle(); } catch (Throwable t) { app.mError.report("loadBitmapScreen", t); } // make sure streams are closed if (fis1 != null) { try { fis1.close(); fis1 = null; } catch (Exception e) { } } try { fis2.close(); fis2 = null; } catch (Exception e) { } return ret; } /** * Resize a bitmap to a given size * @param the bitmap * @param new width * @param new height * @return the new scaled bitmap */ public static Bitmap resizeBitmap(Bitmap selImage, int w, int h) { if (selImage == null) return null; // begin resize process Matrix matrix = new Matrix(); int width = selImage.getWidth(); int height = selImage.getHeight(); boolean rotated = false; // rotate if in portrait if ((width < height && w > h) || (width > height && w < h)) { matrix.postRotate(90, width/2, height/2); int temp = width; width = height; height = temp; rotated = true; } // return if something isn't ready yet if (w == 0 || h == 0 || width == 0 || height == 0) return null; // find proper scale for image float xDiv = ((float)width)/((float)w); float yDiv = ((float)height)/((float)h); float div = Math.min(xDiv, yDiv); float scale = 1f/div; if (scale >= 1 && !rotated) return selImage; // offsets to center image int xOff = 0, yOff = 0; if (width > w/scale) xOff = Math.max(0, (int)((width - w/scale)/2)); if (height > h/scale) yOff = Math.max(0, (int)((height - h/scale)/2)); // do resize if (scale < 1) matrix.postScale(scale, scale); else { scale = 1; xOff = 0; yOff = 0; if (width > w) xOff = Math.max(0, (int)((width - w)/2)); if (height > h) yOff = Math.max(0, (int)((height - h)/2)); } Bitmap ret; try { ret = Bitmap.createBitmap(selImage, xOff, yOff, width - xOff*2, height - yOff*2, matrix, true); } catch (Exception ex) { ret = Bitmap.createScaledBitmap(selImage, w, h, true); } return ret; } /** * Recursively removes all drawables from a View tree. * Fixes android memory issue 8488 : * http://code.google.com/p/android/issues/detail?id=8488 * @param view : the View or ViewGroup to remove all images from */ public static void nullViewDrawablesRecursive(View view) { if (view != null) { try { ViewGroup viewGroup = (ViewGroup) view; int childCount = viewGroup.getChildCount(); for (int index = 0; index < childCount; index++) { View child = viewGroup.getChildAt(index); nullViewDrawablesRecursive(child); } } catch (Exception e) { } nullViewDrawable(view); } } /** * Removes any drawables from a View to allow for garbage collection * @param view : the View to remove images from */ public static void nullViewDrawable(View view) { try { view.setBackgroundDrawable(null); } catch (Exception e) { } try { ImageView imageView = (ImageView) view; imageView.setImageDrawable(null); imageView.setBackgroundDrawable(null); } catch (Exception e) { } } }