/* * Copyright (C) 2012 Eyal LEZMY (http://www.eyal.fr) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fr.eyal.lib.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import fr.eyal.lib.data.service.DataLibService; import fr.eyal.lib.data.service.ServiceHelper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; /** * * This class help to manage the storage access (internal, external, files and cache).<br> * This class can be used statically through the its different functions but can also be * used as a singleton. The singleton is useful when you want to access the internal storage * that use {@link Context} functions ({@link Context#getCacheDir()} or * {@link Context#openFileInput(String)}, ...). <br> * Once the singleton is initialized (thanks to {@link FileManager#getInstance(Context)}), * it is ready to use everywhere you need, even if you don't have access to the execution's context. * <br><br> * If you use the Android DataLib, this class is initialized on the {@link ServiceHelper} and the * {@link DataLibService} constructors. * So it is ready to use everywhere on the application if you use the project's ServiceHelper or DataManager * or if you use add your project DataLib service on you Manifest.xml. * * @author Eyal LEZMY */ public class FileManager { private static final String TAG = FileManager.class.getSimpleName(); private static FileManager sInstance = null; private static final String DEFAULT_EXTENSION = ".jpg"; private static final String NOMEDIA_FILENAME = ".nomedia"; private static StringBuilder sStringBuilder = new StringBuilder(); private Context mContext; /** * Get the instance of the {@link NetflixDataManager} * * @param context The context of execution. Any Context can be put here, the application context * will be automatically used for the {@link NetflixDataManager} * * @return Returns the singleton */ public static FileManager getInstance(final Context context) { synchronized (FileManager.class) { if (sInstance == null) { sInstance = new FileManager(context.getApplicationContext()); } } return sInstance; } /** * Get the instance of the {@link FileManager} * * @return Returns the singleton <b>only if</b> the instance have already been create by the call to * <code>{@linkplain FileManager#getInstance(Context)}</code>. If it has not been called, this * function returns <b>null</b> */ public static FileManager getInstance() { if (sInstance == null) { return null; } return sInstance; } protected FileManager(final Context context){ mContext = context; } /** * Returns whether a file exists on the sdcard * * @param filename the file name * @param packageName the name of the package * @return whether a file exists on the sdcard */ public static boolean isFileOnSd(final String filename, final String packageName) { final String fullFilename = getExternalStorageFileDirectory(packageName) + filename; final File file = new File(fullFilename); return file.isFile(); } /** * Get the full path for a given package name * * @param packageName the name of the package * @return the full path for a given package name */ private static String getExternalStorageFileDirectory(final String packageName) { final StringBuilder sb = new StringBuilder(); sb.append(Environment.getExternalStorageDirectory()); sb.append(File.separatorChar + "Android" + File.separatorChar + "data" + File.separatorChar); sb.append(packageName); sb.append(File.separatorChar + "files" + File.separatorChar); return sb.toString(); } /** * Save a Bitmap in the default application directory into SD card * * @param filename the file name. * @param packageName the name of the package * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting. * * @return returns the file created path or <code>null</code> if an error occurs */ public static String saveOnSdCard(final String fileName, final String packageName, final Bitmap bitmap, final int compressionQuality) { if(bitmap == null) return null; final String directory = getExternalStorageFileDirectory(packageName); final File imageFile = new File(directory, fileName); final File imageDir = new File(directory); if (!imageDir.isDirectory()) { imageDir.mkdirs(); final File noMediaFile = new File(directory, NOMEDIA_FILENAME); try { noMediaFile.createNewFile(); } catch (final IOException e) { Out.e(TAG, "Error while creating .nomedia file", e); return null; } } FileOutputStream fos = null; try { fos = new FileOutputStream(imageFile); if (getFileExtension(fileName).equals(DEFAULT_EXTENSION)) { bitmap.compress(Bitmap.CompressFormat.JPEG, compressionQuality, fos); } else { bitmap.compress(Bitmap.CompressFormat.PNG, compressionQuality, fos); } } catch (final IOException e) { e.printStackTrace(); return null; } finally { if (fos != null) { try { fos.close(); } catch (final IOException e) { e.printStackTrace(); return null; } } } String filePath = directory + File.pathSeparator + fileName; return filePath; } /** * Save a Bitmap in the default application directory into the internal storage * * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns <code>true</code> if the storage has been successful or <code>false</code> if an error occurred */ public boolean saveInInternalStorage(final String filename, final Bitmap bitmap, final int compressionQuality) { return saveInInternalStorage(mContext, filename, bitmap, compressionQuality); } /** * Save a Bitmap in the default application directory into the internal * storage * * @param context the application context * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns <code>true</code> if the storage has been successful or <code>false</code> if an error occurred */ public static boolean saveInInternalStorage(final Context context, final String filename, final Bitmap bitmap, final int compressionQuality) { if(bitmap == null) return false; FileOutputStream fos = null; try { fos = context.openFileOutput(filename, Context.MODE_PRIVATE); if (getFileExtension(filename).equals(DEFAULT_EXTENSION)) { bitmap.compress(Bitmap.CompressFormat.JPEG, compressionQuality, fos); } else { bitmap.compress(Bitmap.CompressFormat.PNG, compressionQuality, fos); } } catch (final IOException e) { e.printStackTrace(); return false; } finally { if (fos != null) { try { fos.close(); } catch (final IOException e) { e.printStackTrace(); return false; } } } return true; } /** * Save a Bitmap in the default application directory into the internal cache storage * * @param directory the child directory where to put the file. This parameter can contains {@link File#separator} character * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns the file created path or <code>null</code> if an error occurs */ public String saveInInternalCache(final String filename, final String extension, final Bitmap bitmap, final int compressionQuality) { return saveInInternalCache(mContext, filename, extension, bitmap, compressionQuality); } /** * Save a Bitmap in the default application directory into the internal cache storage * * @param context the application context * @param directory the child directory where to put the file. This parameter can contains {@link File#separator} character * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns the file created path or <code>null</code> if an error occurs */ public static String saveInInternalCache(final Context context, final String filename, final String extension, final Bitmap bitmap, final int compressionQuality) { return FileManager.saveInInternalCache(context, null, filename, extension, bitmap, compressionQuality); } /** * Save a Bitmap in the default application directory into the internal cache storage * * @param directory the child directory where to put the file. This parameter can contains {@link File#separator} character * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns the file created path or <code>null</code> if an error occurs */ public String saveInInternalCache(final String directory, final String filename, final String extension, final Bitmap bitmap, final int compressionQuality) { return FileManager.saveInInternalCache(mContext, directory, filename, extension, bitmap, compressionQuality); } /** * Save a Bitmap in the default application directory into the internal cache storage * * @param context the application context * @param directory the child directory where to put the file. This parameter can contains {@link File#separator} character * @param filename the file name * @param bitmap the Bitmap to save * @param compressionQuality the quality for compression. Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting * * @return returns the file created path or <code>null</code> if an error occurs */ public static String saveInInternalCache(final Context context, final String directory, final String filename, String extension, final Bitmap bitmap, final int compressionQuality) { if(bitmap == null) return null; File cacheDir = getInternalCacheDirectory(directory, context); //we open the new file final File cacheFile = new File(cacheDir, filename); try { cacheFile.createNewFile(); } catch (final IOException e) { Out.e(TAG, "Error while creating cache file " + filename, e); return null; } //we fill and save the file FileOutputStream fos = null; try { fos = new FileOutputStream(cacheFile); if(extension == null || extension.length() == 0) extension = getFileExtension(filename); if (extension.compareToIgnoreCase(DEFAULT_EXTENSION) == 0) { bitmap.compress(Bitmap.CompressFormat.JPEG, compressionQuality, fos); } else { bitmap.compress(Bitmap.CompressFormat.PNG, compressionQuality, fos); } } catch (final IOException e) { e.printStackTrace(); return null; } finally { if (fos != null) { try { fos.close(); } catch (final IOException e) { e.printStackTrace(); return null; } } } return cacheFile.getAbsolutePath(); } /** * Returns a Bitmap image from device SDCard or null if image not found. * This function reach the cache directory of the SD card, i-e * Android/data/<package>/file/ folder. * * @param filename the file name for this Bitmap. * @param packageName the name of the package * @return the bitmap save on the SDCard */ public static Bitmap getPictureFromSDCard(final String filename, final String packageName) { return getPictureFromSDCard("", filename, packageName); } /** * Returns a Bitmap image from device SDCard or null if image not found. * This function reach the cache directory of the SD card, i-e * Android/data/<package>/file/ folder. * * @param path the path starting from the cache directory of the application * (without separator '/' character) * @param filename the file name for this Bitmap. * @param packageName the name of the package * @return the bitmap save on the SDCard */ public static Bitmap getPictureFromSDCard(final String path, final String filename, final String packageName) { return getPictureFromSDCard(path, filename, packageName, false); } /** * Returns a Bitmap image from device SDCard or null if image not found. * This function reach the cache directory of the SD card, i-e * Android/data/<package>/file/ folder. * * @param path the path starting from the cache directory of the application * (without separator '/' character) * @param filename the file name for this Bitmap. * @param packageName the name of the package * @param hasSpecialPath add "external_sd" to the file path * @return the bitmap save on the SDCard */ public static Bitmap getPictureFromSDCard(final String path, final String filename, final String packageName, final boolean hasSpecialPath) { sStringBuilder.setLength(0); sStringBuilder.append(Environment.getExternalStorageDirectory()); if (hasSpecialPath) { sStringBuilder.append(File.separatorChar + "external_sd"); } sStringBuilder.append(File.separatorChar + "Android" + File.separatorChar + "data" + File.separatorChar); sStringBuilder.append(packageName); sStringBuilder.append(File.separatorChar + "files" + File.separatorChar); if (path != null && path != "") { sStringBuilder.append(path + File.separatorChar); } sStringBuilder.append(filename); final String fullPath = sStringBuilder.toString(); Bitmap returnBitmap = null; final File picture = new File(fullPath); if (picture.isFile()) { returnBitmap = BitmapFactory.decodeFile(fullPath); } return returnBitmap; } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param directory the directory following the cache directory. This parameter can contains {@link File#separator} character * @param path the file path for this Bitmap. * @param options {@link BitmapFactory.Options} to use for the {@link BitmapFactory#decodeFile(String, android.graphics.BitmapFactory.Options)} function * * @return the bitmap save on the internal storage */ public Bitmap getPictureFromInternalCache(final String directory, final String filename, BitmapFactory.Options options) { return FileManager.getPictureFromInternalCache(directory, filename, mContext, options); } /** * Returns the file's path from internal storage or null if image not found. * * @param directory the directory following the cache directory. This parameter can contains {@link File#separator} character * @param path the file path for this file. * @return the bitmap save on the internal storage */ public String getPathFromInternalCache(final String directory, final String filename) { return FileManager.getInternalCacheDirectory(directory, mContext).getAbsolutePath() + File.separator + filename; } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param directory the directory following the cache directory. This parameter can contains {@link File#separator} character * @param path the file path for this Bitmap. * @param context the application context * @return the bitmap save on the internal storage */ public static Bitmap getPictureFromInternalCache(final String directory, final String filename, final Context context) { return FileManager.getPictureFromInternalCache(directory, filename, context, null); } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param directory the directory following the cache directory. This parameter can contains {@link File#separator} character * @param path the file path for this Bitmap. * @param context the application context * @param options {@link BitmapFactory.Options} to use for the {@link BitmapFactory#decodeFile(String, android.graphics.BitmapFactory.Options)} function * * @return the bitmap save on the internal storage */ public static Bitmap getPictureFromInternalCache(final String directory, final String filename, final Context context, BitmapFactory.Options options) { File cacheDir = getInternalCacheDirectory(directory, context); // options.inSampleSize = 1; // Out.d("", "POSTER " + opts.inSampleSize); return BitmapFactory.decodeFile(cacheDir.getAbsolutePath() + File.separator + filename, options); } private static File getInternalCacheDirectory(final String directory, final Context context) { //we get the root cache folder File cacheDir = context.getCacheDir(); //we eventually go through the specified directory if(directory != null){ cacheDir = new File(cacheDir.getAbsoluteFile() + File.separator + directory); if(!cacheDir.isDirectory()) cacheDir.mkdirs(); } return cacheDir; } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param path the file path for this Bitmap. * @param options {@link BitmapFactory.Options} to use for the {@link BitmapFactory#decodeFile(String, android.graphics.BitmapFactory.Options)} function * * @return the bitmap save on the internal storage */ public static Bitmap getPictureFromFile(final String path, BitmapFactory.Options options) { // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inSampleSize = 1; return BitmapFactory.decodeFile(path, options); } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param filename the file name for this Bitmap. * @return the bitmap save on the internal storage */ public Bitmap getPictureFromInternalStorage(final String filename) { return FileManager.getPictureFromInternalStorage(filename, mContext); } /** * Returns a Bitmap image from internal storage or null if image not found. * * @param filename the file name for this Bitmap. * @param context the application context * @return the bitmap save on the internal storage */ public static Bitmap getPictureFromInternalStorage(final String filename, final Context context) { try { final FileInputStream fis = context.openFileInput(filename); return BitmapFactory.decodeStream(fis); } catch (final FileNotFoundException e) { return null; } } public static void deletePrefixedFilesFromDirectoryInSDCard(final String packageName, final String prefix) { final String path = getExternalStorageFileDirectory(packageName); final File directory = new File(path); if (directory != null) { final File[] files = directory.listFiles(); if (files != null) { final int filesLength = files.length; for (int i = 0; i < filesLength; i++) { final String filename = files[i].getName(); if (filename.length() >= prefix.length() && filename.substring(0, prefix.length()).equals(prefix)) { files[i].delete(); } } } } } public static void deletePrefixedFilesFromDirectoryInInternalStorage(final Context context, final String prefix) { final File directory = context.getFilesDir(); if (directory != null) { final File[] files = directory.listFiles(); if (files != null) { final int filesLength = files.length; for (int i = 0; i < filesLength; i++) { final String filename = files[i].getName(); if (filename.length() >= prefix.length() && filename.substring(0, prefix.length()).equals(prefix)) { files[i].delete(); } } } } } /** * Get the file extension * * @param filename the file name * @return the file extension */ public static String getFileExtension(final String filename) { String extension; final int dotPos = filename.lastIndexOf("."); if (dotPos != -1 || (dotPos != filename.length() - 1)) { extension = filename.substring(dotPos); } else { extension = DEFAULT_EXTENSION; } return extension; } /** * Get the file name * * @param fullpath the full path * @return the file name */ public static String getFileName(final String fullpath) { final int dotPos = fullpath.lastIndexOf(File.separatorChar); if (dotPos != -1 || (dotPos != fullpath.length() - 1)) { return fullpath.substring(dotPos + 1); } else { return fullpath; } } }