Java tutorial
/** * Utils.java Created on Oct 19, 2012 Copyright 2013 Michele Bonazza * <emmepuntobi@gmail.com> This file is part of WhatsHare. * * WhatsHare is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * WhatsHare. If not, see <http://www.gnu.org/licenses/>. */ package it.mb.whatshare; import java.util.List; import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; import android.net.ConnectivityManager; import android.util.Log; import android.view.MotionEvent; /** * Contains static methods that are used all over the code base. * * @author Michele Bonazza * */ public final class Utils { private static final int MAX_MESSAGE_LENGTH = 4000; private static final String TAG = "Whatshare"; private static boolean debuggableFlagChecked = false; private static boolean debugEnabled = false; /* * Static methods only! */ private Utils() { } /** * Checks whether the app that's running has the * <code>android:debuggable</code> flag set, and enables/disables all log * calls accordingly. * * @param context * the caller activity */ public static void checkDebug(Context context) { if (!debuggableFlagChecked) { debuggableFlagChecked = true; PackageManager manager = context.getPackageManager(); try { PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); debugEnabled = (info.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } catch (NameNotFoundException e) { e.printStackTrace(); // let's keep debug disabled } } // else it's already been checked once, don't check anymore } /** * A function to be executed on all items in a list. * * <p> * This is a verbose Java equivalent of lambda functions; it's verbose * because you must declare a (private anonymous) class to define the lamba * function. * * <p> * This is how you would use it: * * <blockquote> * * <pre> * List<MyClass> myList = Arrays.asList(new MyClass("one"), new MyClass("two")); * Utils.executeAll(myList, new LambdaFunction<MyClass>() { * public void execute(MyClass item) { * // call one of MyClass methods * item.foo(); * } * } * </pre> * * </blockquote> Where of course the {@link LambdaFunction} can be stored to * be reused several times. * * @author Michele Bonazza * * @param <T> * the type of elements in the list on which the lambda function * must be executed * @see Utils#callOnAll(List, LambdaFunction) * @see Utils#callOnAll(Object[], LambdaFunction) */ public static interface LambdaFunction<T> { /** * The function called on all items in the list. * * @param item * the item onto which the function must be called */ public void execute(T item); } /** * Logs the argument message as a debug string. * * <p> * This method splits the message into several in case the message is larger * than the largest String that LogCat can handle for a single print call. * * <p> * The argument <tt>message</tt> can be a format string, in which case * {@link String#format(String, Object...)} is called with the same * parameters passed to this method. * * @param message * the message to be logged * @param formatParms * zero or more arguments to fill the format string with, in case * <tt>message</tt> is a format string */ public static void debug(String message, Object... formatParms) { if (!debugEnabled) return; int offset = 0; if (formatParms.length > 0) { message = String.format(message, formatParms); } while (offset < message.length()) { Log.d(TAG, message.substring(offset, Math.min(offset + MAX_MESSAGE_LENGTH, message.length()))); offset += MAX_MESSAGE_LENGTH; } } /** * Logs the output of {@link #listToString(String, List)} in case it's not * <code>null</code>. * * @param listName * the name of the list to be logged * @param list * the list to be logged */ public static void debugList(String listName, List<?> list) { String out = listToString(listName, list); if (out != null) debug(out); } /** * Calls the argument lambda <tt>function</tt> on all items in the argument * <tt>list</tt>. * * @param <T> * the type of elemente in the list * @param list * the list onto which the lambda function must be executed * @param function * the function to be executed on all items in the list * @see LambdaFunction */ public static <T> void callOnAll(List<T> list, LambdaFunction<T> function) { for (T item : list) { function.execute(item); } } /** * Calls the argument lambda <tt>function</tt> on all items in the argument * array. * * @param <T> * the type of elemente in the list * @param items * the items onto which the lambda function must be executed * @param function * the function to be executed on all items in the list * @see LambdaFunction */ public static <T> void callOnAll(T[] items, LambdaFunction<T> function) { for (T item : items) { function.execute(item); } } /** * Returns the last element in the argument <tt>array</tt>. * * <p> * The last element is the one at index <tt>array.length - 1</tt>, or * <code>null</code> if <tt>array</tt> is empty or <code>null</code>. * * @param <T> * the type of objects stored into the array * @param array * the array from which to retrieve the last element * @return the last element in the array or <code>null</code> if * <tt>array</tt> is empty or <code>null</code> */ public static <T> T getLast(T[] array) { if (array == null || array.length == 0) { return null; } return array[array.length - 1]; } /** * Returns the last element in the argument <tt>list</tt>. * * <p> * The last element is the one at index <tt>list.size() - 1</tt>, or * <code>null</code> if <tt>list</tt> is empty or <code>null</code>. * * @param <T> * the type of objects stored into the list * @param list * the list from which to retrieve the last element * @return the last element in the list or <code>null</code> if * <tt>list</tt> is empty or <code>null</code> */ public static <T> T getLast(List<T> list) { if (list == null || list.isEmpty()) { return null; } return list.get(list.size() - 1); } /** * Returns a String that is empty in case the argument <tt>string</tt> is * <code>null</code>, the unmodified <tt>string</tt> otherwise. * * @param string * the string to be checked against <code>null</code> * @return the empty String if <tt>string</tt> is <code>null</code>, the * argument <tt>string</tt> unmodified otherwise */ public static String nonNull(final String string) { return string == null ? "" : string; } /** * An equivalent of Python's <code>str.join()</code> function on lists: it * returns a String which is the concatenation of the strings in the * argument array. The separator between elements is the string providing * this method. The separator is appended after the first element and it is * not after the last element. * * @param toJoin * the separator * @param list * a list of <code>Object</code>s on which * {@link Object#toString()} will be called * @return the concatenation of String representations of the objects in the * list */ public static String join(String toJoin, Object[] list) { if (list == null || list.length == 0) return ""; StringBuilder builder = new StringBuilder(); String delimiter = nonNull(toJoin); int i = 0; for (; i < (list.length - 1); i++) { if (list[i] != null) builder.append(list[i]); builder.append(delimiter); } builder.append(getLast(list)); return builder.toString(); } /** * An equivalent of Python's <code>str.join()</code> function on lists: it * returns a String which is the concatenation of the strings in the * argument list. The separator between elements is the string providing * this method. The separator is appended after the first element and it is * not after the last element. * * @param toJoin * the separator * @param list * a list of <code>Object</code>s on which * {@link Object#toString()} will be called * @return the concatenation of String representations of the objects in the * list */ public static String join(String toJoin, List<?> list) { if (list == null || list.isEmpty()) return ""; StringBuilder builder = new StringBuilder(); String delimiter = nonNull(toJoin); int i = 0; for (; i < list.size() - 1; i++) { if (list.get(i) != null) builder.append(list.get(i)); builder.append(delimiter); } builder.append(getLast(list)); return builder.toString(); } /** * An equivalent of Python's <code>str.join()</code> function on lists: it * returns a String which is the concatenation of the strings in the * argument list. The separator between elements is the string providing * this method. The separator is appended after the first element and it is * not after the last element. * * @param toJoin * the separator * @param set * a set of <code>Object</code>s on which * {@link Object#toString()} will be called * @return the concatenation of String representations of the objects in the * set */ public static String join(String toJoin, Set<?> set) { return join(toJoin, set.toArray()); } /** * Capitalizes a string. * * @param s * the string to be capitalized * @return the capitalized string */ static String capitalize(String s) { if (s == null || s.length() == 0) return ""; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } /** * Returns whether this device is currently connected to the Internet. * * @param context * the caller activity * @return <code>true</code> if the Internet is reachable from this device * at this time */ public static boolean isConnectedToTheInternet(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isAvailable() && cm.getActiveNetworkInfo().isConnected(); } /** * Checks whether <b>any</b> of the provided objects is <code>null</code>. * * @param objects * a number of objects of any kind of which a check against * <code>null</code> values is performed. * @return <code>true</code> if at least one of the arguments is * <code>null</code>. */ public static boolean isAnyNull(Object... objects) { for (Object o : objects) { if (o == null) return true; } return false; } /** * Checks whether the argument <tt>list</tt> is sorted according to its * natural ordering. * * <p> * If the list is not sorted this method follows a <i>fail-fast</i> fashion: * as soon as it finds an out-of-order element it returns <code>false</code>. * * @param <T> * the type of elements in the list * @param list * the list to be checked * @return <code>true</code> if <tt>list</tt> is <code>null</code>, empty, * or sorted according to its natural ordering */ public static <T extends Comparable<? super T>> boolean isSorted(List<T> list) { if (list == null || list.isEmpty()) return true; T last = list.get(0); T current; for (int i = 1; i < list.size(); i++) { current = list.get(i); if (current.compareTo(last) < 0) return false; last = current; } return true; } /** * Returns a string structured as such: <blockquote> * * <pre> * listName: item1, item2, item3, ..., itemN * </pre> * * </blockquote> where each <tt>item</tt> is the string returned by calling * {@link Object#toString()} on elements in the list. * * <p> * If <tt>list</tt> is empty, <code>null</code> is returned. * * @param listName * the name of the argument list * @param list * the list to be examined * @return a String as reported above or <code>null</code> if <tt>list</tt> * is empty or any of the arguments are <code>null</code> */ public static String listToString(String listName, List<?> list) { if (isAnyNull(listName, list)) return null; return list.isEmpty() ? null : listName + ": " + join(", ", list); } /** * Returns what <tt>PointF.toString()</tt> should have returned (without the * initial <tt>"PointF"</tt> that the <tt>toString()</tt> default * implementation returns). * * @param point * the point of which a String representation must be returned * @return a String containing the point's coordinates enclosed within * parentheses, or the string "null point" if <tt>point</tt> is * <code>null</code> */ public static String pointToString(PointF point) { if (point == null) return "null point"; return new StringBuilder("(").append(point.x).append(",").append(point.y).append(")").toString(); } /** * Returns what <tt>Point.toString()</tt> should have returned (without the * initial <tt>"Point"</tt> that the <tt>toString()</tt> default * implementation returns). * * @param point * the point of which a String representation must be returned * @return a String containing the point's coordinates enclosed within * parentheses, or the string "null point" if <tt>point</tt> is * <code>null</code> */ public static String pointToString(Point point) { if (point == null) return "null point"; return new StringBuilder("(").append(point.x).append(",").append(point.y).append(")").toString(); } /** * Applies the transformations stored in the array of float values to the * argument list of points. * * <p> * The float array can be obtained starting from a {@link Matrix} object by * calling <blockquote> * * <pre> * Matrix myMatrix; * float[] matrixValues = new float[9]; * myMatrix.getValues(matrixValues); * </pre> * * </blockquote> * * @param matrixValues * the values to apply to all points in the list * @param points * a list of points to which the transformations in the array * will be applied */ public static void applyMatrix(float[] matrixValues, List<PointF> points) { // variable names are the same used by Skia library final float tx = matrixValues[Matrix.MTRANS_X]; final float ty = matrixValues[Matrix.MTRANS_Y]; final float mx = matrixValues[Matrix.MSCALE_X]; final float my = matrixValues[Matrix.MSCALE_Y]; final float kx = matrixValues[Matrix.MSKEW_X]; final float ky = matrixValues[Matrix.MSKEW_Y]; /* * if rotation: skia messes up with the matrix, so sx and sy actually * store cosV, rx and ry store -sinV and sinV */ for (PointF point : points) { final float originalY = point.y; point.y = point.x * ky + (point.y * my) + ty; point.x = point.x * mx + (originalY * kx) + tx; } } /** * Encodes the argument <tt>matrix</tt> into a JSON array. * * <p> * Values are cast to <tt>double</tt> because of JSON lack for a primitive * <tt>float</tt> value. * * @param matrix * the matrix to be encoded * @return the matrix encoded into a JSON array, or <code>null</code> if * <tt>matrix</tt> is <code>null</code> or inconsistent (i.e. it * doesn't contain <tt>float</tt>'s) */ public static JSONArray matrixToJson(Matrix matrix) { if (matrix == null) return null; JSONArray array = new JSONArray(); float[] values = new float[9]; matrix.getValues(values); for (float value : values) { try { array.put(value); } catch (JSONException e) { e.printStackTrace(); return null; } } return array; } /** * Decodes a matrix encoded using {@link #matrixToJson(Matrix)} from JSON * format to a {@link Matrix} object. * * @param array * the encoded matrix * @return a matrix containing values from the JSON string (probably not * 100% equal to the original because of the * <tt>float --> double --> float</tt> conversion) or * <code>null</code> if <tt>array</tt> is <code>null</code> or * doesn't contain a matrix */ public static Matrix jsonToMatrix(JSONArray array) { if (array == null) return null; float[] values = new float[9]; Matrix matrix = new Matrix(); for (int i = 0; i < array.length(); i++) { try { values[i] = (float) array.getDouble(i); } catch (JSONException e) { e.printStackTrace(); return null; } } matrix.setValues(values); return matrix; } /** * Returns the geometric distance between the two argument points. * * @param point1 * the first point * @param point2 * the second point * @return the distance between the two points * @throws NullPointerException * if any of the argument points is <code>null</code> */ public static float getDistance(PointF point1, PointF point2) { if (point1.x == point2.x) return Math.abs(point2.y - point1.y); if (point1.y == point2.y) return Math.abs(point2.x - point1.x); return (float) Math.sqrt((Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2))); } /** * Returns a string that represents the symbolic name of the specified * action such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or an equivalent * numeric constant such as "35" if unknown. By Google. * * @param action * The action. * @return The symbolic name of the specified action. */ public static String actionToString(int action) { switch (action) { case MotionEvent.ACTION_DOWN: return "ACTION_DOWN"; case MotionEvent.ACTION_UP: return "ACTION_UP"; case MotionEvent.ACTION_CANCEL: return "ACTION_CANCEL"; case MotionEvent.ACTION_OUTSIDE: return "ACTION_OUTSIDE"; case MotionEvent.ACTION_MOVE: return "ACTION_MOVE"; case MotionEvent.ACTION_HOVER_MOVE: return "ACTION_HOVER_MOVE"; case MotionEvent.ACTION_SCROLL: return "ACTION_SCROLL"; case MotionEvent.ACTION_HOVER_ENTER: return "ACTION_HOVER_ENTER"; case MotionEvent.ACTION_HOVER_EXIT: return "ACTION_HOVER_EXIT"; } int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN: return "ACTION_POINTER_DOWN(" + index + ")"; case MotionEvent.ACTION_POINTER_UP: return "ACTION_POINTER_UP(" + index + ")"; default: return Integer.toString(action); } } }