summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java264
1 files changed, 244 insertions, 20 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java
index 11d06c7ee..624fd4a88 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java
@@ -1,37 +1,261 @@
package org.yuzu.yuzu_emu.utils;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
+
+import androidx.annotation.Nullable;
+import androidx.documentfile.provider.DocumentFile;
+
+import org.yuzu.yuzu_emu.model.MinimalDocumentFile;
+
import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
public class FileUtil {
- public static byte[] getBytesFromFile(File file) throws IOException {
- final long length = file.length();
+ static final String PATH_TREE = "tree";
+ static final String DECODE_METHOD = "UTF-8";
+ static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
+ static final String TEXT_PLAIN = "text/plain";
- // You cannot create an array using a long type.
- if (length > Integer.MAX_VALUE) {
- // File is too large
- throw new IOException("File is too large!");
+ /**
+ * Create a file from directory with filename.
+ * @param context Application context
+ * @param directory parent path for file.
+ * @param filename file display name.
+ * @return boolean
+ */
+ @Nullable
+ public static DocumentFile createFile(Context context, String directory, String filename) {
+ try {
+ Uri directoryUri = Uri.parse(directory);
+ DocumentFile parent = DocumentFile.fromTreeUri(context, directoryUri);
+ if (parent == null) return null;
+ filename = URLDecoder.decode(filename, DECODE_METHOD);
+ String mimeType = APPLICATION_OCTET_STREAM;
+ if (filename.endsWith(".txt")) {
+ mimeType = TEXT_PLAIN;
+ }
+ DocumentFile exists = parent.findFile(filename);
+ if (exists != null) return exists;
+ return parent.createFile(mimeType, filename);
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage());
}
+ return null;
+ }
- byte[] bytes = new byte[(int) length];
+ /**
+ * Create a directory from directory with filename.
+ * @param context Application context
+ * @param directory parent path for directory.
+ * @param directoryName directory display name.
+ * @return boolean
+ */
+ @Nullable
+ public static DocumentFile createDir(Context context, String directory, String directoryName) {
+ try {
+ Uri directoryUri = Uri.parse(directory);
+ DocumentFile parent = DocumentFile.fromTreeUri(context, directoryUri);
+ if (parent == null) return null;
+ directoryName = URLDecoder.decode(directoryName, DECODE_METHOD);
+ DocumentFile isExist = parent.findFile(directoryName);
+ if (isExist != null) return isExist;
+ return parent.createDirectory(directoryName);
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage());
+ }
+ return null;
+ }
- int offset = 0;
- int numRead;
+ /**
+ * Open content uri and return file descriptor to JNI.
+ * @param context Application context
+ * @param path Native content uri path
+ * @param openmode will be one of "r", "r", "rw", "wa", "rwa"
+ * @return file descriptor
+ */
+ public static int openContentUri(Context context, String path, String openmode) {
+ try {
+ Uri uri = Uri.parse(path);
+ ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, openmode);
+ if (parcelFileDescriptor == null) {
+ Log.error("[FileUtil]: Cannot get the file descriptor from uri: " + path);
+ return -1;
+ }
+ return parcelFileDescriptor.detachFd();
+ }
+ catch (Exception e) {
+ Log.error("[FileUtil]: Cannot open content uri, error: " + e.getMessage());
+ }
+ return -1;
+ }
- try (InputStream is = new FileInputStream(file)) {
- while (offset < bytes.length
- && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
- offset += numRead;
+ /**
+ * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
+ * This function will be faster than DoucmentFile.listFiles
+ * @param context Application context
+ * @param uri Directory uri.
+ * @return CheapDocument lists.
+ */
+ public static MinimalDocumentFile[] listFiles(Context context, Uri uri) {
+ final ContentResolver resolver = context.getContentResolver();
+ final String[] columns = new String[]{
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME,
+ DocumentsContract.Document.COLUMN_MIME_TYPE,
+ };
+ Cursor c = null;
+ final List<MinimalDocumentFile> results = new ArrayList<>();
+ try {
+ String docId;
+ if (isRootTreeUri(uri)) {
+ docId = DocumentsContract.getTreeDocumentId(uri);
+ } else {
+ docId = DocumentsContract.getDocumentId(uri);
+ }
+ final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId);
+ c = resolver.query(childrenUri, columns, null, null, null);
+ while(c.moveToNext()) {
+ final String documentId = c.getString(0);
+ final String documentName = c.getString(1);
+ final String documentMimeType = c.getString(2);
+ final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId);
+ MinimalDocumentFile document = new MinimalDocumentFile(documentName, documentMimeType, documentUri);
+ results.add(document);
}
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot list file error: " + e.getMessage());
+ } finally {
+ closeQuietly(c);
+ }
+ return results.toArray(new MinimalDocumentFile[0]);
+ }
+
+ /**
+ * Check whether given path exists.
+ * @param path Native content uri path
+ * @return bool
+ */
+ public static boolean Exists(Context context, String path) {
+ Cursor c = null;
+ try {
+ Uri mUri = Uri.parse(path);
+ final String[] columns = new String[] { DocumentsContract.Document.COLUMN_DOCUMENT_ID };
+ c = context.getContentResolver().query(mUri, columns, null, null, null);
+ return c.getCount() > 0;
+ } catch (Exception e) {
+ Log.info("[FileUtil] Cannot find file from given path, error: " + e.getMessage());
+ } finally {
+ closeQuietly(c);
}
+ return false;
+ }
+
+ /**
+ * Check whether given path is a directory
+ * @param path content uri path
+ * @return bool
+ */
+ public static boolean isDirectory(Context context, String path) {
+ final ContentResolver resolver = context.getContentResolver();
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_MIME_TYPE
+ };
+ boolean isDirectory = false;
+ Cursor c = null;
+ try {
+ Uri mUri = Uri.parse(path);
+ c = resolver.query(mUri, columns, null, null, null);
+ c.moveToNext();
+ final String mimeType = c.getString(0);
+ isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR);
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot list files, error: " + e.getMessage());
+ } finally {
+ closeQuietly(c);
+ }
+ return isDirectory;
+ }
- // Ensure all the bytes have been read in
- if (offset < bytes.length) {
- throw new IOException("Could not completely read file " + file.getName());
+ /**
+ * Get file display name from given path
+ * @param path content uri path
+ * @return String display name
+ */
+ public static String getFilename(Context context, String path) {
+ final ContentResolver resolver = context.getContentResolver();
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ String filename = "";
+ Cursor c = null;
+ try {
+ Uri mUri = Uri.parse(path);
+ c = resolver.query(mUri, columns, null, null, null);
+ c.moveToNext();
+ filename = c.getString(0);
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage());
+ } finally {
+ closeQuietly(c);
}
+ return filename;
+ }
+
+ public static String[] getFilesName(Context context, String path) {
+ Uri uri = Uri.parse(path);
+ List<String> files = new ArrayList<>();
+ for (MinimalDocumentFile file: FileUtil.listFiles(context, uri)) {
+ files.add(file.getFilename());
+ }
+ return files.toArray(new String[0]);
+ }
- return bytes;
+ /**
+ * Get file size from given path.
+ * @param path content uri path
+ * @return long file size
+ */
+ public static long getFileSize(Context context, String path) {
+ final ContentResolver resolver = context.getContentResolver();
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_SIZE
+ };
+ long size = 0;
+ Cursor c =null;
+ try {
+ Uri mUri = Uri.parse(path);
+ c = resolver.query(mUri, columns, null, null, null);
+ c.moveToNext();
+ size = c.getLong(0);
+ } catch (Exception e) {
+ Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage());
+ } finally {
+ closeQuietly(c);
+ }
+ return size;
+ }
+
+ public static boolean isRootTreeUri(Uri uri) {
+ final List<String> paths = uri.getPathSegments();
+ return paths.size() == 2 && PATH_TREE.equals(paths.get(0));
+ }
+
+ public static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
}
}