handle scoped storage changes
This commit is contained in:
parent
df2fc3f38a
commit
a781514567
12 changed files with 374 additions and 154 deletions
|
@ -95,14 +95,10 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
|
|||
updateNavigationBarColor()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
actionOnPermission = null
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
funAfterSAFPermission = null
|
||||
actionOnPermission = null
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -223,14 +219,14 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
|
|||
Log.i(TAG, "onActivityResult: partition=$partition")
|
||||
Log.i(TAG, "onActivityResult: checkedDocumentPath=$checkedDocumentPath")
|
||||
Log.i(TAG, "onActivityResult: treeUri=${resultData?.data}")
|
||||
Log.i(TAG, "onActivityResult: tree documentId=${DocumentsContract.getTreeDocumentId(resultData?.data)}")
|
||||
Log.i(TAG, "onActivityResult: tree documentId=${resultData?.data?.let { DocumentsContract.getTreeDocumentId(it) }}")
|
||||
val sdOtgPattern = Pattern.compile(SD_OTG_SHORT)
|
||||
|
||||
if (requestCode == OPEN_DOCUMENT_TREE_PRIMARY) {
|
||||
if (resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
|
||||
if (isProperInternalAndroidRoot(resultData.data!!)) {
|
||||
if (resultData.dataString == baseConfig.primaryAndroidTreeUri) {
|
||||
toast(R.string.sd_card_usb_same)
|
||||
if (resultData.dataString == baseConfig.OTGTreeUri || resultData.dataString == baseConfig.sdTreeUri) {
|
||||
toast("Select internal storage")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -447,7 +443,7 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
|
|||
if (isCopyOperation) {
|
||||
startCopyMove(fileDirItems, destination, isCopyOperation, copyPhotoVideoOnly, copyHidden)
|
||||
} else {
|
||||
if (isPathOnOTG(source) || isPathOnOTG(destination) || isPathOnSD(source) || isPathOnSD(destination) || fileDirItems.first().isDirectory) {
|
||||
if (isPathOnOTG(source) || isPathOnOTG(destination) || isPathOnSD(source) || isPathOnSD(destination) || isRestrictedAndroidDir(source) || isRestrictedAndroidDir(destination) || fileDirItems.first().isDirectory) {
|
||||
handleSAFDialog(source) {
|
||||
if (it) {
|
||||
startCopyMove(fileDirItems, destination, isCopyOperation, copyPhotoVideoOnly, copyHidden)
|
||||
|
|
|
@ -18,11 +18,16 @@ import com.simplemobiletools.commons.extensions.*
|
|||
import com.simplemobiletools.commons.helpers.getFilePlaceholderDrawables
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import kotlinx.android.synthetic.main.filepicker_list_item.view.*
|
||||
import java.util.*
|
||||
import java.util.HashMap
|
||||
import java.util.Locale
|
||||
import kotlinx.android.synthetic.main.filepicker_list_item.view.list_item_details
|
||||
import kotlinx.android.synthetic.main.filepicker_list_item.view.list_item_icon
|
||||
import kotlinx.android.synthetic.main.filepicker_list_item.view.list_item_name
|
||||
|
||||
class FilepickerItemsAdapter(activity: BaseSimpleActivity, val fileDirItems: List<FileDirItem>, recyclerView: MyRecyclerView,
|
||||
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick) {
|
||||
class FilepickerItemsAdapter(
|
||||
activity: BaseSimpleActivity, val fileDirItems: List<FileDirItem>, recyclerView: MyRecyclerView,
|
||||
itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick) {
|
||||
|
||||
private lateinit var fileDrawable: Drawable
|
||||
private lateinit var folderDrawable: Drawable
|
||||
|
@ -110,13 +115,15 @@ class FilepickerItemsAdapter(activity: BaseSimpleActivity, val fileDirItems: Lis
|
|||
}
|
||||
|
||||
if (!activity.isDestroyed && !activity.isFinishing) {
|
||||
if (activity.isRestrictedAndroidDir(path)) {
|
||||
itemToLoad = activity.getPrimaryAndroidSAFUri(path)
|
||||
} else if (hasOTGConnected && itemToLoad is String && activity.isPathOnOTG(itemToLoad)) {
|
||||
itemToLoad = itemToLoad.getOTGPublicPath(activity)
|
||||
}
|
||||
|
||||
if (itemToLoad.toString().isGif()) {
|
||||
Glide.with(activity).asBitmap().load(itemToLoad).apply(options).into(list_item_icon)
|
||||
} else {
|
||||
if (hasOTGConnected && itemToLoad is String && activity.isPathOnOTG(itemToLoad)) {
|
||||
itemToLoad = itemToLoad.getOTGPublicPath(activity)
|
||||
}
|
||||
|
||||
Glide.with(activity)
|
||||
.load(itemToLoad)
|
||||
.transition(withCrossFade())
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.io.File
|
|||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashMap
|
||||
|
||||
class CopyMoveTask(
|
||||
val activity: BaseSimpleActivity, val copyOnly: Boolean, val copyMediaOnly: Boolean, val conflictResolutions: LinkedHashMap<String, Int>,
|
||||
|
@ -189,6 +190,21 @@ class CopyMoveTask(
|
|||
copy(oldFileDirItem, newFileDirItem)
|
||||
}
|
||||
mTransferredFiles.add(source)
|
||||
} else if (activity.isRestrictedAndroidDir(source.path)) {
|
||||
activity.getStorageItemsWithTreeUri(source.path, true) { files ->
|
||||
for (child in files) {
|
||||
val newPath = "$destinationPath/${child.name}"
|
||||
if (activity.getDoesFilePathExist(newPath)) {
|
||||
continue
|
||||
}
|
||||
|
||||
val oldPath = "${source.path}/${child.name}"
|
||||
val oldFileDirItem = FileDirItem(oldPath, child.name, child.isDirectory, 0, child.size)
|
||||
val newFileDirItem = FileDirItem(newPath, child.name, child.isDirectory)
|
||||
copy(oldFileDirItem, newFileDirItem)
|
||||
}
|
||||
mTransferredFiles.add(source)
|
||||
}
|
||||
} else {
|
||||
val children = File(source.path).list()
|
||||
for (child in children) {
|
||||
|
@ -264,6 +280,7 @@ class CopyMoveTask(
|
|||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
activity.showErrorToast(e)
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
|
|
|
@ -5,8 +5,8 @@ import androidx.appcompat.app.AlertDialog
|
|||
import com.simplemobiletools.commons.R
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import kotlinx.android.synthetic.main.dialog_create_new_folder.view.*
|
||||
import java.io.File
|
||||
import kotlinx.android.synthetic.main.dialog_create_new_folder.view.*
|
||||
|
||||
class CreateNewFolderDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) {
|
||||
init {
|
||||
|
@ -57,6 +57,7 @@ class CreateNewFolderDialog(val activity: BaseSimpleActivity, val path: String,
|
|||
}
|
||||
}
|
||||
}
|
||||
activity.isRestrictedAndroidDir(path) && activity.createSAFOnlyDirectory(path) -> sendSuccess(alertDialog, path)
|
||||
File(path).mkdirs() -> sendSuccess(alertDialog, path)
|
||||
else -> activity.toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
|
|
|
@ -28,15 +28,17 @@ import java.util.*
|
|||
* @param showFAB toggle the displaying of a Floating Action Button for creating new folders
|
||||
* @param callback the callback used for returning the selected file/folder
|
||||
*/
|
||||
class FilePickerDialog(val activity: BaseSimpleActivity,
|
||||
var currPath: String = Environment.getExternalStorageDirectory().toString(),
|
||||
val pickFile: Boolean = true,
|
||||
var showHidden: Boolean = false,
|
||||
val showFAB: Boolean = false,
|
||||
val canAddShowHiddenButton: Boolean = false,
|
||||
val forceShowRoot: Boolean = false,
|
||||
val showFavoritesButton: Boolean = false,
|
||||
val callback: (pickedPath: String) -> Unit) : Breadcrumbs.BreadcrumbsListener {
|
||||
class FilePickerDialog(
|
||||
val activity: BaseSimpleActivity,
|
||||
var currPath: String = Environment.getExternalStorageDirectory().toString(),
|
||||
val pickFile: Boolean = true,
|
||||
var showHidden: Boolean = false,
|
||||
val showFAB: Boolean = false,
|
||||
val canAddShowHiddenButton: Boolean = false,
|
||||
val forceShowRoot: Boolean = false,
|
||||
val showFavoritesButton: Boolean = false,
|
||||
val callback: (pickedPath: String) -> Unit
|
||||
) : Breadcrumbs.BreadcrumbsListener {
|
||||
|
||||
private var mFirstUpdate = true
|
||||
private var mPrevPath = ""
|
||||
|
@ -204,6 +206,11 @@ class FilePickerDialog(val activity: BaseSimpleActivity,
|
|||
if ((pickFile && fileDocument.isFile) || (!pickFile && fileDocument.isDirectory)) {
|
||||
sendSuccess()
|
||||
}
|
||||
} else if (activity.isRestrictedAndroidDir(currPath)) {
|
||||
val document = activity.getSomePrimaryAndroidSAFDocument(currPath) ?: return
|
||||
if ((pickFile && document.isFile) || (!pickFile && document.isDirectory)) {
|
||||
sendSuccess()
|
||||
}
|
||||
} else {
|
||||
val file = File(currPath)
|
||||
if ((pickFile && file.isFile) || (!pickFile && file.isDirectory)) {
|
||||
|
@ -233,31 +240,39 @@ class FilePickerDialog(val activity: BaseSimpleActivity,
|
|||
|
||||
private fun getRegularItems(path: String, lastModifieds: HashMap<String, Long>, callback: (List<FileDirItem>) -> Unit) {
|
||||
val items = ArrayList<FileDirItem>()
|
||||
val base = File(path)
|
||||
val files = base.listFiles()
|
||||
if (files == null) {
|
||||
|
||||
if (activity.isRestrictedAndroidDir(path)) {
|
||||
activity.handlePrimarySAFDialog(path) {
|
||||
activity.getStorageItemsWithTreeUri(path, showHidden) {
|
||||
callback(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val base = File(path)
|
||||
val files = base.listFiles()
|
||||
if (files == null) {
|
||||
callback(items)
|
||||
return
|
||||
}
|
||||
for (file in files) {
|
||||
if (!showHidden && file.name.startsWith('.')) {
|
||||
continue
|
||||
}
|
||||
|
||||
val curPath = file.absolutePath
|
||||
val curName = curPath.getFilenameFromPath()
|
||||
val size = file.length()
|
||||
var lastModified = lastModifieds.remove(curPath)
|
||||
val isDirectory = if (lastModified != null) false else file.isDirectory
|
||||
if (lastModified == null) {
|
||||
lastModified = 0 // we don't actually need the real lastModified that badly, do not check file.lastModified()
|
||||
}
|
||||
|
||||
val children = if (isDirectory) file.getDirectChildrenCount(activity, showHidden) else 0
|
||||
items.add(FileDirItem(curPath, curName, isDirectory, children, size, lastModified))
|
||||
}
|
||||
callback(items)
|
||||
return
|
||||
}
|
||||
|
||||
for (file in files) {
|
||||
if (!showHidden && file.name.startsWith('.')) {
|
||||
continue
|
||||
}
|
||||
|
||||
val curPath = file.absolutePath
|
||||
val curName = curPath.getFilenameFromPath()
|
||||
val size = file.length()
|
||||
var lastModified = lastModifieds.remove(curPath)
|
||||
val isDirectory = if (lastModified != null) false else file.isDirectory
|
||||
if (lastModified == null) {
|
||||
lastModified = 0 // we don't actually need the real lastModified that badly, do not check file.lastModified()
|
||||
}
|
||||
|
||||
val children = if (isDirectory) file.getDirectChildrenCount(showHidden) else 0
|
||||
items.add(FileDirItem(curPath, curName, isDirectory, children, size, lastModified))
|
||||
}
|
||||
callback(items)
|
||||
}
|
||||
|
||||
private fun containsDirectory(items: List<FileDirItem>) = items.any { it.isDirectory }
|
||||
|
|
|
@ -88,6 +88,13 @@ class PropertiesDialog() {
|
|||
} catch (e: Exception) {
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
} else if (activity.isRestrictedAndroidDir(path)) {
|
||||
try {
|
||||
ExifInterface(activity.contentResolver.openInputStream(activity.getPrimaryAndroidSAFUri(path))!!)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
} else {
|
||||
ExifInterface(fileDirItem.path)
|
||||
}
|
||||
|
@ -110,7 +117,7 @@ class PropertiesDialog() {
|
|||
|
||||
when {
|
||||
fileDirItem.isDirectory -> {
|
||||
addProperty(R.string.direct_children_count, fileDirItem.getDirectChildrenCount(activity, countHiddenItems).toString())
|
||||
addProperty(R.string.direct_children_count, fileDirItem.getDirectChildrenCount(activity as BaseSimpleActivity, countHiddenItems).toString())
|
||||
addProperty(R.string.files_count, "…", R.id.properties_file_count)
|
||||
}
|
||||
fileDirItem.path.isImageSlow() -> {
|
||||
|
@ -144,7 +151,11 @@ class PropertiesDialog() {
|
|||
if (activity.baseConfig.appId.removeSuffix(".debug") == "com.simplemobiletools.filemanager.pro") {
|
||||
addProperty(R.string.md5, "…", R.id.properties_md5)
|
||||
ensureBackgroundThread {
|
||||
val md5 = File(path).md5()
|
||||
val md5 = if (activity.isRestrictedAndroidDir(path)) {
|
||||
activity.contentResolver.openInputStream(activity.getPrimaryAndroidSAFUri(path))?.md5()
|
||||
} else {
|
||||
File(path).md5()
|
||||
}
|
||||
activity.runOnUiThread {
|
||||
(view.findViewById<LinearLayout>(R.id.properties_md5).property_value as TextView).text = md5
|
||||
}
|
||||
|
@ -220,6 +231,13 @@ class PropertiesDialog() {
|
|||
} catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
} else if (activity.isRestrictedAndroidDir(path)) {
|
||||
try {
|
||||
ExifInterface(activity.contentResolver.openInputStream(activity.getPrimaryAndroidSAFUri(path))!!)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ExifInterface(path)
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import java.io.OutputStream
|
|||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlinx.android.synthetic.main.dialog_title.view.*
|
||||
import org.w3c.dom.Document
|
||||
|
||||
fun AppCompatActivity.updateActionBarTitle(text: String, color: Int = baseConfig.primaryColor) {
|
||||
supportActionBar?.title = Html.fromHtml("<font color='${color.getContrastColor().toHex()}'>$text</font>")
|
||||
|
@ -148,7 +149,7 @@ fun BaseSimpleActivity.isShowingSAFDialog(path: String): Boolean {
|
|||
}
|
||||
|
||||
fun BaseSimpleActivity.isShowingSAFPrimaryDialog(path: String): Boolean {
|
||||
return if (isSAFOnlyRoot(path) && (baseConfig.primaryAndroidTreeUri.isEmpty() || !hasProperStoredPrimaryTreeUri())) {
|
||||
return if (isRPlus() && isSAFOnlyRoot(path) && (baseConfig.primaryAndroidTreeUri.isEmpty() || !hasProperStoredPrimaryTreeUri())) {
|
||||
runOnUiThread {
|
||||
if (!isDestroyed && !isFinishing) {
|
||||
WritePermissionDialog(this, false) {
|
||||
|
@ -628,7 +629,7 @@ fun BaseSimpleActivity.deleteFile(fileDirItem: FileDirItem, allowDeleteFolder: B
|
|||
|
||||
fun BaseSimpleActivity.deleteFileBg(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
|
||||
val path = fileDirItem.path
|
||||
if (isRPlus() && isSAFOnlyRoot(path)) {
|
||||
if (isRestrictedAndroidDir(path)) {
|
||||
deleteSAFOnlyDir(path, allowDeleteFolder, callback)
|
||||
} else {
|
||||
val file = File(path)
|
||||
|
@ -707,7 +708,27 @@ fun Activity.rescanPaths(paths: List<String>, callback: (() -> Unit)? = null) {
|
|||
}
|
||||
|
||||
fun BaseSimpleActivity.renameFile(oldPath: String, newPath: String, callback: ((success: Boolean) -> Unit)? = null) {
|
||||
if (needsStupidWritePermissions(newPath)) {
|
||||
if (isRestrictedAndroidDir(oldPath)) {
|
||||
handlePrimarySAFDialog(oldPath) {
|
||||
if (!it) {
|
||||
runOnUiThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
return@handlePrimarySAFDialog
|
||||
}
|
||||
try {
|
||||
val success = renameSAFOnlyDocument(oldPath, newPath)
|
||||
runOnUiThread {
|
||||
callback?.invoke(success)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
runOnUiThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (needsStupidWritePermissions(newPath)) {
|
||||
handleSAFDialog(newPath) {
|
||||
if (!it) {
|
||||
return@handleSAFDialog
|
||||
|
@ -752,49 +773,35 @@ fun BaseSimpleActivity.renameFile(oldPath: String, newPath: String, callback: ((
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (isRestrictedAndroidDir(oldPath)) {
|
||||
try {
|
||||
val success = renameSAFOnlyDocument(oldPath, newPath)
|
||||
runOnUiThread {
|
||||
callback?.invoke(success)
|
||||
val oldFile = File(oldPath)
|
||||
val newFile = File(newPath)
|
||||
val tempFile = oldFile.createTempFile()
|
||||
val oldToTempSucceeds = oldFile.renameTo(tempFile)
|
||||
val tempToNewSucceeds = tempFile.renameTo(newFile)
|
||||
if (oldToTempSucceeds && tempToNewSucceeds) {
|
||||
if (newFile.isDirectory) {
|
||||
updateInMediaStore(oldPath, newPath)
|
||||
rescanPath(newPath) {
|
||||
runOnUiThread {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
scanPathRecursively(newPath)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
runOnUiThread {
|
||||
callback?.invoke(false)
|
||||
} else {
|
||||
if (!baseConfig.keepLastModified) {
|
||||
newFile.setLastModified(System.currentTimeMillis())
|
||||
}
|
||||
updateInMediaStore(oldPath, newPath)
|
||||
scanPathsRecursively(arrayListOf(newPath)) {
|
||||
runOnUiThread {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val oldFile = File(oldPath)
|
||||
val newFile = File(newPath)
|
||||
val tempFile = oldFile.createTempFile()
|
||||
val oldToTempSucceeds = oldFile.renameTo(tempFile)
|
||||
val tempToNewSucceeds = tempFile.renameTo(newFile)
|
||||
if (oldToTempSucceeds && tempToNewSucceeds) {
|
||||
if (newFile.isDirectory) {
|
||||
updateInMediaStore(oldPath, newPath)
|
||||
rescanPath(newPath) {
|
||||
runOnUiThread {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
scanPathRecursively(newPath)
|
||||
}
|
||||
} else {
|
||||
if (!baseConfig.keepLastModified) {
|
||||
newFile.setLastModified(System.currentTimeMillis())
|
||||
}
|
||||
updateInMediaStore(oldPath, newPath)
|
||||
scanPathsRecursively(arrayListOf(newPath)) {
|
||||
runOnUiThread {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tempFile.delete()
|
||||
runOnUiThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
tempFile.delete()
|
||||
runOnUiThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -899,6 +906,12 @@ fun BaseSimpleActivity.getFileOutputStreamSync(path: String, mimeType: String, p
|
|||
showErrorToast(e)
|
||||
null
|
||||
}
|
||||
} else if (isRestrictedAndroidDir(path)) {
|
||||
val uri = getPrimaryAndroidSAFUri(path)
|
||||
if (!getDoesFilePathExist(path)) {
|
||||
createSAFOnlyFile(path)
|
||||
}
|
||||
applicationContext.contentResolver.openOutputStream(uri)
|
||||
} else {
|
||||
if (targetFile.parentFile?.exists() == false) {
|
||||
targetFile.parentFile.mkdirs()
|
||||
|
@ -1021,6 +1034,10 @@ fun BaseSimpleActivity.createDirectorySync(directory: String): Boolean {
|
|||
return newDir != null
|
||||
}
|
||||
|
||||
if (isRestrictedAndroidDir(directory)) {
|
||||
return createSAFOnlyDirectory(directory)
|
||||
}
|
||||
|
||||
return File(directory).mkdirs()
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ fun Context.isPathOnSD(path: String) = sdCardPath.isNotEmpty() && path.startsWit
|
|||
|
||||
fun Context.isPathOnOTG(path: String) = otgPath.isNotEmpty() && path.startsWith(otgPath)
|
||||
|
||||
val DIRS_ACCESSIBLE_ONLY_WITH_SAF = listOf("/Android")
|
||||
val DIRS_ACCESSIBLE_ONLY_WITH_SAF = listOf("/Android/data", "/Android/obb")
|
||||
|
||||
fun Context.getSAFOnlyDirs(): List<String> {
|
||||
return DIRS_ACCESSIBLE_ONLY_WITH_SAF.map { "$internalStoragePath$it" }
|
||||
|
@ -480,12 +480,10 @@ fun Context.getOTGItems(path: String, shouldShowHidden: Boolean, getProperFileSi
|
|||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean, getProperFileSize: Boolean, callback: (ArrayList<FileDirItem>) -> Unit) {
|
||||
fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean, getProperFileSize: Boolean = true, callback: (ArrayList<FileDirItem>) -> Unit) {
|
||||
val items = ArrayList<FileDirItem>()
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val relativePath = path.substring(baseConfig.internalStoragePath.length).trim('/')
|
||||
val documentId = "primary:$relativePath"
|
||||
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
val childrenUri = try {
|
||||
DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId)
|
||||
} catch (e: Exception) {
|
||||
|
@ -499,7 +497,7 @@ fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean,
|
|||
return
|
||||
}
|
||||
|
||||
val projection = arrayOf(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE)
|
||||
val projection = arrayOf(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE, Document.COLUMN_LAST_MODIFIED)
|
||||
val rawCursor = contentResolver.query(childrenUri, projection, null, null)!!
|
||||
val cursor = ExternalStorageProviderHack.transformQueryResult(childrenUri, rawCursor)
|
||||
cursor.use {
|
||||
|
@ -508,6 +506,7 @@ fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean,
|
|||
val docId = cursor.getStringValue(Document.COLUMN_DOCUMENT_ID)
|
||||
val name = cursor.getStringValue(Document.COLUMN_DISPLAY_NAME)
|
||||
val mimeType = cursor.getStringValue(Document.COLUMN_MIME_TYPE)
|
||||
val lastModified = cursor.getLongValue(Document.COLUMN_LAST_MODIFIED)
|
||||
val isDirectory = mimeType == Document.MIME_TYPE_DIR
|
||||
val filePath = docId.substring("primary:".length)
|
||||
if (!shouldShowHidden && name.startsWith(".")) {
|
||||
|
@ -522,12 +521,11 @@ fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean,
|
|||
}
|
||||
|
||||
val childrenCount = if (isDirectory) {
|
||||
getChildrenCount(treeUri, docId, shouldShowHidden)
|
||||
getDirectChildrenCount(treeUri, docId, shouldShowHidden)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val lastModified = System.currentTimeMillis()
|
||||
val fileDirItem = FileDirItem(decodedPath, name, isDirectory, childrenCount, fileSize, lastModified)
|
||||
items.add(fileDirItem)
|
||||
} while (cursor.moveToNext())
|
||||
|
@ -536,7 +534,7 @@ fun Context.getStorageItemsWithTreeUri(path: String, shouldShowHidden: Boolean,
|
|||
callback(items)
|
||||
}
|
||||
|
||||
fun Context.getChildrenCount(treeUri: Uri, documentId: String, shouldShowHidden: Boolean): Int {
|
||||
fun Context.getDirectChildrenCount(treeUri: Uri, documentId: String, shouldShowHidden: Boolean): Int {
|
||||
val projection = arrayOf(Document.COLUMN_DOCUMENT_ID)
|
||||
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId)
|
||||
val rawCursor = contentResolver.query(childrenUri, projection, null, null, null)!!
|
||||
|
@ -544,55 +542,152 @@ fun Context.getChildrenCount(treeUri: Uri, documentId: String, shouldShowHidden:
|
|||
return if (shouldShowHidden) {
|
||||
cursor.count
|
||||
} else {
|
||||
val children = mutableListOf<String>()
|
||||
var count = 0
|
||||
cursor.use {
|
||||
while (cursor.moveToNext()) {
|
||||
children.add(cursor.getStringValue(Document.COLUMN_DOCUMENT_ID))
|
||||
val docId = cursor.getStringValue(Document.COLUMN_DOCUMENT_ID)
|
||||
if (!docId.getFilenameFromPath().startsWith('.') || shouldShowHidden) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
children.filter { !it.getFilenameFromPath().startsWith(".") }.size
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getProperChildrenCount(treeUri: Uri, documentId: String, shouldShowHidden: Boolean): Int {
|
||||
val projection = arrayOf(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE)
|
||||
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId)
|
||||
val rawCursor = contentResolver.query(childrenUri, projection, null, null, null)!!
|
||||
val cursor = ExternalStorageProviderHack.transformQueryResult(childrenUri, rawCursor)
|
||||
return if (cursor.count > 0) {
|
||||
var count = 0
|
||||
cursor.use {
|
||||
while (cursor.moveToNext()) {
|
||||
val docId = cursor.getStringValue(Document.COLUMN_DOCUMENT_ID)
|
||||
val mimeType = cursor.getStringValue(Document.COLUMN_MIME_TYPE)
|
||||
if (mimeType == Document.MIME_TYPE_DIR) {
|
||||
count++
|
||||
count += getProperChildrenCount(treeUri, docId, shouldShowHidden)
|
||||
} else if (!docId.getFilenameFromPath().startsWith('.') || shouldShowHidden) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getFileSize(treeUri: Uri, documentId: String): Long {
|
||||
val projection = arrayOf(Document.COLUMN_SIZE)
|
||||
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId)
|
||||
val rawCursor = contentResolver.query(childrenUri, projection, null, null, null)!!
|
||||
val cursor = ExternalStorageProviderHack.transformQueryResult(childrenUri, rawCursor)
|
||||
var size = 0L
|
||||
cursor.use { c ->
|
||||
if (c.moveToFirst()) {
|
||||
size = c.getLongValue(Document.COLUMN_SIZE)
|
||||
}
|
||||
val documentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
return contentResolver.query(documentUri, projection, null, null, null)?.use { cursor ->
|
||||
if (cursor.moveToFirst()) cursor.getLongValue(Document.COLUMN_SIZE) else 0L
|
||||
} ?: 0L
|
||||
}
|
||||
|
||||
fun Context.getPrimaryAndroidSAFDocumentId(path: String): String {
|
||||
val relativePath = path.substring(baseConfig.internalStoragePath.length).trim('/')
|
||||
return "primary:$relativePath"
|
||||
}
|
||||
|
||||
fun Context.getPrimaryAndroidSAFUri(path: String): Uri {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
return DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
}
|
||||
|
||||
fun Context.getPrimaryAndroidSAFDocument(path: String): DocumentFile? {
|
||||
val primaryAndroidPath = File(internalStoragePath, "Android").path
|
||||
var relativePath = path.substring(primaryAndroidPath.length)
|
||||
if (relativePath.startsWith(File.separator)) {
|
||||
relativePath = relativePath.substring(1)
|
||||
}
|
||||
return size
|
||||
|
||||
return try {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
var document = DocumentFile.fromTreeUri(applicationContext, treeUri)
|
||||
val files = document?.listFiles()?.map { it.name }
|
||||
val parts = relativePath.split("/").filter { it.isNotEmpty() }
|
||||
for (part in parts) {
|
||||
document = document?.findFile(part)
|
||||
}
|
||||
document
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getSomePrimaryAndroidSAFDocument(path: String): DocumentFile? = getFastPrimaryAndroidSAFDocument(path) ?: getPrimaryAndroidSAFDocument(path)
|
||||
|
||||
fun Context.getFastPrimaryAndroidSAFDocument(path: String): DocumentFile? {
|
||||
val uri = getPrimaryAndroidSAFUri(path)
|
||||
return DocumentFile.fromSingleUri(this, uri)
|
||||
}
|
||||
|
||||
fun Context.getPrimaryAndroidSAFChildrenUri(path: String): Uri {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
return DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId)
|
||||
}
|
||||
|
||||
fun Context.createSAFOnlyDirectory(path: String): Boolean {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val relativePath = path.getParentPath().substring(baseConfig.internalStoragePath.length).trim('/')
|
||||
val documentId = "primary:$relativePath"
|
||||
val parentPath = path.getParentPath()
|
||||
if (!getDoesFilePathExist(parentPath)) {
|
||||
createSAFOnlyDirectory(parentPath)
|
||||
}
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(parentPath)
|
||||
val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
return DocumentsContract.createDocument(contentResolver, parentUri, Document.MIME_TYPE_DIR, path.getFilenameFromPath()) != null
|
||||
val createdUri = DocumentsContract.createDocument(contentResolver, parentUri, Document.MIME_TYPE_DIR, path.getFilenameFromPath())
|
||||
return createdUri != null
|
||||
}
|
||||
|
||||
fun Context.createSAFOnlyFile(path: String): Boolean {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val relativePath = path.getParentPath().substring(baseConfig.internalStoragePath.length).trim('/')
|
||||
val documentId = "primary:$relativePath"
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path.getParentPath())
|
||||
val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
return DocumentsContract.createDocument(contentResolver, parentUri, path.getMimeType(), path.getFilenameFromPath()) != null
|
||||
}
|
||||
|
||||
fun Context.renameSAFOnlyDocument(oldPath: String, newPath: String): Boolean {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val relativePath = oldPath.substring(baseConfig.internalStoragePath.length).trim('/')
|
||||
val documentId = "primary:$relativePath"
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(oldPath)
|
||||
val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
return DocumentsContract.renameDocument(contentResolver, parentUri, newPath.getFilenameFromPath()) != null
|
||||
}
|
||||
|
||||
private const val TAG = "Context-storage"
|
||||
fun Context.getSAFOnlyFileSize(path: String): Long {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
val size = getFileSize(treeUri, documentId)
|
||||
return size
|
||||
}
|
||||
|
||||
fun Context.getSAFOnlyFileCount(path: String, countHidden: Boolean): Int {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
val size = getProperChildrenCount(treeUri, documentId, countHidden)
|
||||
return size
|
||||
}
|
||||
|
||||
fun Context.getSAFOnlyDirectChildrenCount(path: String, countHidden: Boolean): Int {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
return getDirectChildrenCount(treeUri, documentId, countHidden)
|
||||
}
|
||||
|
||||
fun Context.getSAFOnlyLastModified(path: String): Long {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
val documentId = getPrimaryAndroidSAFDocumentId(path)
|
||||
val projection = arrayOf(Document.COLUMN_LAST_MODIFIED)
|
||||
val documentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
|
||||
return contentResolver.query(documentUri, projection, null, null, null)?.use { cursor ->
|
||||
if (cursor.moveToFirst()) cursor.getLongValue(Document.COLUMN_LAST_MODIFIED) else 0L
|
||||
} ?: 0L
|
||||
}
|
||||
|
||||
fun Context.deleteSAFOnlyDir(path: String, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
|
||||
val treeUri = baseConfig.primaryAndroidTreeUri.toUri()
|
||||
|
@ -633,6 +728,9 @@ fun Context.getFileInputStreamSync(path: String): InputStream? {
|
|||
return if (isPathOnOTG(path)) {
|
||||
val fileDocument = getSomeDocumentFile(path)
|
||||
applicationContext.contentResolver.openInputStream(fileDocument?.uri!!)
|
||||
} else if (isRestrictedAndroidDir(path)) {
|
||||
val uri = getPrimaryAndroidSAFUri(path)
|
||||
applicationContext.contentResolver.openInputStream(uri)
|
||||
} else {
|
||||
FileInputStream(File(path))
|
||||
}
|
||||
|
@ -649,7 +747,9 @@ fun Context.updateOTGPathFromPartition() {
|
|||
|
||||
fun Context.getDoesFilePathExist(path: String, otgPathToUse: String? = null): Boolean {
|
||||
val otgPath = otgPathToUse ?: baseConfig.OTGPath
|
||||
return if (otgPath.isNotEmpty() && path.startsWith(otgPath)) {
|
||||
return if (isRestrictedAndroidDir(path)) {
|
||||
getFastPrimaryAndroidSAFDocument(path)?.exists() ?: false
|
||||
} else if (otgPath.isNotEmpty() && path.startsWith(otgPath)) {
|
||||
getOTGFastDocumentFile(path)?.exists() ?: false
|
||||
} else {
|
||||
File(path).exists()
|
||||
|
@ -657,7 +757,9 @@ fun Context.getDoesFilePathExist(path: String, otgPathToUse: String? = null): Bo
|
|||
}
|
||||
|
||||
fun Context.getIsPathDirectory(path: String): Boolean {
|
||||
return if (isPathOnOTG(path)) {
|
||||
return if (isRestrictedAndroidDir(path)) {
|
||||
getFastPrimaryAndroidSAFDocument(path)?.isDirectory ?: false
|
||||
} else if (isPathOnOTG(path)) {
|
||||
getOTGFastDocumentFile(path)?.isDirectory ?: false
|
||||
} else {
|
||||
File(path).isDirectory
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.content.pm.PackageManager
|
|||
import android.content.pm.ShortcutManager
|
||||
import android.content.res.Configuration
|
||||
import android.database.Cursor
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.Point
|
||||
import android.media.MediaMetadataRetriever
|
||||
|
@ -383,7 +384,9 @@ fun Context.getMimeTypeFromUri(uri: Uri): String {
|
|||
}
|
||||
|
||||
fun Context.ensurePublicUri(path: String, applicationId: String): Uri? {
|
||||
return if (isPathOnOTG(path)) {
|
||||
return if (isRestrictedAndroidDir(path)) {
|
||||
getPrimaryAndroidSAFUri(path)
|
||||
} else if (isPathOnOTG(path)) {
|
||||
getDocumentFile(path)?.uri
|
||||
} else {
|
||||
val uri = Uri.parse(path)
|
||||
|
@ -743,7 +746,7 @@ fun Context.getTimeFormat() = if (baseConfig.use24HourFormat) TIME_FORMAT_24 els
|
|||
|
||||
fun Context.getResolution(path: String): Point? {
|
||||
return if (path.isImageFast() || path.isImageSlow()) {
|
||||
path.getImageResolution()
|
||||
getImageResolution(path)
|
||||
} else if (path.isVideoFast() || path.isVideoSlow()) {
|
||||
getVideoResolution(path)
|
||||
} else {
|
||||
|
@ -751,10 +754,31 @@ fun Context.getResolution(path: String): Point? {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.getImageResolution(path: String): Point? {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
if (isRestrictedAndroidDir(path)) {
|
||||
BitmapFactory.decodeStream(contentResolver.openInputStream(getPrimaryAndroidSAFUri(path)), null, options)
|
||||
} else {
|
||||
BitmapFactory.decodeFile(path, options)
|
||||
}
|
||||
val width = options.outWidth
|
||||
val height = options.outHeight
|
||||
return if (width > 0 && height > 0) {
|
||||
Point(options.outWidth, options.outHeight)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getVideoResolution(path: String): Point? {
|
||||
var point = try {
|
||||
val retriever = MediaMetadataRetriever()
|
||||
retriever.setDataSource(path)
|
||||
if (isRestrictedAndroidDir(path)) {
|
||||
retriever.setDataSource(this, getPrimaryAndroidSAFUri(path))
|
||||
} else {
|
||||
retriever.setDataSource(path)
|
||||
}
|
||||
val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
|
||||
val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
|
||||
Point(width, height)
|
||||
|
|
|
@ -76,7 +76,7 @@ private fun getDirectoryFileCount(dir: File, countHiddenItems: Boolean): Int {
|
|||
return count
|
||||
}
|
||||
|
||||
fun File.getDirectChildrenCount(countHiddenItems: Boolean) = listFiles()?.filter { if (countHiddenItems) true else !it.name.startsWith('.') }?.size
|
||||
fun File.getDirectChildrenCount(context: Context, countHiddenItems: Boolean) = if(context.isSAFOnlyRoot(path)) context.getSAFOnlyDirectChildrenCount(path, countHiddenItems) else listFiles()?.filter { if (countHiddenItems) true else !it.name.startsWith('.') }?.size
|
||||
?: 0
|
||||
|
||||
fun File.toFileDirItem(context: Context) = FileDirItem(absolutePath, name, context.getIsPathDirectory(absolutePath), 0, length(), lastModified())
|
||||
|
@ -128,17 +128,7 @@ fun File.doesParentHaveNoMedia(): Boolean {
|
|||
}
|
||||
|
||||
fun File.getDigest(algorithm: String): String {
|
||||
return inputStream().use { fis ->
|
||||
val md = MessageDigest.getInstance(algorithm)
|
||||
val buffer = ByteArray(8192)
|
||||
generateSequence {
|
||||
when (val bytesRead = fis.read(buffer)) {
|
||||
-1 -> null
|
||||
else -> bytesRead
|
||||
}
|
||||
}.forEach { bytesRead -> md.update(buffer, 0, bytesRead) }
|
||||
md.digest().joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
return inputStream().getDigest(algorithm)
|
||||
}
|
||||
|
||||
fun File.md5(): String = this.getDigest(MD5)
|
||||
|
|
|
@ -71,7 +71,7 @@ fun String.isImageFast() = photoExtensions.any { endsWith(it, true) }
|
|||
fun String.isAudioFast() = audioExtensions.any { endsWith(it, true) }
|
||||
fun String.isRawFast() = rawExtensions.any { endsWith(it, true) }
|
||||
|
||||
fun String.isImageSlow() = isImageFast() || getMimeType().startsWith("image") || startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())
|
||||
fun String. isImageSlow() = isImageFast() || getMimeType().startsWith("image") || startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())
|
||||
fun String.isVideoSlow() = isVideoFast() || getMimeType().startsWith("video") || startsWith(MediaStore.Video.Media.EXTERNAL_CONTENT_URI.toString())
|
||||
fun String.isAudioSlow() = isAudioFast() || getMimeType().startsWith("audio") || startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())
|
||||
|
||||
|
@ -94,16 +94,21 @@ fun String.getGenericMimeType(): String {
|
|||
}
|
||||
|
||||
fun String.getParentPath() = removeSuffix("/${getFilenameFromPath()}")
|
||||
fun String.relativizeWith(path: String) = this.substring(path.length)
|
||||
|
||||
fun String.containsNoMedia() = File(this).containsNoMedia()
|
||||
|
||||
fun String.doesThisOrParentHaveNoMedia(folderNoMediaStatuses: HashMap<String, Boolean>, callback: ((path: String, hasNoMedia: Boolean) -> Unit)?) =
|
||||
File(this).doesThisOrParentHaveNoMedia(folderNoMediaStatuses, callback)
|
||||
|
||||
fun String.getImageResolution(): Point? {
|
||||
fun String.getImageResolution(context: Context): Point? {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeFile(this, options)
|
||||
if(context.isRestrictedAndroidDir(this)){
|
||||
BitmapFactory.decodeStream(context.contentResolver.openInputStream(context.getPrimaryAndroidSAFUri(this)), null, options)
|
||||
}else{
|
||||
BitmapFactory.decodeFile(this, options)
|
||||
}
|
||||
val width = options.outWidth
|
||||
val height = options.outHeight
|
||||
return if (width > 0 && height > 0) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.simplemobiletools.commons.models
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
|
@ -7,7 +8,14 @@ import com.simplemobiletools.commons.extensions.*
|
|||
import com.simplemobiletools.commons.helpers.*
|
||||
import java.io.File
|
||||
|
||||
open class FileDirItem(val path: String, val name: String = "", var isDirectory: Boolean = false, var children: Int = 0, var size: Long = 0L, var modified: Long = 0L) :
|
||||
open class FileDirItem(
|
||||
val path: String,
|
||||
val name: String = "",
|
||||
var isDirectory: Boolean = false,
|
||||
var children: Int = 0,
|
||||
var size: Long = 0L,
|
||||
var modified: Long = 0L
|
||||
) :
|
||||
Comparable<FileDirItem> {
|
||||
companion object {
|
||||
var sorting = 0
|
||||
|
@ -72,24 +80,42 @@ open class FileDirItem(val path: String, val name: String = "", var isDirectory:
|
|||
} catch (e: Exception) {
|
||||
context.getSizeFromContentUri(Uri.parse(path))
|
||||
}
|
||||
} else if (context.isRestrictedAndroidDir(path)) {
|
||||
context.getSAFOnlyFileSize(path)
|
||||
} else {
|
||||
File(path).getProperSize(countHidden)
|
||||
}
|
||||
}
|
||||
|
||||
fun getProperFileCount(context: Context, countHidden: Boolean): Int {
|
||||
return if (context.isPathOnOTG(path)) {
|
||||
context.getDocumentFile(path)?.getFileCount(countHidden) ?: 0
|
||||
} else {
|
||||
File(path).getFileCount(countHidden)
|
||||
return when {
|
||||
context.isPathOnOTG(path) -> {
|
||||
context.getDocumentFile(path)?.getFileCount(countHidden) ?: 0
|
||||
}
|
||||
context.isRestrictedAndroidDir(path) -> {
|
||||
context.getSAFOnlyFileCount(path, countHidden)
|
||||
}
|
||||
else -> {
|
||||
File(path).getFileCount(countHidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getDirectChildrenCount(context: Context, countHiddenItems: Boolean): Int {
|
||||
return if (context.isPathOnOTG(path)) {
|
||||
context.getDocumentFile(path)?.listFiles()?.filter { if (countHiddenItems) true else !it.name!!.startsWith(".") }?.size ?: 0
|
||||
} else {
|
||||
File(path).getDirectChildrenCount(countHiddenItems)
|
||||
return when {
|
||||
context.isPathOnOTG(path) -> {
|
||||
context.getDocumentFile(path)?.listFiles()?.filter { if (countHiddenItems) true else !it.name!!.startsWith(".") }?.size ?: 0
|
||||
}
|
||||
context.isRestrictedAndroidDir(path) -> {
|
||||
try {
|
||||
context.getSAFOnlyDirectChildrenCount(path, countHiddenItems)
|
||||
} catch (e: Exception) {
|
||||
0
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
File(path).getDirectChildrenCount(context, countHiddenItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,6 +124,8 @@ open class FileDirItem(val path: String, val name: String = "", var isDirectory:
|
|||
context.getFastDocumentFile(path)?.lastModified() ?: 0L
|
||||
} else if (isNougatPlus() && path.startsWith("content://")) {
|
||||
context.getMediaStoreLastModified(path)
|
||||
} else if (context.isRestrictedAndroidDir(path)) {
|
||||
context.getSAFOnlyLastModified(path)
|
||||
} else {
|
||||
File(path).lastModified()
|
||||
}
|
||||
|
@ -119,7 +147,7 @@ open class FileDirItem(val path: String, val name: String = "", var isDirectory:
|
|||
|
||||
fun getVideoResolution(context: Context) = context.getVideoResolution(path)
|
||||
|
||||
fun getImageResolution() = path.getImageResolution()
|
||||
fun getImageResolution(context: Context) = context.getImageResolution(path)
|
||||
|
||||
fun getPublicUri(context: Context) = context.getDocumentFile(path)?.uri ?: ""
|
||||
|
||||
|
|
Loading…
Reference in a new issue