/* * GeoSolutions - MapstoreMobile - GeoSpatial Framework on Android based devices * Copyright (C) 2014 GeoSolutions (www.geo-solutions.it) * * 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 it.geosolutions.geocollect.android.core.form.utils; import it.geosolutions.android.map.utils.MapFilesProvider; import it.geosolutions.geocollect.android.app.R; import it.geosolutions.geocollect.android.core.form.action.AndroidAction; import it.geosolutions.geocollect.android.core.form.action.CameraAction; import it.geosolutions.geocollect.android.core.form.action.CenterOnMarkerAction; import it.geosolutions.geocollect.android.core.form.action.LocalizeAction; import it.geosolutions.geocollect.android.core.form.action.SaveAction; import it.geosolutions.geocollect.android.core.form.action.SendAction; import it.geosolutions.geocollect.model.viewmodel.FormAction; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; /** * A static class that provide utility methods * * @author Lorenzo Natali (lorenzo.natali@geo-solutions.it) * */ public class FormUtils { private static String ICON_SAVE = "ic_save"; private static String ICON_SEND = "ic_send"; private static String ICON_ACCEPT = "ic_accept"; private static String ICON_CAMERA = "ic_camera"; private static String ICON_CENTER_ON_MARKER = "ic_center"; private static String ICON_LOCALIZE = "ic_localize"; /** * Provides the drawable for the icon class in <MissionTemplate> If the correct drawable cannot be found, returns a question mark icon * * @param iconCls * @return */ public static Integer getDrawable(String iconCls) { if (iconCls == null) { return R.drawable.ic_action_help; } if (ICON_SAVE.equals(iconCls)) { return R.drawable.ic_navigation_accept; } else if (ICON_SEND.equals(iconCls)) { return R.drawable.ic_action_upload; } else if (ICON_ACCEPT.equals(iconCls)) { return R.drawable.ic_rating_good; } else if (ICON_CAMERA.equals(iconCls)) { return R.drawable.ic_device_access_camera; } else if (ICON_CENTER_ON_MARKER.equals(iconCls)) { return R.drawable.ic_center; } else if (ICON_LOCALIZE.equals(iconCls)) { return R.drawable.ic_localize_marker; } else { return R.drawable.ic_action_help; } } /** * @param a */ public static AndroidAction getAndroidAction(FormAction a) { switch (a.type) { case confirm: case send: return new SendAction(a); case save: return new SaveAction(a); case photo: return new CameraAction(a); case video: break; case localize: return new LocalizeAction(a); case center: return new CenterOnMarkerAction(a); } return null; } /** * resize the images in the folder to the provided max size of the larger dimension * * @param feature_id * @param maxSize */ public static void resizeImagesToMax(final Context context, final String feature_id, final int maxSize) { if (feature_id == null || feature_id.isEmpty()) { Log.w("FormUtils", "getPhotoUriStrings: Could not get feature_id"); return; } File folder = new File(MapFilesProvider.getEnvironmentDirPath(context) + "/geocollect/media/" + feature_id); folder.mkdirs(); File[] listOfFiles = folder.listFiles(); if (listOfFiles == null) { // Zero-length array as "not found" return; } // resize for (int i = 0; i < listOfFiles.length; i++) { Bitmap resizedBitmap = null; try { // Following http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap // to avoid OOE decoding bitmaps, read out the bounds of the image, calculate the sample size // and only then decode the already resized image, which will need less memory BitmapFactory.Options options = new BitmapFactory.Options(); // this won't allocate memory but read out only the bounds of the image options.inJustDecodeBounds = true; BitmapFactory.decodeFile(listOfFiles[i].getAbsolutePath(), options); // Calculate inSampleSize final int sampleSize = calculateInSampleSize(options, maxSize); if (sampleSize == 1) { // no need to resample, this image is small enough continue; } options.inSampleSize = sampleSize; // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; resizedBitmap = BitmapFactory.decodeFile(listOfFiles[i].getAbsolutePath(), options); } catch (OutOfMemoryError e) { // according to http://stackoverflow.com/questions/7138645/catching-outofmemoryerror-in-decoding-bitmap // we could try to catch that OOE and do System.gc() but I think the above should avoid OOEs // so for now give up Log.e(FormUtils.class.getSimpleName(), "OOE decoding Bitmap ", e); continue; } try { final File origFile = listOfFiles[i]; final String newfileName = origFile.getName().substring(0, origFile.getName().lastIndexOf(".")) + "_resized"; final String extension = origFile.getName().substring( origFile.getName().lastIndexOf(".") + 1); final String newFile = origFile.getPath().substring(0, origFile.getPath().lastIndexOf("/") + 1) + newfileName + "." + extension; FileOutputStream fOut = new FileOutputStream(newFile); resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut); fOut.flush(); fOut.close(); // all worked out, delete original origFile.delete(); } catch (FileNotFoundException e) { Log.e(FormUtils.class.getSimpleName(), e.getClass().getSimpleName(), e); } catch (IOException e) { Log.e(FormUtils.class.getSimpleName(), e.getClass().getSimpleName(), e); } } } /** * calculates the samplesize (the scale factor 1 / n) to subsample the image Note : A power of two value is calculated because the decoder uses a * final value by rounding down to the nearest power of two, as per the inSampleSize documentation. * * @param options * @param maxSize * @return */ public static int calculateInSampleSize(BitmapFactory.Options options, final int maxSize) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; int max = height > width ? height : width; float factor = (float) max / maxSize; int reqWidth = (int) (width / factor); int reqHeight = (int) (height / factor); if (height > reqHeight || width > reqWidth) { // Calculate the inSampleSize value that is a power of 2 and will keep both // height and width smaller than the required while (height / inSampleSize > reqHeight && width / inSampleSize > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } /** * Return a list of Strings representing Uris for the media TODO: Filter media based on feature.id * * @return */ public static String[] getPhotoUriStrings(final Context context, final String feature_id) { if (feature_id == null || feature_id.isEmpty()) { Log.w("FormUtils", "getPhotoUriStrings: Could not get feature_id"); return new String[0]; } File folder = new File(MapFilesProvider.getEnvironmentDirPath(context) + "/geocollect/media/" + feature_id);// TODO Parametrize this folder.mkdirs(); File[] listOfFiles = folder.listFiles(); if (listOfFiles == null) { // Zero-length array as "not found" return new String[0]; } ArrayList<String> newFileListUri = new ArrayList<String>(); for (int i = 0; i < listOfFiles.length; i++) { if (listOfFiles[i].isFile()) { newFileListUri.add(Uri.fromFile(listOfFiles[i]).toString()); } } return newFileListUri.toArray(new String[newFileListUri.size()]); } /** * Given a string encoded file URI, delete that file * * @param filePath * @return boolean whether the file was deleted or not */ public static boolean deleteFile(String filePath) { if (filePath == null) return false; URI fileURI = URI.create(filePath); File f = new File(fileURI); if (f == null || !f.exists() || !f.isFile() || !f.canWrite()) { return false; } return f.delete(); } }