/** * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.andlib.utility; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ContentValues; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.text.InputType; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Comparator; import java.util.Map.Entry; /** * Android Utility Classes * * @author Tim Su * */ public class AndroidUtilities { private static final Logger log = LoggerFactory.getLogger(AndroidUtilities.class); public static final String SEPARATOR_ESCAPE = "!PIPE!"; //$NON-NLS-1$ public static final String SERIALIZATION_SEPARATOR = "|"; //$NON-NLS-1$ // --- utility methods /** Suppress virtual keyboard until user's first tap */ public static void suppressVirtualKeyboard(final TextView editor) { final int inputType = editor.getInputType(); editor.setInputType(InputType.TYPE_NULL); editor.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { editor.setInputType(inputType); editor.setOnTouchListener(null); return false; } }); } /** * Put an arbitrary object into a {@link ContentValues} */ public static void putInto(ContentValues target, String key, Object value) { if (value instanceof Boolean) { target.put(key, (Boolean) value); } else if (value instanceof Byte) { target.put(key, (Byte) value); } else if (value instanceof Double) { target.put(key, (Double) value); } else if (value instanceof Float) { target.put(key, (Float) value); } else if (value instanceof Integer) { target.put(key, (Integer) value); } else if (value instanceof Long) { target.put(key, (Long) value); } else if (value instanceof Short) { target.put(key, (Short) value); } else if (value instanceof String) { target.put(key, (String) value); } else { throw new UnsupportedOperationException("Could not handle type " + //$NON-NLS-1$ value.getClass()); } } /** * Put an arbitrary object into a {@link ContentValues} */ public static void putInto(Bundle target, String key, Object value) { if (value instanceof Boolean) { target.putBoolean(key, (Boolean) value); } else if (value instanceof Byte) { target.putByte(key, (Byte) value); } else if (value instanceof Double) { target.putDouble(key, (Double) value); } else if (value instanceof Float) { target.putFloat(key, (Float) value); } else if (value instanceof Integer) { target.putInt(key, (Integer) value); } else if (value instanceof Long) { target.putLong(key, (Long) value); } else if (value instanceof Short) { target.putShort(key, (Short) value); } else if (value instanceof String) { target.putString(key, (String) value); } } // --- serialization /** * Return index of value in array * @param array array to search * @param value value to look for */ public static int indexOf(TYPE[] array, TYPE value) { for(int i = 0; i < array.length; i++) { if (array[i].equals(value)) { return i; } } return -1; } /** * Serializes a content value into a string */ public static String contentValuesToSerializedString(ContentValues source) { StringBuilder result = new StringBuilder(); for(Entry entry : source.valueSet()) { addSerialized(result, entry.getKey(), entry.getValue()); } return result.toString(); } /** add serialized helper */ private static void addSerialized(StringBuilder result, String key, Object value) { result.append(key.replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)).append( SERIALIZATION_SEPARATOR); if(value instanceof Integer) { result.append('i').append(value); } else if(value instanceof Double) { result.append('d').append(value); } else if(value instanceof Long) { result.append('l').append(value); } else if(value instanceof String) { result.append('s').append(value.toString().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)); } else if (value instanceof Boolean) { result.append('b').append(value); } else { throw new UnsupportedOperationException(value.getClass().toString()); } result.append(SERIALIZATION_SEPARATOR); } /** * Serializes a {@link android.os.Bundle} into a string */ public static String bundleToSerializedString(Bundle source) { StringBuilder result = new StringBuilder(); if (source == null) { return null; } for(String key : source.keySet()) { addSerialized(result, key, source.get(key)); } return result.toString(); } /** * Turn ContentValues into a string */ public static ContentValues contentValuesFromSerializedString(String string) { if(string == null) { return new ContentValues(); } ContentValues result = new ContentValues(); fromSerialized(string, result, new SerializedPut() { @Override public void put(ContentValues object, String key, char type, String value) throws NumberFormatException { switch(type) { case 'i': object.put(key, Integer.parseInt(value)); break; case 'd': object.put(key, Double.parseDouble(value)); break; case 'l': object.put(key, Long.parseLong(value)); break; case 's': object.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR)); break; case 'b': object.put(key, Boolean.parseBoolean(value)); break; } } }); return result; } /** * Turn {@link android.os.Bundle} into a string */ public static Bundle bundleFromSerializedString(String string) { if(string == null) { return new Bundle(); } Bundle result = new Bundle(); fromSerialized(string, result, new SerializedPut() { @Override public void put(Bundle object, String key, char type, String value) throws NumberFormatException { switch(type) { case 'i': object.putInt(key, Integer.parseInt(value)); break; case 'd': object.putDouble(key, Double.parseDouble(value)); break; case 'l': object.putLong(key, Long.parseLong(value)); break; case 's': object.putString(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR)); break; case 'b': object.putBoolean(key, Boolean.parseBoolean(value)); break; } } }); return result; } public interface SerializedPut { void put(T object, String key, char type, String value) throws NumberFormatException; } private static void fromSerialized(String string, T object, SerializedPut putter) { String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); //$NON-NLS-1$ for(int i = 0; i < pairs.length; i += 2) { try { String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR); String value = pairs[i+1].substring(1); try { putter.put(object, key, pairs[i+1].charAt(0), value); } catch (NumberFormatException e) { // failed parse to number putter.put(object, key, 's', value); log.error(e.getMessage(), e); } } catch (IndexOutOfBoundsException e) { log.error(e.getMessage(), e); } } } /** * Turn ContentValues into a string */ public static ContentValues contentValuesFromString(String string) { if(string == null) { return null; } String[] pairs = string.split("="); ContentValues result = new ContentValues(); String key = null; for(int i = 0; i < pairs.length; i++) { String newKey; int lastSpace = pairs[i].lastIndexOf(' '); if(lastSpace != -1) { newKey = pairs[i].substring(lastSpace + 1); pairs[i] = pairs[i].substring(0, lastSpace); } else { newKey = pairs[i]; } if(key != null) { result.put(key.trim(), pairs[i].trim()); } key = newKey; } return result; } /** * Copy a file from one place to another * @throws Exception */ public static void copyFile(File in, File out) throws Exception { FileInputStream fis = new FileInputStream(in); FileOutputStream fos = new FileOutputStream(out); try { copyStream(fis, fos); } finally { fis.close(); fos.close(); } } /** * Copy stream from source to destination * @throws IOException */ public static void copyStream(InputStream source, OutputStream dest) throws IOException { int bytes; byte[] buffer; int BUFFER_SIZE = 1024; buffer = new byte[BUFFER_SIZE]; while ((bytes = source.read(buffer)) != -1) { if (bytes == 0) { bytes = source.read(); if (bytes < 0) { break; } dest.write(bytes); dest.flush(); continue; } dest.write(buffer, 0, bytes); dest.flush(); } } public static boolean preFroyo() { return !atLeastFroyo(); } public static boolean preGingerbreadMR1() { return !atLeastGingerbreadMR1(); } public static boolean preIceCreamSandwich() { return !atLeastIceCreamSandwich(); } public static boolean preHoneycomb() { return !atLeastHoneycomb(); } public static boolean preJellybean() { return !atLeastJellybean(); } public static boolean atLeastFroyo() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; } public static boolean atLeastGingerbread() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; } public static boolean atLeastGingerbreadMR1() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1; } public static boolean atLeastHoneycomb() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } public static boolean atLeastIceCreamSandwich() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; } public static boolean atLeastJellybean() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; } public static boolean atLeastLollipop() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } /** * Sort files by date so the newest file is on top */ public static void sortFilesByDateDesc(File[] files) { Arrays.sort(files, new Comparator() { @Override public int compare(File o1, File o2) { return Long.valueOf(o2.lastModified()).compareTo(o1.lastModified()); } }); } /** * Sleep, ignoring interruption. Before using this method, think carefully * about why you are ignoring interruptions. */ public static void sleepDeep(long l) { try { Thread.sleep(l); } catch (InterruptedException e) { // ignore } } /** * If you want to set a transition, please use this method rather than callApiMethod to ensure * you really pass an Activity-instance. * * @param activity the activity-instance for which to set the finish-transition * @param enterAnim the incoming-transition of the next activity * @param exitAnim the outgoing-transition of this activity */ public static void callOverridePendingTransition(Activity activity, int enterAnim, int exitAnim) { activity.overridePendingTransition(enterAnim, exitAnim); } /** * Capitalize the first character */ public static String capitalize(String string) { return string.substring(0, 1).toUpperCase() + string.substring(1); } /** * Dismiss the keyboard if it is displayed by any of the listed views * @param views - a list of views that might potentially be displaying the keyboard */ public static void hideSoftInputForViews(Context context, View...views) { InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); for (View v : views) { imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } /** * Wraps a call to Activity.unregisterReceiver in a try/catch block to prevent * exceptions being thrown if receiver was never registered with that activity */ public static void tryUnregisterReceiver(Activity activity, BroadcastReceiver receiver) { try { activity.unregisterReceiver(receiver); } catch (IllegalArgumentException e) { // Receiver wasn't registered for some reason log.error(e.getMessage(), e); } } /** * Returns the final word characters after the last '.' */ public static String getFileExtension(String file) { int index = file.lastIndexOf('.'); String extension = ""; if (index > 0) { extension = file.substring(index + 1); if (!extension.matches("\\w+")) { extension = ""; } } return extension; } }