#283 fixed restoring to missing Download subdir
This commit is contained in:
parent
25ae1a6c1e
commit
38b9f84af0
4 changed files with 67 additions and 47 deletions
|
@ -15,6 +15,10 @@ All notable changes to this project will be documented in this file.
|
|||
- Slideshow: option for no transition
|
||||
- Widget: tap action setting
|
||||
|
||||
### Fixed
|
||||
|
||||
- restoring to missing Download subdir
|
||||
|
||||
## <a id="v1.7.0"></a>[v1.7.0] - 2022-09-19
|
||||
|
||||
### Added
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.content.*
|
|||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import com.commonsware.cwac.document.DocumentFileCompat
|
||||
|
@ -391,8 +390,13 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
effectiveTargetDir = targetDir
|
||||
targetDirDocFile = StorageUtils.createDirectoryDocIfAbsent(activity, targetDir)
|
||||
if (!File(targetDir).exists()) {
|
||||
callback.onFailure(Exception("failed to create directory at path=$targetDir"))
|
||||
return
|
||||
val downloadDirPath = StorageUtils.getDownloadDirPath(activity, targetDir)
|
||||
val isDownloadSubdir = downloadDirPath != null && targetDir.startsWith(downloadDirPath)
|
||||
// download subdirectories can be created later by Media Store insertion
|
||||
if (!isDownloadSubdir) {
|
||||
callback.onFailure(Exception("failed to create directory at path=$targetDir"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,54 +539,57 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
targetNameWithoutExtension: String,
|
||||
write: (OutputStream) -> Unit,
|
||||
): String {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && isDownloadDir(activity, targetDir)) {
|
||||
val targetFileName = "$targetNameWithoutExtension${extensionFor(mimeType)}"
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, targetFileName)
|
||||
put(MediaStore.MediaColumns.IS_PENDING, 1)
|
||||
}
|
||||
val resolver = activity.contentResolver
|
||||
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val downloadDirPath = StorageUtils.getDownloadDirPath(activity, targetDir)
|
||||
val isDownloadSubdir = downloadDirPath != null && targetDir.startsWith(downloadDirPath)
|
||||
if (isDownloadSubdir) {
|
||||
val volumePath = StorageUtils.getVolumePath(activity, targetDir)
|
||||
val relativePath = targetDir.substring(volumePath?.length ?: 0)
|
||||
|
||||
uri?.let {
|
||||
resolver.openOutputStream(uri)?.use(write)
|
||||
values.clear()
|
||||
values.put(MediaStore.MediaColumns.IS_PENDING, 0)
|
||||
resolver.update(uri, values, null, null)
|
||||
} ?: throw Exception("MediaStore failed for some reason")
|
||||
|
||||
File(targetDir, targetFileName).path
|
||||
} else {
|
||||
targetDirDocFile ?: throw Exception("failed to get tree doc for directory at path=$targetDir")
|
||||
|
||||
// the file created from a `TreeDocumentFile` is also a `TreeDocumentFile`
|
||||
// but in order to open an output stream to it, we need to use a `SingleDocumentFile`
|
||||
// through a document URI, not a tree URI
|
||||
// note that `DocumentFile.getParentFile()` returns null if we did not pick a tree first
|
||||
val targetTreeFile = targetDirDocFile.createFile(mimeType, targetNameWithoutExtension)
|
||||
val targetDocFile = DocumentFileCompat.fromSingleUri(activity, targetTreeFile.uri)
|
||||
|
||||
try {
|
||||
targetDocFile.openOutputStream().use(write)
|
||||
} catch (e: Exception) {
|
||||
// remove empty file
|
||||
if (targetDocFile.exists()) {
|
||||
targetDocFile.delete()
|
||||
val targetFileName = "$targetNameWithoutExtension${extensionFor(mimeType)}"
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, targetFileName)
|
||||
put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.MediaColumns.IS_PENDING, 1)
|
||||
}
|
||||
throw e
|
||||
val resolver = activity.contentResolver
|
||||
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
|
||||
|
||||
uri?.let {
|
||||
resolver.openOutputStream(uri)?.use(write)
|
||||
values.clear()
|
||||
values.put(MediaStore.MediaColumns.IS_PENDING, 0)
|
||||
resolver.update(uri, values, null, null)
|
||||
} ?: throw Exception("MediaStore failed for some reason")
|
||||
|
||||
return File(targetDir, targetFileName).path
|
||||
}
|
||||
|
||||
// the source file name and the created document file name can be different when:
|
||||
// - a file with the same name already exists, some implementations give a suffix like ` (1)`, some *do not*
|
||||
// - the original extension does not match the extension added by the underlying provider
|
||||
val fileName = targetDocFile.name
|
||||
targetDir + fileName
|
||||
}
|
||||
}
|
||||
|
||||
private fun isDownloadDir(context: Context, dirPath: String): Boolean {
|
||||
val relativeDir = removeTrailingSeparator(PathSegments(context, dirPath).relativeDir ?: "")
|
||||
return relativeDir == Environment.DIRECTORY_DOWNLOADS
|
||||
targetDirDocFile ?: throw Exception("failed to get tree doc for directory at path=$targetDir")
|
||||
|
||||
// the file created from a `TreeDocumentFile` is also a `TreeDocumentFile`
|
||||
// but in order to open an output stream to it, we need to use a `SingleDocumentFile`
|
||||
// through a document URI, not a tree URI
|
||||
// note that `DocumentFile.getParentFile()` returns null if we did not pick a tree first
|
||||
val targetTreeFile = targetDirDocFile.createFile(mimeType, targetNameWithoutExtension)
|
||||
val targetDocFile = DocumentFileCompat.fromSingleUri(activity, targetTreeFile.uri)
|
||||
|
||||
try {
|
||||
targetDocFile.openOutputStream().use(write)
|
||||
} catch (e: Exception) {
|
||||
// remove empty file
|
||||
if (targetDocFile.exists()) {
|
||||
targetDocFile.delete()
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
// the source file name and the created document file name can be different when:
|
||||
// - a file with the same name already exists, some implementations give a suffix like ` (1)`, some *do not*
|
||||
// - the original extension does not match the extension added by the underlying provider
|
||||
val fileName = targetDocFile.name
|
||||
return targetDir + fileName
|
||||
}
|
||||
|
||||
override suspend fun renameMultiple(
|
||||
|
|
|
@ -105,7 +105,11 @@ object PermissionManager {
|
|||
val primaryDir = dirSegments.firstOrNull()
|
||||
if (getRestrictedPrimaryDirectories().contains(primaryDir) && dirSegments.size > 1) {
|
||||
// request secondary directory (if any) for restricted primary directory
|
||||
dirSet.add(dirSegments.take(2).joinToString(File.separator))
|
||||
val dir = dirSegments.take(2).joinToString(File.separator)
|
||||
// only register directories that exist on storage, so they can be selected for access grant
|
||||
if (File(volumePath, dir).exists()) {
|
||||
dirSet.add(dir)
|
||||
}
|
||||
} else {
|
||||
primaryDir?.let { dirSet.add(it) }
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.content.pm.PackageManager
|
|||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.storage.StorageManager
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
|
@ -93,6 +94,10 @@ object StorageUtils {
|
|||
return getVolumePaths(context).firstOrNull { anyPath.startsWith(it) }
|
||||
}
|
||||
|
||||
fun getDownloadDirPath(context: Context, anyPath: String): String? {
|
||||
return getVolumePath(context, anyPath)?.let { volumePath -> ensureTrailingSeparator(File(volumePath, Environment.DIRECTORY_DOWNLOADS).path) }
|
||||
}
|
||||
|
||||
private fun getPathStepIterator(context: Context, anyPath: String, root: String?): Iterator<String?>? {
|
||||
val rootLength = (root ?: getVolumePath(context, anyPath))?.length ?: return null
|
||||
|
||||
|
|
Loading…
Reference in a new issue