/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2016 HydroloGIS (www.hydrologis.com) * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geopaparazzi.library.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Date; import java.util.HashMap; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Looper; import android.os.StatFs; import android.preference.PreferenceManager; import android.support.v4.content.FileProvider; import android.telephony.TelephonyManager; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import eu.geopaparazzi.library.R; import eu.geopaparazzi.library.core.ResourcesManager; import eu.geopaparazzi.library.core.activities.DirectoryBrowserActivity; import eu.geopaparazzi.library.database.DatabaseUtilities; import eu.geopaparazzi.library.database.GPLog; /** * Utilities class. * * @author Andrea Antonello (www.hydrologis.com) */ public class Utilities { public static final String GEOPAPARAZZI_LIBRARY_FILEPROVIDER_PLUS = ".library.fileprovider"; public static String getLastFilePath(Context context) throws Exception { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); return preferences.getString(LibraryConstants.PREFS_KEY_LASTPATH, ResourcesManager.getInstance(context).getSdcardDir().getAbsolutePath()); } public static void setLastFilePath(Context context, String lastPath) throws Exception { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); File file = new File(lastPath); if (file.exists()) { if (!file.isDirectory()) { file = file.getParentFile(); } } SharedPreferences.Editor editor = preferences.edit(); editor.putString(LibraryConstants.PREFS_KEY_LASTPATH, file.getAbsolutePath()); editor.apply(); } public static Uri getFileUriInApplicationFolder(Context context, File imageFile) throws Exception { String packageName = ResourcesManager.getInstance(context).getPackageName(); Uri outputFileUri = FileProvider.getUriForFile(context, packageName + GEOPAPARAZZI_LIBRARY_FILEPROVIDER_PLUS, imageFile); return outputFileUri; } /** * get unique device id. * * @param context the context to use. * @return the unique id. */ public static String getUniqueDeviceId(Context context) { // try to go for the imei TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); String id = tm.getDeviceId(); if (id == null) { // try the android id id = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); } return id; } /** * Checks if we are on the UI thread. * * @return <code>true</code> if we are on the UI thread. */ public static boolean isInUiThread() { if (Looper.myLooper() == Looper.getMainLooper()) { // UI Thread return true; } return false; } /** * Convert decimal degrees to exif format. * * @param decimalDegree the angle in decimal format. * @return the exif format string. */ @SuppressWarnings("nls") public static String degreeDecimal2ExifFormat(double decimalDegree) { StringBuilder sb = new StringBuilder(); sb.append((int) decimalDegree); sb.append("/1,"); decimalDegree = (decimalDegree - (int) decimalDegree) * 60; sb.append((int) decimalDegree); sb.append("/1,"); decimalDegree = (decimalDegree - (int) decimalDegree) * 60000; sb.append((int) decimalDegree); sb.append("/1000"); if (GPLog.LOG) { GPLog.addLogEntry("UTILITIES", sb.toString()); } return sb.toString(); } /** * Convert exif format to decimal degree. * * @param exifFormat the exif string of the gps position. * @return the decimal degree. */ @SuppressWarnings("nls") public static double exifFormat2degreeDecimal(String exifFormat) { // latitude=44/1,10/1,28110/1000 String[] exifSplit = exifFormat.trim().split(","); String[] value = exifSplit[0].split("/"); double tmp1 = Double.parseDouble(value[0]); double tmp2 = Double.parseDouble(value[1]); double degree = tmp1 / tmp2; value = exifSplit[1].split("/"); tmp1 = Double.parseDouble(value[0]); tmp2 = Double.parseDouble(value[1]); double minutes = tmp1 / tmp2; value = exifSplit[2].split("/"); tmp1 = Double.parseDouble(value[0]); tmp2 = Double.parseDouble(value[1]); double seconds = tmp1 / tmp2; double result = degree + (minutes / 60.0) + (seconds / 3600.0); return result; } /** * Calculates the hypothenuse as of the Pythagorean theorem. * * @param d1 the length of the first leg. * @param d2 the length of the second leg. * @return the length of the hypothenuse. */ public static double pythagoras(double d1, double d2) { return Math.sqrt(Math.pow(d1, 2.0) + Math.pow(d2, 2.0)); } /** * Tries to adapt a value to the supplied type. * * @param value the value to adapt. * @param adaptee the class to adapt to. * @return the adapted object or <code>null</code>, if it fails. */ public static <T> T adapt(Object value, Class<T> adaptee) { if (value instanceof Number) { Number num = (Number) value; if (adaptee.isAssignableFrom(Double.class)) { return adaptee.cast(num.doubleValue()); } else if (adaptee.isAssignableFrom(Float.class)) { return adaptee.cast(num.floatValue()); } else if (adaptee.isAssignableFrom(Integer.class)) { return adaptee.cast(num.intValue()); } else if (adaptee.isAssignableFrom(Long.class)) { return adaptee.cast(num.longValue()); } else if (adaptee.isAssignableFrom(String.class)) { return adaptee.cast(num.toString()); } else { throw new IllegalArgumentException(); } } else if (value instanceof String) { if (adaptee.isAssignableFrom(Double.class)) { try { Double parsed = Double.parseDouble((String) value); return adaptee.cast(parsed); } catch (Exception e) { return null; } } else if (adaptee.isAssignableFrom(Float.class)) { try { Float parsed = Float.parseFloat((String) value); return adaptee.cast(parsed); } catch (Exception e) { return null; } } else if (adaptee.isAssignableFrom(Integer.class)) { try { Integer parsed = Integer.parseInt((String) value); return adaptee.cast(parsed); } catch (Exception e) { return null; } } else if (adaptee.isAssignableFrom(String.class)) { return adaptee.cast(value); } else { throw new IllegalArgumentException(); } } else { throw new IllegalArgumentException("Can't adapt attribute of type: " + value.getClass().getCanonicalName()); //$NON-NLS-1$ } } /** * Ring action. * * @param context if something goes wrong. */ public static void ring(Context context) { Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); Ringtone r = RingtoneManager.getRingtone(context, notification); r.play(); } /** * Convert unsafe chars. * * @param string text to check. * @return safe text. */ @SuppressWarnings("nls") public static String makeXmlSafe(String string) { if (string == null) return ""; string = string.replaceAll("&", "&"); return string; } /** * A string formatter. * <p/> * <p>This method exists, because the method to use is not definitive yet.</p> * <p> * Currently the format of the substitutes in the message are: * <ul> * <li>%1$d = for numeric</li> * <li>%1$s = for strings</li> * </ul> * The %1, %2, etc refer to the number of the args. * </p> * * @param msg the message. * @param args the args to substitute. * @return the formatted string. */ public static String format(String msg, String... args) { String msgFormat = String.format(msg, (Object[]) args); return msgFormat; } /** * Convert bytes to hex string. * * @param b the bytes array to convert. * @param size the size of the array to consider. * @return the hex string. */ public static String getHexString(byte[] b, int size) { if (size < 1) { size = b.length; } StringBuilder sb = new StringBuilder(); for (int i = size - 1; i >= 0; i--) { if (i >= 0) sb.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1)); } return sb.toString(); } /** * Get the megabytes available in the filesystem at 'file'. * * @param file the filesystem's path. * @return the available space in mb. */ public static float getAvailableMegabytes(File file) { StatFs stat = new StatFs(file.getPath()); long bytesAvailable = (long) stat.getBlockSize() * (long) stat.getAvailableBlocks(); return bytesAvailable / (1024.f * 1024.f); } /** * Get the size in megabytes of the filesystem at 'file'. * * @param file the filesystem's path. * @return the size in mb. */ public static float getFilesystemMegabytes(File file) { StatFs stat = new StatFs(file.getPath()); long bytes = (long) stat.getBlockSize() * (long) stat.getBlockCount(); return bytes / (1024.f * 1024.f); } /** * Method to help define file names that need to be hidden. * <p/> * <p>Currently ones that start with _ are hidden.</p> * * @param name the name to check. * @return <code>true</code> if the name defines a file to hide. */ public static boolean isNameFromHiddenFile(String name) { return name.startsWith("_"); //$NON-NLS-1$ } /** * Serialize an object. * * @param obj the object to serialize. * @return the byte array. * @throws IOException */ public static byte[] serializeObject(Object obj) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(obj); out.close(); return bos.toByteArray(); } /** * Deserialize an array of bytes. * * @param bytes the array to convert. * @param clazz the class contained in the array. * @return the converted object. * @throws Exception */ public static <T> T deserializeObject(byte[] bytes, Class<T> clazz) throws Exception { ObjectInputStream in = null; try { in = new ObjectInputStream(new ByteArrayInputStream(bytes)); return clazz.cast(in.readObject()); } finally { if (in != null) in.close(); } } }