package eu.siacs.conversations.utils; import android.annotation.SuppressLint; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.OpenableColumns; import java.io.File; import java.net.URL; import java.util.List; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import eu.siacs.conversations.entities.Transferable; public final class FileUtils { /** * Get a file path from a Uri. This will get the the path for Storage Access * Framework Documents, as well as the _data field for the MediaStore and * other file-based ContentProviders. * * @param uri The Uri to query. * @author paulburke */ @SuppressLint("NewApi") public static String getPath(final Uri uri) { if (uri == null) { return null; } final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; final Context context = ConversationsPlusApplication.getAppContext(); // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{ split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { String path = getDataColumn(context, uri, null, null); if (path != null) { File file = new File(path); if (!file.canRead()) { return null; } } return path; } // File else if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } catch(Exception e) { return null; } finally { if (cursor != null) { cursor.close(); } } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } public static String getRelevantExtension(URL url) { if (url == null) { return null; } String path = url.getPath(); return getRelevantExtension(path); } public static String getRelevantExtension(String path) { if (path == null || path.isEmpty()) { return null; } String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); final String lastPart = FileUtils.getLastExtension(filename); if (!lastPart.isEmpty()) { // we want the real file extension, not the crypto one final String secondToLastPart = FileUtils.getSecondToLastExtension(filename); if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { return secondToLastPart; } else { return lastPart; } } return null; } /** * @param filename The filename to extract extension from * @return last extension or empty string */ public static String getLastExtension(final String filename) { if (filename == null || filename.isEmpty()) { return ""; } final int lastDotPosition = filename.lastIndexOf('.'); final String lastPart = lastDotPosition != -1 ? filename.substring(lastDotPosition + 1) : ""; return lastPart; } /** * @param filename The filename to extract extension from * @return second to last extension or empty string */ public static String getSecondToLastExtension(final String filename) { if (filename == null || filename.isEmpty()) { return ""; } final int lastDotPosition = filename.lastIndexOf('.'); final int secondToLastDotPosition = filename.lastIndexOf('.', lastDotPosition - 1); final String secondToLastPart = secondToLastDotPosition != -1 ? filename.substring(secondToLastDotPosition + 1, lastDotPosition) : ""; return secondToLastPart; } /** * Retrieve file size from given uri * @param context actual Context * @param uri uri to file * @return file size or -1 in case of error */ private static long getFileSize(Context context, Uri uri) { Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); } else { return -1; } } /** * Check for given list of uris if corresponding file sizes are all smaller than given maximum * @param context actual Context * @param uris list of uris * @param max maximum file size * @return true if all file sizes are smaller than max, false otherwise */ public static boolean allFilesUnderSize(Context context, List uris, long max) { if (max <= 0) { return true; //exception to be compatible with HTTP Upload < v0.2 } for(Uri uri : uris) { if (getFileSize(context, uri) > max) { return false; } } return true; } private FileUtils() { // Utility class - do not instantiate } }