Skip to content

Instantly share code, notes, and snippets.

@rok5ek
Last active March 1, 2019 13:45
Show Gist options
  • Save rok5ek/2e92883af83e5d2d3744105c756d22a1 to your computer and use it in GitHub Desktop.
Save rok5ek/2e92883af83e5d2d3744105c756d22a1 to your computer and use it in GitHub Desktop.
File util methods for Android
/*
* Copyright (C) 2018 OpenIntents.org
*
* 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.
*/
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
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 android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.util.Log;
import android.webkit.MimeTypeMap;
import okhttp3.ResponseBody;
import java.io.*;
import java.text.DecimalFormat;
import java.util.Comparator;
public class FileHelper {
public static final String DOCUMENTS_DIR = "documents";
// configured android:authorities in AndroidManifest (https://developer.android.com/reference/android/support/v4/content/FileProvider)
public static final String AUTHORITY = "YOUR_AUTHORITY.provider";
public static final String HIDDEN_PREFIX = ".";
private FileHelper() {
} //private constructor to enforce Singleton pattern
/**
* TAG for log messages.
*/
static final String TAG = "FileHelper";
private static final boolean DEBUG = false; // Set to true to enable logging
/**
* File and folder comparator. TODO Expose sorting option method
*/
public static Comparator<File> sComparator = (f1, f2) -> {
// Sort alphabetically by lower case, which is much cleaner
return f1.getName().toLowerCase().compareTo(
f2.getName().toLowerCase());
};
/**
* File (not directories) filter.
*/
public static FileFilter sFileFilter = file -> {
final String fileName = file.getName();
// Return files only (not directories) and skip hidden files
return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
};
/**
* Folder (directories) filter.
*/
public static FileFilter sDirFilter = file -> {
final String fileName = file.getName();
// Return directories only and skip hidden directories
return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
};
/**
* Gets the extension of a file name, like ".png" or ".jpg".
*
* @param uri
* @return Extension including the dot("."); "" if there is no extension;
* null if uri was null.
*/
public static String getExtension(String uri) {
if (uri == null) {
return null;
}
int dot = uri.lastIndexOf(".");
if (dot >= 0) {
return uri.substring(dot);
} else {
// No extension.
return "";
}
}
/**
* @return Whether the URI is a local one.
*/
public static boolean isLocal(String url) {
return url != null && !url.startsWith("http://") && !url.startsWith("https://");
}
/**
* @return True if Uri is a MediaStore Uri.
* @author paulburke
*/
public static boolean isMediaUri(Uri uri) {
return "media".equalsIgnoreCase(uri.getAuthority());
}
/**
* Convert File into Uri.
*
* @param file
* @return uri
*/
public static Uri getUri(File file) {
return (file != null) ? Uri.fromFile(file) : null;
}
/**
* Returns the path only (without file name).
*
* @param file
* @return
*/
public static File getPathWithoutFilename(File file) {
if (file != null) {
if (file.isDirectory()) {
// no file to be split off. Return everything
return file;
} else {
String filename = file.getName();
String filepath = file.getAbsolutePath();
// Construct path without file name.
String pathwithoutname = filepath.substring(0,
filepath.length() - filename.length());
if (pathwithoutname.endsWith("/")) {
pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
}
return new File(pathwithoutname);
}
}
return null;
}
/**
* @return The MIME type for the given file.
*/
public static String getMimeType(File file) {
String extension = getExtension(file.getName());
if (extension.length() > 0)
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
return "application/octet-stream";
}
/**
* @return The MIME type for the give Uri.
*/
public static String getMimeType(Context context, Uri uri) {
File file = new File(getPath(context, uri));
return getMimeType(file);
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is local.
*/
public static boolean isLocalStorageDocument(Uri uri) {
return AUTHORITY.equals(uri.getAuthority());
}
/**
* @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());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
/**
* 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.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = MediaStore.Files.FileColumns.DATA;
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
if (DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* 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.<br>
* <br>
* Callers should check whether the path is local before assuming it
* represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
* @see #isLocal(String)
* @see #getFile(Context, Uri)
*/
public static String getPath(final Context context, final Uri uri) {
if (DEBUG)
Log.d(TAG + " File -",
"Authority: " + uri.getAuthority() +
", Fragment: " + uri.getFragment() +
", Port: " + uri.getPort() +
", Query: " + uri.getQuery() +
", Scheme: " + uri.getScheme() +
", Host: " + uri.getHost() +
", Segments: " + uri.getPathSegments().toString()
);
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// LocalStorageProvider
if (isLocalStorageDocument(uri)) {
// The path is the id
return DocumentsContract.getDocumentId(uri);
}
// ExternalStorageProvider
else 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];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
if (id != null && id.startsWith("raw:")) {
return id.substring(4);
}
String[] contentUriPrefixesToTry = new String[]{
"content://downloads/public_downloads",
"content://downloads/my_downloads"
};
for (String contentUriPrefix : contentUriPrefixesToTry) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
try {
String path = getDataColumn(context, contentUri, null, null);
if (path != null) {
return path;
}
} catch (Exception e) {
}
}
// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams
String fileName = getFileName(context, uri);
File cacheDir = getDocumentCacheDir(context);
File file = generateFileName(fileName, cacheDir);
String destinationPath = null;
if (file != null) {
destinationPath = file.getAbsolutePath();
saveFileFromUri(context, uri, destinationPath);
}
return destinationPath;
}
// 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 ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Convert Uri into File, if possible.
*
* @return file A local file that the Uri was pointing to, or null if the
* Uri is unsupported or pointed to a remote resource.
* @author paulburke
* @see #getPath(Context, Uri)
*/
public static File getFile(Context context, Uri uri) {
if (uri != null) {
String path = getPath(context, uri);
if (path != null && isLocal(path)) {
return new File(path);
}
}
return null;
}
/**
* Get the file size in a human-readable string.
*
* @param size
* @return
* @author paulburke
*/
public static String getReadableFileSize(int size) {
final int BYTES_IN_KILOBYTES = 1024;
final DecimalFormat dec = new DecimalFormat("###.#");
final String KILOBYTES = " KB";
final String MEGABYTES = " MB";
final String GIGABYTES = " GB";
float fileSize = 0;
String suffix = KILOBYTES;
if (size > BYTES_IN_KILOBYTES) {
fileSize = size / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES) {
fileSize = fileSize / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES) {
fileSize = fileSize / BYTES_IN_KILOBYTES;
suffix = GIGABYTES;
} else {
suffix = MEGABYTES;
}
}
}
return String.valueOf(dec.format(fileSize) + suffix);
}
/**
* Get the Intent for selecting content to be used in an Intent Chooser.
*
* @return The intent for opening a file with Intent.createChooser()
*/
public static Intent createGetContentIntent() {
// Implicitly allow the user to select a particular kind of data
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// The MIME data type filter
intent.setType("*/*");
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
/**
* Creates View intent for given file
*
* @param file
* @return The intent for viewing file
*/
public static Intent getViewIntent(Context context, File file) {
//Uri uri = Uri.fromFile(file);
Uri uri = FileProvider.getUriForFile(context, AUTHORITY, file);
Intent intent = new Intent(Intent.ACTION_VIEW);
String url = file.toString();
if (url.contains(".doc") || url.contains(".docx")) {
// Word document
intent.setDataAndType(uri, "application/msword");
} else if (url.contains(".pdf")) {
// PDF file
intent.setDataAndType(uri, "application/pdf");
} else if (url.contains(".ppt") || url.contains(".pptx")) {
// Powerpoint file
intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
} else if (url.contains(".xls") || url.contains(".xlsx")) {
// Excel file
intent.setDataAndType(uri, "application/vnd.ms-excel");
} else if (url.contains(".zip") || url.contains(".rar")) {
// WAV audio file
intent.setDataAndType(uri, "application/x-wav");
} else if (url.contains(".rtf")) {
// RTF file
intent.setDataAndType(uri, "application/rtf");
} else if (url.contains(".wav") || url.contains(".mp3")) {
// WAV audio file
intent.setDataAndType(uri, "audio/x-wav");
} else if (url.contains(".gif")) {
// GIF file
intent.setDataAndType(uri, "image/gif");
} else if (url.contains(".jpg") || url.contains(".jpeg") || url.contains(".png")) {
// JPG file
intent.setDataAndType(uri, "image/jpeg");
} else if (url.contains(".txt")) {
// Text file
intent.setDataAndType(uri, "text/plain");
} else if (url.contains(".3gp") || url.contains(".mpg") || url.contains(".mpeg") ||
url.contains(".mpe") || url.contains(".mp4") || url.contains(".avi")) {
// Video files
intent.setDataAndType(uri, "video/*");
} else {
intent.setDataAndType(uri, "*/*");
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return intent;
}
public static File getDownloadsDir() {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
}
public static File getDocumentCacheDir(@NonNull Context context) {
File dir = new File(context.getCacheDir(), DOCUMENTS_DIR);
if (!dir.exists()) {
dir.mkdirs();
}
logDir(context.getCacheDir());
logDir(dir);
return dir;
}
private static void logDir(File dir) {
if (!DEBUG) return;
Log.d(TAG, "Dir=" + dir);
File[] files = dir.listFiles();
for (File file : files) {
Log.d(TAG, "File=" + file.getPath());
}
}
@Nullable
public static File generateFileName(@Nullable String name, File directory) {
if (name == null) {
return null;
}
File file = new File(directory, name);
if (file.exists()) {
String fileName = name;
String extension = "";
int dotIndex = name.lastIndexOf('.');
if (dotIndex > 0) {
fileName = name.substring(0, dotIndex);
extension = name.substring(dotIndex);
}
int index = 0;
while (file.exists()) {
index++;
name = fileName + '(' + index + ')' + extension;
file = new File(directory, name);
}
}
try {
if (!file.createNewFile()) {
return null;
}
} catch (IOException e) {
Log.w(TAG, e);
return null;
}
logDir(directory);
return file;
}
/**
* Writes response body to disk
*
* @param body ResponseBody
* @param path file path
* @return File
*/
public static File writeResponseBodyToDisk(ResponseBody body, String path) {
try {
File target = new File(path);
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
inputStream = body.byteStream();
outputStream = new FileOutputStream(target);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
}
outputStream.flush();
return target;
} catch (IOException e) {
return null;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return null;
}
}
private static void saveFileFromUri(Context context, Uri uri, String destinationPath) {
InputStream is = null;
BufferedOutputStream bos = null;
try {
is = context.getContentResolver().openInputStream(uri);
bos = new BufferedOutputStream(new FileOutputStream(destinationPath, false));
byte[] buf = new byte[1024];
is.read(buf);
do {
bos.write(buf);
} while (is.read(buf) != -1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) is.close();
if (bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static byte[] readBytesFromFile(String filePath) {
FileInputStream fileInputStream = null;
byte[] bytesArray = null;
try {
File file = new File(filePath);
bytesArray = new byte[(int) file.length()];
//read file into bytes[]
fileInputStream = new FileInputStream(file);
fileInputStream.read(bytesArray);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytesArray;
}
public static File createTempImageFile(Context context, String fileName) throws IOException {
// Create an image file name
File storageDir = new File(context.getCacheDir(), DOCUMENTS_DIR);
return File.createTempFile(fileName, ".jpg", storageDir);
}
public static String getFileName(@NonNull Context context, Uri uri) {
String mimeType = context.getContentResolver().getType(uri);
String filename = null;
if (mimeType == null && context != null) {
String path = getPath(context, uri);
if (path == null) {
filename = getName(uri.toString());
} else {
File file = new File(path);
filename = file.getName();
}
} else {
Cursor returnCursor = context.getContentResolver().query(uri, null,
null, null, null);
if (returnCursor != null) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
filename = returnCursor.getString(nameIndex);
returnCursor.close();
}
}
return filename;
}
public static String getName(String filename) {
if (filename == null) {
return null;
}
int index = filename.lastIndexOf('/');
return filename.substring(index + 1);
}
}
import android.content.ContentUris
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.database.DatabaseUtils
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 android.support.v4.content.FileProvider
import android.webkit.MimeTypeMap
import okhttp3.ResponseBody
import timber.log.Timber
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.text.DecimalFormat
/*
* Copyright (C) 2018 OpenIntents.org
*
* 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.
*/
object FileHelperKt {
private const val DOCUMENTS_DIR = "documents"
// configured android:authorities in AndroidManifest (https://developer.android.com/reference/android/support/v4/content/FileProvider)
private const val AUTHORITY = "YOUR_AUTHORITY.provider"
private const val HIDDEN_PREFIX = "."
private val DEBUG = false // Set to true to enable logging
/**
* File and folder comparator. TODO Expose sorting option method
*/
var sComparator = { f1: File, f2: File ->
// Sort alphabetically by lower case, which is much cleaner
f1.name.toLowerCase().compareTo(
f2.name.toLowerCase()
)
}
/**
* File (not directories) filter.
*/
var sFileFilter = { file: File ->
val fileName = file.getName()
// Return files only (not directories) and skip hidden files
file.isFile() && !fileName.startsWith(HIDDEN_PREFIX)
}
/**
* Folder (directories) filter.
*/
var sDirFilter = { file: File ->
val fileName = file.name
// Return directories only and skip hidden directories
file.isDirectory && !fileName.startsWith(HIDDEN_PREFIX)
}
val downloadsDir: File
get() = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
/**
* Gets the extension of a file name, like ".png" or ".jpg".
*
* @param uri
* @return Extension including the dot("."); "" if there is no extension;
* null if uri was null.
*/
fun getExtension(uri: String?): String? {
if (uri == null) {
return null
}
val dot = uri.lastIndexOf(".")
return if (dot >= 0) {
uri.substring(dot)
} else {
// No extension.
""
}
}
/**
* @return Whether the URI is a local one.
*/
fun isLocal(url: String?): Boolean {
return url != null && !url.startsWith("http://") && !url.startsWith("https://")
}
/**
* @return True if Uri is a MediaStore Uri.
* @author paulburke
*/
fun isMediaUri(uri: Uri): Boolean {
return "media".equals(uri.authority!!, ignoreCase = true)
}
/**
* Convert File into Uri.
*
* @param file
* @return uri
*/
fun getUri(file: File?): Uri? {
return if (file != null) Uri.fromFile(file) else null
}
/**
* Returns the path only (without file name).
*
* @param file
* @return
*/
fun getPathWithoutFilename(file: File?): File? {
if (file != null) {
if (file.isDirectory) {
// no file to be split off. Return everything
return file
} else {
val filename = file.name
val filepath = file.absolutePath
// Construct path without file name.
var pathwithoutname = filepath.substring(
0,
filepath.length - filename.length
)
if (pathwithoutname.endsWith("/")) {
pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length - 1)
}
return File(pathwithoutname)
}
}
return null
}
/**
* @return The MIME type for the given file.
*/
fun getMimeType(file: File): String? {
val extension = getExtension(file.name)
if (extension!!.isNotEmpty()) {
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1))
} else {
return "application/octet-stream"
}
}
/**
* @return The MIME type for the give Uri.
*/
fun getMimeType(context: Context, uri: Uri): String? {
val file = File(getPath(context, uri)!!)
return getMimeType(file)
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is local.
*/
fun isLocalStorageDocument(uri: Uri): Boolean {
return AUTHORITY == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
/**
* 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.
*/
fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? {
var cursor: Cursor? = null
val column = MediaStore.Files.FileColumns.DATA
val projection = arrayOf(column)
try {
cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
if (DEBUG)
DatabaseUtils.dumpCursor(cursor)
val columnIndex = cursor.getColumnIndexOrThrow(column)
return cursor.getString(columnIndex)
}
} finally {
cursor?.close()
}
return null
}
/**
* 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.<br></br>
* <br></br>
* Callers should check whether the path is local before assuming it
* represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
* @see .isLocal
* @see .getFile
*/
fun getPath(context: Context, uri: Uri): String? {
Timber.d(
"[app] FileHelper - Authority: ${uri.authority}, " +
"Fragment: ${uri.fragment}, " +
"Port: ${uri.port}, " +
"Query: ${uri.query}, " +
"Scheme: ${uri.scheme}, " +
"Host: ${uri.host}, " +
"Segments: ${uri.pathSegments}"
)
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// LocalStorageProvider
if (isLocalStorageDocument(uri)) {
// The path is the id
return DocumentsContract.getDocumentId(uri)
}
// ExternalStorageProvider
else if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split((":").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return (Environment.getExternalStorageDirectory()).toString() + "/" + split[1]
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri)
if (id != null && id.startsWith("raw:")) {
return id.substring(4)
}
val contentUriPrefixesToTry =
arrayOf("content://downloads/public_downloads", "content://downloads/my_downloads")
for (contentUriPrefix in contentUriPrefixesToTry) {
val contentUri =
ContentUris.withAppendedId(Uri.parse(contentUriPrefix), java.lang.Long.valueOf(id!!))
try {
val path = getDataColumn(context, contentUri, null, null)
if (path != null) {
return path
}
} catch (e: Exception) {
}
}
// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams
val fileName = getFileName(context, uri)
val cacheDir = getDocumentCacheDir(context)
val file = generateFileName(fileName, cacheDir)
var destinationPath: String? = null
if (file != null) {
destinationPath = file.absolutePath
saveFileFromUri(context, uri, destinationPath)
}
return destinationPath
}
// MediaProvider
else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split((":").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
when (type) {
"image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
}
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
// Return the remote address
if (isGooglePhotosUri(uri)) {
return uri.lastPathSegment
}
return getDataColumn(context, uri, null, null)
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
return uri.path
}
return null
}
/**
* Convert Uri into File, if possible.
*
* @return file A local file that the Uri was pointing to, or null if the
* Uri is unsupported or pointed to a remote resource.
* @author paulburke
* @see .getPath
*/
fun getFile(context: Context, uri: Uri?): File? {
if (uri != null) {
val path = getPath(context, uri)
if (path != null && isLocal(path)) {
return File(path)
}
}
return null
}
/**
* Get the file size in a human-readable string.
*
* @param size
* @return
* @author paulburke
*/
fun getReadableFileSize(size: Int): String {
val bytesInKilobytes = 1024
val dec = DecimalFormat("###.#")
val kb = " KB"
val mb = " MB"
val gb = " GB"
var fileSize = 0f
var suffix = kb
if (size > bytesInKilobytes) {
fileSize = (size / bytesInKilobytes).toFloat()
if (fileSize > bytesInKilobytes) {
fileSize /= bytesInKilobytes
if (fileSize > bytesInKilobytes) {
fileSize /= bytesInKilobytes
suffix = gb
} else {
suffix = mb
}
}
}
return (dec.format(fileSize.toDouble()) + suffix).toString()
}
/**
* Get the Intent for selecting content to be used in an Intent Chooser.
*
* @return The intent for opening a file with Intent.createChooser()
*/
fun createGetContentIntent(): Intent {
// Implicitly allow the user to select a particular kind of data
val intent = Intent(Intent.ACTION_GET_CONTENT)
// The MIME data type filter
intent.type = "*/*"
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE)
return intent
}
/**
* Creates View intent for given file
*
* @param file
* @return The intent for viewing file
*/
fun getViewIntent(context: Context, file: File): Intent {
// Uri uri = Uri.fromFile(file);
val uri = FileProvider.getUriForFile(context, AUTHORITY, file)
val intent = Intent(Intent.ACTION_VIEW)
val url = file.toString()
if (url.contains(".doc") || url.contains(".docx")) {
// Word document
intent.setDataAndType(uri, "application/msword")
} else if (url.contains(".pdf")) {
// PDF file
intent.setDataAndType(uri, "application/pdf")
} else if (url.contains(".ppt") || url.contains(".pptx")) {
// Powerpoint file
intent.setDataAndType(uri, "application/vnd.ms-powerpoint")
} else if (url.contains(".xls") || url.contains(".xlsx")) {
// Excel file
intent.setDataAndType(uri, "application/vnd.ms-excel")
} else if (url.contains(".zip") || url.contains(".rar")) {
// WAV audio file
intent.setDataAndType(uri, "application/x-wav")
} else if (url.contains(".rtf")) {
// RTF file
intent.setDataAndType(uri, "application/rtf")
} else if (url.contains(".wav") || url.contains(".mp3")) {
// WAV audio file
intent.setDataAndType(uri, "audio/x-wav")
} else if (url.contains(".gif")) {
// GIF file
intent.setDataAndType(uri, "image/gif")
} else if (url.contains(".jpg") || url.contains(".jpeg") || url.contains(".png")) {
// JPG file
intent.setDataAndType(uri, "image/jpeg")
} else if (url.contains(".txt")) {
// Text file
intent.setDataAndType(uri, "text/plain")
} else if ((url.contains(".3gp") || url.contains(".mpg") || url.contains(".mpeg") ||
url.contains(".mpe") || url.contains(".mp4") || url.contains(".avi"))
) {
// Video files
intent.setDataAndType(uri, "video/*")
} else {
intent.setDataAndType(uri, "*/*")
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
return intent
}
fun getDocumentCacheDir(context: Context): File {
val dir = File(context.cacheDir, DOCUMENTS_DIR)
if (!dir.exists()) {
dir.mkdirs()
}
logDir(context.cacheDir)
logDir(dir)
return dir
}
private fun logDir(dir: File) {
if (!DEBUG) return
Timber.d("[app] FileHelper log dir=$dir")
val files = dir.listFiles()
for (file in files!!) {
Timber.d("[app] FileHelper path:${file.path}")
}
}
fun generateFileName(name: String?, directory: File): File? {
var name: String? = name ?: return null
var file = File(directory, name!!)
if (file.exists()) {
var fileName: String = name
var extension = ""
val dotIndex = name.lastIndexOf('.')
if (dotIndex > 0) {
fileName = name.substring(0, dotIndex)
extension = name.substring(dotIndex)
}
var index = 0
while (file.exists()) {
index++
name = "$fileName($index)$extension"
file = File(directory, name)
}
}
try {
if (!file.createNewFile()) {
return null
}
} catch (e: IOException) {
Timber.w(e, "[app] FileHelper")
return null
}
logDir(directory)
return file
}
/**
* Writes response body to disk
*
* @param body ResponseBody
* @param path file path
* @return File
*/
fun writeResponseBodyToDisk(body: ResponseBody, path: String): File? {
try {
val target = File(path)
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val fileReader = ByteArray(4096)
inputStream = body.byteStream()
outputStream = FileOutputStream(target)
while (true) {
val read = inputStream!!.read(fileReader)
if (read == -1) {
break
}
outputStream.write(fileReader, 0, read)
}
outputStream.flush()
return target
} catch (e: IOException) {
return null
} finally {
if (inputStream != null) {
inputStream.close()
}
if (outputStream != null) {
outputStream.close()
}
}
} catch (e: IOException) {
return null
}
}
private fun saveFileFromUri(context: Context, uri: Uri, destinationPath: String) {
var inputStream: InputStream? = null
var bos: BufferedOutputStream? = null
try {
inputStream = context.contentResolver.openInputStream(uri)
bos = BufferedOutputStream(FileOutputStream(destinationPath, false))
val buf = ByteArray(1024)
inputStream!!.read(buf)
do {
bos.write(buf)
} while (inputStream.read(buf) != -1)
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
inputStream?.close()
bos?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
fun readBytesFromFile(filePath: String): ByteArray? {
var fileInputStream: FileInputStream? = null
var bytesArray: ByteArray? = null
try {
val file = File(filePath)
bytesArray = ByteArray(file.length().toInt())
// read file into bytes[]
fileInputStream = FileInputStream(file)
fileInputStream.read(bytesArray)
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
return bytesArray
}
@Throws(IOException::class)
fun createTempImageFile(context: Context, fileName: String): File {
// Create an image file name
val storageDir = File(context.cacheDir, DOCUMENTS_DIR)
return File.createTempFile(fileName, ".jpg", storageDir)
}
fun getFileName(context: Context, uri: Uri): String? {
val mimeType = context.contentResolver.getType(uri)
var filename: String? = null
if (mimeType == null) {
val path = getPath(context, uri)
if (path == null) {
filename = getName(uri.toString())
} else {
val file = File(path)
filename = file.name
}
} else {
val returnCursor = context.contentResolver.query(uri, null, null, null, null)
if (returnCursor != null) {
val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
filename = returnCursor.getString(nameIndex)
returnCursor.close()
}
}
return filename
}
fun getName(filename: String?): String? {
if (filename == null) {
return null
}
val index = filename.lastIndexOf('/')
return filename.substring(index + 1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment