diff options
| author | 2023-03-11 00:37:04 -0500 | |
|---|---|---|
| committer | 2023-06-03 00:05:41 -0700 | |
| commit | 7b54c2b2e25adb81c95d54941f55bae76770cdb9 (patch) | |
| tree | 28032417460af44cd98210e098ab394cffdcd66b /src/android | |
| parent | android: Convert FileBrowserHelper to Kotlin (diff) | |
| download | yuzu-7b54c2b2e25adb81c95d54941f55bae76770cdb9.tar.gz yuzu-7b54c2b2e25adb81c95d54941f55bae76770cdb9.tar.xz yuzu-7b54c2b2e25adb81c95d54941f55bae76770cdb9.zip | |
android: Convert FileUtil to Kotlin
Diffstat (limited to 'src/android')
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java | 296 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | 292 |
2 files changed, 292 insertions, 296 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 deleted file mode 100644 index 8665704cc..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java +++ /dev/null | |||
| @@ -1,296 +0,0 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils; | ||
| 2 | |||
| 3 | import android.content.ContentResolver; | ||
| 4 | import android.content.Context; | ||
| 5 | import android.database.Cursor; | ||
| 6 | import android.net.Uri; | ||
| 7 | import android.os.ParcelFileDescriptor; | ||
| 8 | import android.provider.DocumentsContract; | ||
| 9 | |||
| 10 | import androidx.annotation.Nullable; | ||
| 11 | import androidx.documentfile.provider.DocumentFile; | ||
| 12 | |||
| 13 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile; | ||
| 14 | |||
| 15 | import java.io.FileOutputStream; | ||
| 16 | import java.io.IOException; | ||
| 17 | import java.io.InputStream; | ||
| 18 | import java.net.URLDecoder; | ||
| 19 | import java.util.ArrayList; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 22 | public class FileUtil { | ||
| 23 | static final String PATH_TREE = "tree"; | ||
| 24 | static final String DECODE_METHOD = "UTF-8"; | ||
| 25 | static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; | ||
| 26 | static final String TEXT_PLAIN = "text/plain"; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Create a file from directory with filename. | ||
| 30 | * @param context Application context | ||
| 31 | * @param directory parent path for file. | ||
| 32 | * @param filename file display name. | ||
| 33 | * @return boolean | ||
| 34 | */ | ||
| 35 | @Nullable | ||
| 36 | public static DocumentFile createFile(Context context, String directory, String filename) { | ||
| 37 | try { | ||
| 38 | Uri directoryUri = Uri.parse(directory); | ||
| 39 | DocumentFile parent = DocumentFile.fromTreeUri(context, directoryUri); | ||
| 40 | if (parent == null) return null; | ||
| 41 | filename = URLDecoder.decode(filename, DECODE_METHOD); | ||
| 42 | String mimeType = APPLICATION_OCTET_STREAM; | ||
| 43 | if (filename.endsWith(".txt")) { | ||
| 44 | mimeType = TEXT_PLAIN; | ||
| 45 | } | ||
| 46 | DocumentFile exists = parent.findFile(filename); | ||
| 47 | if (exists != null) return exists; | ||
| 48 | return parent.createFile(mimeType, filename); | ||
| 49 | } catch (Exception e) { | ||
| 50 | Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage()); | ||
| 51 | } | ||
| 52 | return null; | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Create a directory from directory with filename. | ||
| 57 | * @param context Application context | ||
| 58 | * @param directory parent path for directory. | ||
| 59 | * @param directoryName directory display name. | ||
| 60 | * @return boolean | ||
| 61 | */ | ||
| 62 | @Nullable | ||
| 63 | public static DocumentFile createDir(Context context, String directory, String directoryName) { | ||
| 64 | try { | ||
| 65 | Uri directoryUri = Uri.parse(directory); | ||
| 66 | DocumentFile parent = DocumentFile.fromTreeUri(context, directoryUri); | ||
| 67 | if (parent == null) return null; | ||
| 68 | directoryName = URLDecoder.decode(directoryName, DECODE_METHOD); | ||
| 69 | DocumentFile isExist = parent.findFile(directoryName); | ||
| 70 | if (isExist != null) return isExist; | ||
| 71 | return parent.createDirectory(directoryName); | ||
| 72 | } catch (Exception e) { | ||
| 73 | Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage()); | ||
| 74 | } | ||
| 75 | return null; | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Open content uri and return file descriptor to JNI. | ||
| 80 | * @param context Application context | ||
| 81 | * @param path Native content uri path | ||
| 82 | * @param openmode will be one of "r", "r", "rw", "wa", "rwa" | ||
| 83 | * @return file descriptor | ||
| 84 | */ | ||
| 85 | public static int openContentUri(Context context, String path, String openmode) { | ||
| 86 | try { | ||
| 87 | Uri uri = Uri.parse(path); | ||
| 88 | ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, openmode); | ||
| 89 | if (parcelFileDescriptor == null) { | ||
| 90 | Log.error("[FileUtil]: Cannot get the file descriptor from uri: " + path); | ||
| 91 | return -1; | ||
| 92 | } | ||
| 93 | return parcelFileDescriptor.detachFd(); | ||
| 94 | } | ||
| 95 | catch (Exception e) { | ||
| 96 | Log.error("[FileUtil]: Cannot open content uri, error: " + e.getMessage()); | ||
| 97 | } | ||
| 98 | return -1; | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow | ||
| 103 | * This function will be faster than DoucmentFile.listFiles | ||
| 104 | * @param context Application context | ||
| 105 | * @param uri Directory uri. | ||
| 106 | * @return CheapDocument lists. | ||
| 107 | */ | ||
| 108 | public static MinimalDocumentFile[] listFiles(Context context, Uri uri) { | ||
| 109 | final ContentResolver resolver = context.getContentResolver(); | ||
| 110 | final String[] columns = new String[]{ | ||
| 111 | DocumentsContract.Document.COLUMN_DOCUMENT_ID, | ||
| 112 | DocumentsContract.Document.COLUMN_DISPLAY_NAME, | ||
| 113 | DocumentsContract.Document.COLUMN_MIME_TYPE, | ||
| 114 | }; | ||
| 115 | Cursor c = null; | ||
| 116 | final List<MinimalDocumentFile> results = new ArrayList<>(); | ||
| 117 | try { | ||
| 118 | String docId; | ||
| 119 | if (isRootTreeUri(uri)) { | ||
| 120 | docId = DocumentsContract.getTreeDocumentId(uri); | ||
| 121 | } else { | ||
| 122 | docId = DocumentsContract.getDocumentId(uri); | ||
| 123 | } | ||
| 124 | final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId); | ||
| 125 | c = resolver.query(childrenUri, columns, null, null, null); | ||
| 126 | while(c.moveToNext()) { | ||
| 127 | final String documentId = c.getString(0); | ||
| 128 | final String documentName = c.getString(1); | ||
| 129 | final String documentMimeType = c.getString(2); | ||
| 130 | final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId); | ||
| 131 | MinimalDocumentFile document = new MinimalDocumentFile(documentName, documentMimeType, documentUri); | ||
| 132 | results.add(document); | ||
| 133 | } | ||
| 134 | } catch (Exception e) { | ||
| 135 | Log.error("[FileUtil]: Cannot list file error: " + e.getMessage()); | ||
| 136 | } finally { | ||
| 137 | closeQuietly(c); | ||
| 138 | } | ||
| 139 | return results.toArray(new MinimalDocumentFile[0]); | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Check whether given path exists. | ||
| 144 | * @param path Native content uri path | ||
| 145 | * @return bool | ||
| 146 | */ | ||
| 147 | public static boolean Exists(Context context, String path) { | ||
| 148 | Cursor c = null; | ||
| 149 | try { | ||
| 150 | Uri mUri = Uri.parse(path); | ||
| 151 | final String[] columns = new String[] { DocumentsContract.Document.COLUMN_DOCUMENT_ID }; | ||
| 152 | c = context.getContentResolver().query(mUri, columns, null, null, null); | ||
| 153 | return c.getCount() > 0; | ||
| 154 | } catch (Exception e) { | ||
| 155 | Log.info("[FileUtil] Cannot find file from given path, error: " + e.getMessage()); | ||
| 156 | } finally { | ||
| 157 | closeQuietly(c); | ||
| 158 | } | ||
| 159 | return false; | ||
| 160 | } | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Check whether given path is a directory | ||
| 164 | * @param path content uri path | ||
| 165 | * @return bool | ||
| 166 | */ | ||
| 167 | public static boolean isDirectory(Context context, String path) { | ||
| 168 | final ContentResolver resolver = context.getContentResolver(); | ||
| 169 | final String[] columns = new String[] { | ||
| 170 | DocumentsContract.Document.COLUMN_MIME_TYPE | ||
| 171 | }; | ||
| 172 | boolean isDirectory = false; | ||
| 173 | Cursor c = null; | ||
| 174 | try { | ||
| 175 | Uri mUri = Uri.parse(path); | ||
| 176 | c = resolver.query(mUri, columns, null, null, null); | ||
| 177 | c.moveToNext(); | ||
| 178 | final String mimeType = c.getString(0); | ||
| 179 | isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR); | ||
| 180 | } catch (Exception e) { | ||
| 181 | Log.error("[FileUtil]: Cannot list files, error: " + e.getMessage()); | ||
| 182 | } finally { | ||
| 183 | closeQuietly(c); | ||
| 184 | } | ||
| 185 | return isDirectory; | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Get file display name from given path | ||
| 190 | * @param path content uri path | ||
| 191 | * @return String display name | ||
| 192 | */ | ||
| 193 | public static String getFilename(Context context, String path) { | ||
| 194 | final ContentResolver resolver = context.getContentResolver(); | ||
| 195 | final String[] columns = new String[] { | ||
| 196 | DocumentsContract.Document.COLUMN_DISPLAY_NAME | ||
| 197 | }; | ||
| 198 | String filename = ""; | ||
| 199 | Cursor c = null; | ||
| 200 | try { | ||
| 201 | Uri mUri = Uri.parse(path); | ||
| 202 | c = resolver.query(mUri, columns, null, null, null); | ||
| 203 | c.moveToNext(); | ||
| 204 | filename = c.getString(0); | ||
| 205 | } catch (Exception e) { | ||
| 206 | Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage()); | ||
| 207 | } finally { | ||
| 208 | closeQuietly(c); | ||
| 209 | } | ||
| 210 | return filename; | ||
| 211 | } | ||
| 212 | |||
| 213 | public static String[] getFilesName(Context context, String path) { | ||
| 214 | Uri uri = Uri.parse(path); | ||
| 215 | List<String> files = new ArrayList<>(); | ||
| 216 | for (MinimalDocumentFile file: FileUtil.listFiles(context, uri)) { | ||
| 217 | files.add(file.getFilename()); | ||
| 218 | } | ||
| 219 | return files.toArray(new String[0]); | ||
| 220 | } | ||
| 221 | |||
| 222 | /** | ||
| 223 | * Get file size from given path. | ||
| 224 | * @param path content uri path | ||
| 225 | * @return long file size | ||
| 226 | */ | ||
| 227 | public static long getFileSize(Context context, String path) { | ||
| 228 | final ContentResolver resolver = context.getContentResolver(); | ||
| 229 | final String[] columns = new String[] { | ||
| 230 | DocumentsContract.Document.COLUMN_SIZE | ||
| 231 | }; | ||
| 232 | long size = 0; | ||
| 233 | Cursor c =null; | ||
| 234 | try { | ||
| 235 | Uri mUri = Uri.parse(path); | ||
| 236 | c = resolver.query(mUri, columns, null, null, null); | ||
| 237 | c.moveToNext(); | ||
| 238 | size = c.getLong(0); | ||
| 239 | } catch (Exception e) { | ||
| 240 | Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage()); | ||
| 241 | } finally { | ||
| 242 | closeQuietly(c); | ||
| 243 | } | ||
| 244 | return size; | ||
| 245 | } | ||
| 246 | |||
| 247 | public static boolean copyUriToInternalStorage(Context context, Uri sourceUri, String destinationParentPath, String destinationFilename) { | ||
| 248 | InputStream input = null; | ||
| 249 | FileOutputStream output = null; | ||
| 250 | try { | ||
| 251 | input = context.getContentResolver().openInputStream(sourceUri); | ||
| 252 | output = new FileOutputStream(destinationParentPath + "/" + destinationFilename); | ||
| 253 | byte[] buffer = new byte[1024]; | ||
| 254 | int len; | ||
| 255 | while ((len = input.read(buffer)) != -1) { | ||
| 256 | output.write(buffer, 0, len); | ||
| 257 | } | ||
| 258 | output.flush(); | ||
| 259 | return true; | ||
| 260 | } catch (Exception e) { | ||
| 261 | Log.error("[FileUtil]: Cannot copy file, error: " + e.getMessage()); | ||
| 262 | } finally { | ||
| 263 | if (input != null) { | ||
| 264 | try { | ||
| 265 | input.close(); | ||
| 266 | } catch (IOException e) { | ||
| 267 | Log.error("[FileUtil]: Cannot close input file, error: " + e.getMessage()); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | if (output != null) { | ||
| 271 | try { | ||
| 272 | output.close(); | ||
| 273 | } catch (IOException e) { | ||
| 274 | Log.error("[FileUtil]: Cannot close output file, error: " + e.getMessage()); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | return false; | ||
| 279 | } | ||
| 280 | |||
| 281 | public static boolean isRootTreeUri(Uri uri) { | ||
| 282 | final List<String> paths = uri.getPathSegments(); | ||
| 283 | return paths.size() == 2 && PATH_TREE.equals(paths.get(0)); | ||
| 284 | } | ||
| 285 | |||
| 286 | public static void closeQuietly(AutoCloseable closeable) { | ||
| 287 | if (closeable != null) { | ||
| 288 | try { | ||
| 289 | closeable.close(); | ||
| 290 | } catch (RuntimeException rethrown) { | ||
| 291 | throw rethrown; | ||
| 292 | } catch (Exception ignored) { | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt new file mode 100644 index 000000000..47fae0933 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | |||
| @@ -0,0 +1,292 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils | ||
| 2 | |||
| 3 | import android.content.Context | ||
| 4 | import android.database.Cursor | ||
| 5 | import android.net.Uri | ||
| 6 | import android.provider.DocumentsContract | ||
| 7 | import androidx.documentfile.provider.DocumentFile | ||
| 8 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile | ||
| 9 | import java.io.FileOutputStream | ||
| 10 | import java.io.IOException | ||
| 11 | import java.io.InputStream | ||
| 12 | import java.net.URLDecoder | ||
| 13 | |||
| 14 | object FileUtil { | ||
| 15 | const val PATH_TREE = "tree" | ||
| 16 | const val DECODE_METHOD = "UTF-8" | ||
| 17 | const val APPLICATION_OCTET_STREAM = "application/octet-stream" | ||
| 18 | const val TEXT_PLAIN = "text/plain" | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Create a file from directory with filename. | ||
| 22 | * @param context Application context | ||
| 23 | * @param directory parent path for file. | ||
| 24 | * @param filename file display name. | ||
| 25 | * @return boolean | ||
| 26 | */ | ||
| 27 | fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? { | ||
| 28 | var decodedFilename = filename | ||
| 29 | try { | ||
| 30 | val directoryUri = Uri.parse(directory) | ||
| 31 | val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null | ||
| 32 | decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD) | ||
| 33 | var mimeType = APPLICATION_OCTET_STREAM | ||
| 34 | if (decodedFilename.endsWith(".txt")) { | ||
| 35 | mimeType = TEXT_PLAIN | ||
| 36 | } | ||
| 37 | val exists = parent.findFile(decodedFilename) | ||
| 38 | return exists ?: parent.createFile(mimeType, decodedFilename) | ||
| 39 | } catch (e: Exception) { | ||
| 40 | Log.error("[FileUtil]: Cannot create file, error: " + e.message) | ||
| 41 | } | ||
| 42 | return null | ||
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Create a directory from directory with filename. | ||
| 47 | * @param context Application context | ||
| 48 | * @param directory parent path for directory. | ||
| 49 | * @param directoryName directory display name. | ||
| 50 | * @return boolean | ||
| 51 | */ | ||
| 52 | fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? { | ||
| 53 | var decodedDirectoryName = directoryName | ||
| 54 | try { | ||
| 55 | val directoryUri = Uri.parse(directory) | ||
| 56 | val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null | ||
| 57 | decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD) | ||
| 58 | val isExist = parent.findFile(decodedDirectoryName) | ||
| 59 | return isExist ?: parent.createDirectory(decodedDirectoryName) | ||
| 60 | } catch (e: Exception) { | ||
| 61 | Log.error("[FileUtil]: Cannot create file, error: " + e.message) | ||
| 62 | } | ||
| 63 | return null | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Open content uri and return file descriptor to JNI. | ||
| 68 | * @param context Application context | ||
| 69 | * @param path Native content uri path | ||
| 70 | * @param openMode will be one of "r", "r", "rw", "wa", "rwa" | ||
| 71 | * @return file descriptor | ||
| 72 | */ | ||
| 73 | @JvmStatic | ||
| 74 | fun openContentUri(context: Context, path: String, openMode: String?): Int { | ||
| 75 | try { | ||
| 76 | val uri = Uri.parse(path) | ||
| 77 | val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!) | ||
| 78 | if (parcelFileDescriptor == null) { | ||
| 79 | Log.error("[FileUtil]: Cannot get the file descriptor from uri: $path") | ||
| 80 | return -1 | ||
| 81 | } | ||
| 82 | val fileDescriptor = parcelFileDescriptor.detachFd() | ||
| 83 | parcelFileDescriptor.close() | ||
| 84 | return fileDescriptor | ||
| 85 | } catch (e: Exception) { | ||
| 86 | Log.error("[FileUtil]: Cannot open content uri, error: " + e.message) | ||
| 87 | } | ||
| 88 | return -1 | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow | ||
| 93 | * This function will be faster than DoucmentFile.listFiles | ||
| 94 | * @param context Application context | ||
| 95 | * @param uri Directory uri. | ||
| 96 | * @return CheapDocument lists. | ||
| 97 | */ | ||
| 98 | fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> { | ||
| 99 | val resolver = context.contentResolver | ||
| 100 | val columns = arrayOf( | ||
| 101 | DocumentsContract.Document.COLUMN_DOCUMENT_ID, | ||
| 102 | DocumentsContract.Document.COLUMN_DISPLAY_NAME, | ||
| 103 | DocumentsContract.Document.COLUMN_MIME_TYPE | ||
| 104 | ) | ||
| 105 | var c: Cursor? = null | ||
| 106 | val results: MutableList<MinimalDocumentFile> = ArrayList() | ||
| 107 | try { | ||
| 108 | val docId: String = if (isRootTreeUri(uri)) { | ||
| 109 | DocumentsContract.getTreeDocumentId(uri) | ||
| 110 | } else { | ||
| 111 | DocumentsContract.getDocumentId(uri) | ||
| 112 | } | ||
| 113 | val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId) | ||
| 114 | c = resolver.query(childrenUri, columns, null, null, null) | ||
| 115 | while (c!!.moveToNext()) { | ||
| 116 | val documentId = c.getString(0) | ||
| 117 | val documentName = c.getString(1) | ||
| 118 | val documentMimeType = c.getString(2) | ||
| 119 | val documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId) | ||
| 120 | val document = MinimalDocumentFile(documentName, documentMimeType, documentUri) | ||
| 121 | results.add(document) | ||
| 122 | } | ||
| 123 | } catch (e: Exception) { | ||
| 124 | Log.error("[FileUtil]: Cannot list file error: " + e.message) | ||
| 125 | } finally { | ||
| 126 | closeQuietly(c) | ||
| 127 | } | ||
| 128 | return results.toTypedArray() | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Check whether given path exists. | ||
| 133 | * @param path Native content uri path | ||
| 134 | * @return bool | ||
| 135 | */ | ||
| 136 | fun exists(context: Context, path: String?): Boolean { | ||
| 137 | var c: Cursor? = null | ||
| 138 | try { | ||
| 139 | val mUri = Uri.parse(path) | ||
| 140 | val columns = arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID) | ||
| 141 | c = context.contentResolver.query(mUri, columns, null, null, null) | ||
| 142 | return c!!.count > 0 | ||
| 143 | } catch (e: Exception) { | ||
| 144 | Log.info("[FileUtil] Cannot find file from given path, error: " + e.message) | ||
| 145 | } finally { | ||
| 146 | closeQuietly(c) | ||
| 147 | } | ||
| 148 | return false | ||
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Check whether given path is a directory | ||
| 153 | * @param path content uri path | ||
| 154 | * @return bool | ||
| 155 | */ | ||
| 156 | fun isDirectory(context: Context, path: String): Boolean { | ||
| 157 | val resolver = context.contentResolver | ||
| 158 | val columns = arrayOf( | ||
| 159 | DocumentsContract.Document.COLUMN_MIME_TYPE | ||
| 160 | ) | ||
| 161 | var isDirectory = false | ||
| 162 | var c: Cursor? = null | ||
| 163 | try { | ||
| 164 | val mUri = Uri.parse(path) | ||
| 165 | c = resolver.query(mUri, columns, null, null, null) | ||
| 166 | c!!.moveToNext() | ||
| 167 | val mimeType = c.getString(0) | ||
| 168 | isDirectory = mimeType == DocumentsContract.Document.MIME_TYPE_DIR | ||
| 169 | } catch (e: Exception) { | ||
| 170 | Log.error("[FileUtil]: Cannot list files, error: " + e.message) | ||
| 171 | } finally { | ||
| 172 | closeQuietly(c) | ||
| 173 | } | ||
| 174 | return isDirectory | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * Get file display name from given path | ||
| 179 | * @param path content uri path | ||
| 180 | * @return String display name | ||
| 181 | */ | ||
| 182 | fun getFilename(context: Context, path: String): String { | ||
| 183 | val resolver = context.contentResolver | ||
| 184 | val columns = arrayOf( | ||
| 185 | DocumentsContract.Document.COLUMN_DISPLAY_NAME | ||
| 186 | ) | ||
| 187 | var filename = "" | ||
| 188 | var c: Cursor? = null | ||
| 189 | try { | ||
| 190 | val mUri = Uri.parse(path) | ||
| 191 | c = resolver.query(mUri, columns, null, null, null) | ||
| 192 | c!!.moveToNext() | ||
| 193 | filename = c.getString(0) | ||
| 194 | } catch (e: Exception) { | ||
| 195 | Log.error("[FileUtil]: Cannot get file size, error: " + e.message) | ||
| 196 | } finally { | ||
| 197 | closeQuietly(c) | ||
| 198 | } | ||
| 199 | return filename | ||
| 200 | } | ||
| 201 | |||
| 202 | fun getFilesName(context: Context, path: String): Array<String> { | ||
| 203 | val uri = Uri.parse(path) | ||
| 204 | val files: MutableList<String> = ArrayList() | ||
| 205 | for (file in listFiles(context, uri)) { | ||
| 206 | files.add(file.filename) | ||
| 207 | } | ||
| 208 | return files.toTypedArray() | ||
| 209 | } | ||
| 210 | |||
| 211 | /** | ||
| 212 | * Get file size from given path. | ||
| 213 | * @param path content uri path | ||
| 214 | * @return long file size | ||
| 215 | */ | ||
| 216 | @JvmStatic | ||
| 217 | fun getFileSize(context: Context, path: String): Long { | ||
| 218 | val resolver = context.contentResolver | ||
| 219 | val columns = arrayOf( | ||
| 220 | DocumentsContract.Document.COLUMN_SIZE | ||
| 221 | ) | ||
| 222 | var size: Long = 0 | ||
| 223 | var c: Cursor? = null | ||
| 224 | try { | ||
| 225 | val mUri = Uri.parse(path) | ||
| 226 | c = resolver.query(mUri, columns, null, null, null) | ||
| 227 | c!!.moveToNext() | ||
| 228 | size = c.getLong(0) | ||
| 229 | } catch (e: Exception) { | ||
| 230 | Log.error("[FileUtil]: Cannot get file size, error: " + e.message) | ||
| 231 | } finally { | ||
| 232 | closeQuietly(c) | ||
| 233 | } | ||
| 234 | return size | ||
| 235 | } | ||
| 236 | |||
| 237 | @JvmStatic | ||
| 238 | fun copyUriToInternalStorage( | ||
| 239 | context: Context, | ||
| 240 | sourceUri: Uri?, | ||
| 241 | destinationParentPath: String, | ||
| 242 | destinationFilename: String | ||
| 243 | ): Boolean { | ||
| 244 | var input: InputStream? = null | ||
| 245 | var output: FileOutputStream? = null | ||
| 246 | try { | ||
| 247 | input = context.contentResolver.openInputStream(sourceUri!!) | ||
| 248 | output = FileOutputStream("$destinationParentPath/$destinationFilename") | ||
| 249 | val buffer = ByteArray(1024) | ||
| 250 | var len: Int | ||
| 251 | while (input!!.read(buffer).also { len = it } != -1) { | ||
| 252 | output.write(buffer, 0, len) | ||
| 253 | } | ||
| 254 | output.flush() | ||
| 255 | return true | ||
| 256 | } catch (e: Exception) { | ||
| 257 | Log.error("[FileUtil]: Cannot copy file, error: " + e.message) | ||
| 258 | } finally { | ||
| 259 | if (input != null) { | ||
| 260 | try { | ||
| 261 | input.close() | ||
| 262 | } catch (e: IOException) { | ||
| 263 | Log.error("[FileUtil]: Cannot close input file, error: " + e.message) | ||
| 264 | } | ||
| 265 | } | ||
| 266 | if (output != null) { | ||
| 267 | try { | ||
| 268 | output.close() | ||
| 269 | } catch (e: IOException) { | ||
| 270 | Log.error("[FileUtil]: Cannot close output file, error: " + e.message) | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | return false | ||
| 275 | } | ||
| 276 | |||
| 277 | fun isRootTreeUri(uri: Uri): Boolean { | ||
| 278 | val paths = uri.pathSegments | ||
| 279 | return paths.size == 2 && PATH_TREE == paths[0] | ||
| 280 | } | ||
| 281 | |||
| 282 | fun closeQuietly(closeable: AutoCloseable?) { | ||
| 283 | if (closeable != null) { | ||
| 284 | try { | ||
| 285 | closeable.close() | ||
| 286 | } catch (rethrown: RuntimeException) { | ||
| 287 | throw rethrown | ||
| 288 | } catch (ignored: Exception) { | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||