package com.radicaldynamic.groupinform.utilities; import java.io.File; import java.util.Calendar; import java.util.regex.Pattern; import com.radicaldynamic.groupinform.application.Collect; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.os.Environment; import android.util.Log; public final class FileUtilsExtended { private static final String t = "FileUtilsExtended: "; // Storage paths with support for API level 7 public static final String EXTERNAL_PATH = Environment.getExternalStorageDirectory() + "/Android/data/com.radicaldynamic.groupinform"; public static final String EXTERNAL_FILES = EXTERNAL_PATH + "/files"; // API level 8 can use getExternalFilesDir() public static final String EXTERNAL_CACHE = EXTERNAL_PATH + "/cache"; // API level 8 can use getExternalCacheDir() public static final String EXTERNAL_COUCH = EXTERNAL_PATH + "/couchdb"; public static final String EXTERNAL_ERLANG = EXTERNAL_PATH + "/erlang"; public static final String EXTERNAL_DB = EXTERNAL_PATH + "/db"; // Temporary storage for forms downloaded from ODK Aggregate or about to be uploaded to same public static final String ODK_IMPORT_PATH = EXTERNAL_CACHE + "/odk/import"; public static final String ODK_UPLOAD_PATH = EXTERNAL_CACHE + "/odk/upload"; // Temporary instance storage public static final String FORMS_PATH = EXTERNAL_CACHE + "/forms"; public static final String INSTANCES_PATH = EXTERNAL_CACHE + "/instances"; public static final String MEDIA_DIR = "media"; public static final String CAPTURED_IMAGE_FILE = "tmp.jpg"; public static final String DEVICE_CACHE_FILE = "devices.json"; public static final String FOLDER_CACHE_FILE = "folders.json"; public static final String FORM_LOGO_FILE = "form_logo.png"; public static final String SESSION_CACHE_FILE = "session.bin"; public static final String SPLASH_SCREEN_FILE = "splash.png"; public static final int IMAGE_WIDGET_MAX_WIDTH = 1024; public static final int IMAGE_WIDGET_MAX_HEIGHT = 768; public static final int IMAGE_WIDGET_QUALITY = 75; // Minutes in second units public static final int TIME_TWO_MINUTES = 120; // 24 hours (represented as milliseconds) private static final long TIME_24_HOURS = 86400000; /* * Delete files from the app cache directory on the SD card according to * file names matching a specific CouchDB document ID. This is usually * a form instance ID. */ public static void deleteExternalInstanceCacheFiles(String id) { final String tt = t + "deleteExternalInstanceCacheFiles(): "; File cacheDir = new File(EXTERNAL_CACHE); String[] fileNames = cacheDir.list(); for (String f : fileNames) { if (Collect.Log.VERBOSE) Log.v(Collect.LOGTAG, tt + "evaluating " + f + " for removal"); if (Pattern.matches("^" + id + "[.].*", f)) { if (new File(EXTERNAL_CACHE, f).delete()) { if (Collect.Log.VERBOSE) Log.v(Collect.LOGTAG, tt + "removed " + f); } else { if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, tt + "unable to remove " + f); } } } } /* * Removed from ODK FileUtils but we still use it */ public static boolean deleteFolder(String path) { // not recursive if (path != null && storageReady()) { File dir = new File(path); if (dir.exists() && dir.isDirectory()) { File[] files = dir.listFiles(); for (File file : files) { if (!file.delete()) { if (Collect.Log.ERROR) Log.e(t, "Failed to delete " + file); } } } return dir.delete(); } else { return false; } } /* * Recursively go through the external cache directories & files and * remove empty directories and files older than 72 hours. */ public static void expireExternalCache(File f) { final String tt = t + "expireExternalCache(): "; if (!f.exists()) { if (Collect.Log.WARN) Log.w(Collect.LOGTAG, tt + f.getAbsolutePath() + " could not be found, aborting"); return; } if (f.isDirectory()) { if (Collect.Log.VERBOSE) Log.v(Collect.LOGTAG, tt + "expiring old files in " + f.getAbsolutePath()); for (File c : f.listFiles()) { expireExternalCache(c); } } if (f.isFile() && (System.currentTimeMillis() - f.lastModified() >= TIME_24_HOURS * 3) || f.isDirectory() && f.listFiles().length == 0) { if (Collect.Log.VERBOSE) Log.v(Collect.LOGTAG, tt + f.getName() + " older than 72 hours or empty directory, removing"); f.delete(); } } /* * Used by ImageWidget to resize a captured bitmap to equal to or less than maxWidth x maxHeight */ public static Bitmap getBitmapResizedToStore(File f, int maxWidth, int maxHeight) { // Determine image size of f BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(f.getAbsolutePath(), o); int newWidth; int newHeight; float scaleWidth; float scaleHeight; if (o.outWidth >= o.outHeight) { scaleWidth = (float) maxWidth / o.outWidth; scaleHeight = scaleWidth; } else { scaleHeight = (float) maxHeight / o.outHeight; scaleWidth = scaleHeight; } newWidth = Math.round(o.outWidth * scaleWidth); newHeight = Math.round(o.outHeight * scaleHeight); if (Collect.Log.DEBUG) Log.d(t, "Image should be " + maxWidth + "x" + maxHeight + ". Image has been scaled to " + newWidth + "x" + newHeight); return Bitmap.createScaledBitmap( BitmapFactory.decodeFile(f.getAbsolutePath(), new Options()), newWidth, newHeight, true); } /* * Find out if a given file is older than span (in seconds) */ public static final boolean isFileOlderThan(String path, int span) { File f = new File(path); if (f.lastModified() < (Calendar.getInstance().getTimeInMillis() - span * 1000)) return true; else return false; } /* * Removed from ODK FileUtils but we still use it */ public static boolean storageReady() { String cardstatus = Environment.getExternalStorageState(); if (cardstatus.equals(Environment.MEDIA_REMOVED) || cardstatus.equals(Environment.MEDIA_UNMOUNTABLE) || cardstatus.equals(Environment.MEDIA_UNMOUNTED) || cardstatus.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { return false; } else { return true; } } }