From 03511dc8da7e17c9abffdc2a2550e25c84ad1aa5 Mon Sep 17 00:00:00 2001 From: Anggrayudi H Date: Wed, 21 Jul 2021 23:59:42 +0700 Subject: [PATCH] Add requiresWriteAccess to DocumentFileCompat.from*() --- gradle.properties | 2 +- .../storage/file/DocumentFileCompat.kt | 30 ++++++++++--------- .../storage/file/DocumentFileExt.kt | 16 +++++++--- .../com/anggrayudi/storage/file/FileExt.kt | 13 +++++++- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/gradle.properties b/gradle.properties index 514e067..4a3dc0c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,4 @@ android.enableJetifier=true kotlin.code.style=official GROUP=com.anggrayudi POM_ARTIFACT_ID=storage -VERSION_NAME=0.10.0-SNAPSHOT \ No newline at end of file +VERSION_NAME=0.11.0-SNAPSHOT \ No newline at end of file diff --git a/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileCompat.kt b/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileCompat.kt index 10462ca..ad6d54d 100644 --- a/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileCompat.kt +++ b/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileCompat.kt @@ -114,15 +114,16 @@ object DocumentFileCompat { storageId: String = PRIMARY, basePath: String = "", documentType: DocumentFileType = DocumentFileType.ANY, + requiresWriteAccess: Boolean = false, considerRawFile: Boolean = true ): DocumentFile? { if (storageId == DATA) { return DocumentFile.fromFile(context.dataDirectory.child(basePath)) } return if (basePath.isEmpty()) { - getRootDocumentFile(context, storageId, considerRawFile) + getRootDocumentFile(context, storageId, requiresWriteAccess, considerRawFile) } else { - val file = exploreFile(context, storageId, basePath, documentType, considerRawFile) + val file = exploreFile(context, storageId, basePath, documentType, requiresWriteAccess, considerRawFile) if (file == null && basePath.startsWith(Environment.DIRECTORY_DOWNLOADS) && storageId == PRIMARY) { val downloads = context.fromTreeUri(Uri.parse(DOWNLOADS_TREE_URI))?.takeIf { it.canRead() } ?: return null downloads.child(context, basePath.substringAfter('/', ""))?.takeIf { @@ -149,14 +150,15 @@ object DocumentFileCompat { context: Context, fullPath: String, documentType: DocumentFileType = DocumentFileType.ANY, + requiresWriteAccess: Boolean = false, considerRawFile: Boolean = true ): DocumentFile? { return if (fullPath.startsWith('/')) { // absolute path - fromFile(context, File(fullPath), documentType, considerRawFile) + fromFile(context, File(fullPath), documentType, requiresWriteAccess, considerRawFile) } else { // simple path - fromSimplePath(context, fullPath.substringBefore(':'), fullPath.substringAfter(':'), documentType, considerRawFile) + fromSimplePath(context, fullPath.substringBefore(':'), fullPath.substringAfter(':'), documentType, requiresWriteAccess, considerRawFile) } } @@ -176,17 +178,18 @@ object DocumentFileCompat { context: Context, file: File, documentType: DocumentFileType = DocumentFileType.ANY, + requiresWriteAccess: Boolean = false, considerRawFile: Boolean = true ): DocumentFile? { - return if (file.canRead() && (considerRawFile || file.isExternalStorageManager(context))) { + return if (file.checkRequirements(context, requiresWriteAccess, considerRawFile)) { if (documentType == DocumentFileType.FILE && !file.isFile || documentType == DocumentFileType.FOLDER && !file.isDirectory) null else DocumentFile.fromFile(file) } else { val basePath = file.getBasePath(context).removeForbiddenCharsFromFilename().trimFileSeparator() - exploreFile(context, file.getStorageId(context), basePath, documentType, considerRawFile) - ?: fromSimplePath(context, file.getStorageId(context), basePath, documentType, considerRawFile) + exploreFile(context, file.getStorageId(context), basePath, documentType, requiresWriteAccess, considerRawFile) + ?: fromSimplePath(context, file.getStorageId(context), basePath, documentType, requiresWriteAccess, considerRawFile) } } @@ -209,9 +212,7 @@ object DocumentFileCompat { if (subFile.isNotEmpty()) { rawFile = File("$rawFile/$subFile".trimEnd('/')) } - if (rawFile.canRead() && (considerRawFile || rawFile.isExternalStorageManager(context)) - && (requiresWriteAccess && rawFile.isWritable(context) || !requiresWriteAccess) - ) { + if (rawFile.checkRequirements(context, requiresWriteAccess, considerRawFile)) { return DocumentFile.fromFile(rawFile) } @@ -484,7 +485,7 @@ object DocumentFileCompat { return null } } - return currentDirectory.takeIf { requiresWriteAccess && it.isWritable(context) || !requiresWriteAccess } + return currentDirectory.takeIfWritable(context, requiresWriteAccess) } /** @@ -545,7 +546,7 @@ object DocumentFileCompat { } } results.indices.forEach { index -> - results[index] = results[index]?.takeIf { requiresWriteAccess && it.isWritable(context) || !requiresWriteAccess } + results[index] = results[index]?.takeIfWritable(context, requiresWriteAccess) } return results } @@ -652,10 +653,11 @@ object DocumentFileCompat { storageId: String, basePath: String, documentType: DocumentFileType, + requiresWriteAccess: Boolean, considerRawFile: Boolean ): DocumentFile? { val rawFile = File(buildAbsolutePath(context, storageId, basePath)) - if ((considerRawFile || storageId == DATA) && rawFile.canRead()) { + if ((considerRawFile || storageId == DATA) && rawFile.canRead() && rawFile.shouldWritable(context, requiresWriteAccess)) { return if (documentType == DocumentFileType.ANY || documentType == DocumentFileType.FILE && rawFile.isFile || documentType == DocumentFileType.FOLDER && rawFile.isDirectory ) { @@ -665,7 +667,7 @@ object DocumentFileCompat { } } val file = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - getRootDocumentFile(context, storageId, considerRawFile)?.child(context, basePath) ?: return null + getRootDocumentFile(context, storageId, requiresWriteAccess, considerRawFile)?.child(context, basePath) ?: return null } else { val directorySequence = getDirectorySequence(basePath).toMutableList() val parentTree = ArrayList(directorySequence.size) diff --git a/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileExt.kt b/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileExt.kt index b332012..d1c6aff 100644 --- a/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileExt.kt +++ b/storage/src/main/java/com/anggrayudi/storage/file/DocumentFileExt.kt @@ -260,9 +260,7 @@ fun DocumentFile.toRawDocumentFile(context: Context): DocumentFile? { fun DocumentFile.toTreeDocumentFile(context: Context): DocumentFile? { return if (isRawFile) { DocumentFileCompat.fromFile(context, toRawFile(context) ?: return null, considerRawFile = false) - } else { - this - } + } else takeIf { it.isTreeDocumentFile } } fun DocumentFile.toMediaFile(context: Context) = if (isTreeDocumentFile) null else MediaFile(context, uri) @@ -291,7 +289,7 @@ fun DocumentFile.child(context: Context, path: String, requiresWriteAccess: Bool } currentDirectory } - file?.takeIf { requiresWriteAccess && it.isWritable(context) || !requiresWriteAccess } + file?.takeIfWritable(context, requiresWriteAccess) } else -> null } @@ -333,6 +331,16 @@ fun DocumentFile.quickFindTreeFile(context: Context, resolver: ContentResolver, return null } +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun DocumentFile.shouldWritable(context: Context, requiresWriteAccess: Boolean) = requiresWriteAccess && isWritable(context) || !requiresWriteAccess + +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun DocumentFile.takeIfWritable(context: Context, requiresWriteAccess: Boolean) = takeIf { it.shouldWritable(context, requiresWriteAccess) } + +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun DocumentFile.checkRequirements(context: Context, requiresWriteAccess: Boolean, considerRawFile: Boolean) = canRead() && + (considerRawFile || isExternalStorageManager(context)) && shouldWritable(context, requiresWriteAccess) + /** * @return File path without storage ID. Returns empty `String` if: * * It is the root path diff --git a/storage/src/main/java/com/anggrayudi/storage/file/FileExt.kt b/storage/src/main/java/com/anggrayudi/storage/file/FileExt.kt index d74d8b4..cfd7f45 100644 --- a/storage/src/main/java/com/anggrayudi/storage/file/FileExt.kt +++ b/storage/src/main/java/com/anggrayudi/storage/file/FileExt.kt @@ -5,6 +5,7 @@ package com.anggrayudi.storage.file import android.content.Context import android.os.Build import android.os.Environment +import androidx.annotation.RestrictTo import androidx.annotation.WorkerThread import androidx.core.content.ContextCompat import androidx.documentfile.provider.DocumentFile @@ -100,7 +101,7 @@ val File.mimeType: String? @JvmOverloads fun File.getRootRawFile(context: Context, requiresWriteAccess: Boolean = false) = getRootPath(context).let { if (it.isEmpty()) null else File(it).run { - if (canRead() && (requiresWriteAccess && isWritable(context) || !requiresWriteAccess)) this else null + if (canRead()) takeIfWritable(context, requiresWriteAccess) else null } } @@ -111,6 +112,16 @@ fun File.canModify(context: Context) = canRead() && isWritable(context) val File.isEmpty: Boolean get() = isFile && length() == 0L || isDirectory && list().isNullOrEmpty() +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun File.shouldWritable(context: Context, requiresWriteAccess: Boolean) = requiresWriteAccess && isWritable(context) || !requiresWriteAccess + +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun File.takeIfWritable(context: Context, requiresWriteAccess: Boolean) = takeIf { it.shouldWritable(context, requiresWriteAccess) } + +@RestrictTo(RestrictTo.Scope.LIBRARY) +fun File.checkRequirements(context: Context, requiresWriteAccess: Boolean, considerRawFile: Boolean) = canRead() && + (considerRawFile || isExternalStorageManager(context)) && shouldWritable(context, requiresWriteAccess) + fun File.createNewFileIfPossible(): Boolean = try { isFile || createNewFile() } catch (e: IOException) {