flutter_file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java

307 lines
11 KiB
Java

package com.mr.flutter.plugin.filepicker;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
import androidx.annotation.Nullable;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Random;
public class FileUtils {
private static final String TAG = "FilePickerUtils";
private static final String PRIMARY_VOLUME_NAME = "primary";
public static String[] getMimeTypes(final ArrayList<String> allowedExtensions) {
if (allowedExtensions == null || allowedExtensions.isEmpty()) {
return null;
}
final ArrayList<String> mimes = new ArrayList<>();
for (int i = 0; i < allowedExtensions.size(); i++) {
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(allowedExtensions.get(i));
if (mime == null) {
Log.w(TAG, "Custom file type " + allowedExtensions.get(i) + " is unsupported and will be ignored.");
continue;
}
mimes.add(mime);
}
Log.d(TAG, "Allowed file extensions mimes: " + mimes);
return mimes.toArray(new String[0]);
}
public static String getFileName(Uri uri, final Context context) {
String result = null;
try {
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
} catch (Exception ex){
Log.e(TAG, "Failed to handle file name: " + ex.toString());
}
return result;
}
public static boolean clearCache(final Context context) {
try {
final File cacheDir = new File(context.getCacheDir() + "/file_picker/");
final File[] files = cacheDir.listFiles();
if (files != null) {
for (final File file : files) {
file.delete();
}
}
} catch (final Exception ex) {
Log.e(TAG, "There was an error while clearing cached files: " + ex.toString());
return false;
}
return true;
}
public static void loadData(final File file, FileInfo.Builder fileInfo) {
try {
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
buf.read(bytes, 0, bytes.length);
buf.close();
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.getMessage(), null);
} catch (IOException e) {
Log.e(TAG, "Failed to close file streams: " + e.getMessage(), null);
}
fileInfo.withData(bytes);
} catch (Exception e) {
Log.e(TAG, "Failed to load bytes into memory with error " + e.toString() + ". Probably the file is too big to fit device memory. Bytes won't be added to the file this time.");
}
}
public static FileInfo openFileStream(final Context context, final Uri uri, boolean withData) {
Log.i(TAG, "Caching from URI: " + uri.toString());
FileOutputStream fos = null;
final FileInfo.Builder fileInfo = new FileInfo.Builder();
final String fileName = FileUtils.getFileName(uri, context);
final String path = context.getCacheDir().getAbsolutePath() + "/file_picker/" + (fileName != null ? fileName : new Random().nextInt(100000));
final File file = new File(path);
if(!file.exists()) {
file.getParentFile().mkdirs();
try {
fos = new FileOutputStream(path);
try {
final BufferedOutputStream out = new BufferedOutputStream(fos);
final InputStream in = context.getContentResolver().openInputStream(uri);
final byte[] buffer = new byte[8192];
int len = 0;
while ((len = in.read(buffer)) >= 0) {
out.write(buffer, 0, len);
}
out.flush();
} finally {
fos.getFD().sync();
}
} catch (final Exception e) {
try {
fos.close();
} catch (final IOException | NullPointerException ex) {
Log.e(TAG, "Failed to close file streams: " + e.getMessage(), null);
return null;
}
Log.e(TAG, "Failed to retrieve path: " + e.getMessage(), null);
return null;
}
}
Log.d(TAG, "File loaded and cached at:" + path);
if(withData) {
loadData(file, fileInfo);
}
fileInfo
.withPath(path)
.withName(fileName)
.withSize(Long.parseLong(String.valueOf(file.length())));
return fileInfo.build();
}
@Nullable
@SuppressWarnings("deprecation")
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
if (treeUri == null) {
return null;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (isDownloadsDocument(treeUri)) {
String docId = DocumentsContract.getDocumentId(treeUri);
String extPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
if (docId.equals("downloads")) {
return extPath;
} else if (docId.matches("^ms[df]\\:.*")) {
String fileName = getFileName(treeUri, con);
return extPath + "/" + fileName;
} else if (docId.startsWith("raw:")) {
String rawPath = docId.split(":")[1];
return rawPath;
}
return null;
}
}
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri), con);
FileInfo.Builder fileInfo = new FileInfo.Builder();
if (volumePath == null) {
return File.separator;
}
if (volumePath.endsWith(File.separator))
volumePath = volumePath.substring(0, volumePath.length() - 1);
String documentPath = getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
documentPath = documentPath.substring(0, documentPath.length() - 1);
if (documentPath.length() > 0) {
if (documentPath.startsWith(File.separator)) {
return volumePath + documentPath;
}
else {
return volumePath + File.separator + documentPath;
}
} else {
return volumePath;
}
}
@Nullable
private static String getDirectoryPath(Class<?> storageVolumeClazz, Object storageVolumeElement) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Method getPath = storageVolumeClazz.getMethod("getPath");
return (String) getPath.invoke(storageVolumeElement);
}
Method getDirectory = storageVolumeClazz.getMethod("getDirectory");
File f = (File) getDirectory.invoke(storageVolumeElement);
if (f != null)
return f.getPath();
} catch (Exception ex) {
return null;
}
return null;
}
@SuppressLint("ObsoleteSdkInt")
private static String getVolumePath(final String volumeId, Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;
try {
StorageManager mStorageManager =
(StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
if (result == null)
return null;
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
// primary volume?
if (primary != null && PRIMARY_VOLUME_NAME.equals(volumeId)) {
return getDirectoryPath(storageVolumeClazz, storageVolumeElement);
}
// other volumes?
if (uuid != null && uuid.equals(volumeId)) {
return getDirectoryPath(storageVolumeClazz, storageVolumeElement);
}
}
// not found.
return null;
} catch (Exception ex) {
return null;
}
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0) return split[0];
else return null;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null)) return split[1];
else return File.separator;
}
}