failed action_open_document_tree now required
devfemibadmus opened this issue · 1 comments
we using manage external storage instead of other well tried action_open_document_tree
but didn't work https://github.com/devfemibadmus/whatsapp-status-saver#bug-flutter-fileimagevideo-not-working-with-android-action_open_document_tree-but-works-fine-in-kotlin so i skipped but now playstore reject app and said we should use smth else instead not manage_external_storage
if you can fix this please feel free to fork this repo
below is other code i try with but seems i am not getting smth right, now i am leaving to contributors
private fun getStatusFilesInfo(appType: String): List<Map<String, Any>> {
val statusFilesInfo = mutableListOf<Map<String, Any>>()
Log.d(TAG, "statusFilesInfo")
Log.d(TAG, "appType: ${appType}")
val baseDirectoryPath = when (appType) {
"SAVED" -> getSavedStatusesPath()
"WHATSAPP" -> getWhatsAppPath()
"WHATSAPP4B" -> getWhatsApp4BPath()
else -> null
}
/*
baseDirectoryPath?.let { path ->
if (!isPermissionGranted(path)) {
Log.d(TAG, "perission ${path} new")
requestSpecificFolderAccess(Uri.parse(path))
}else{
Log.d(TAG, "perission ${path} granted")
}
STATUS_DIRECTORY = Uri.parse(path)
}
Log.d(TAG, "${baseDirectoryPath}")
Log.d(TAG, "${baseDirectoryPath}")
*/
Log.d(TAG, "baseDirectoryPath: ${baseDirectoryPath}")
Log.d(TAG, "STATUS_DIRECTORY: ${STATUS_DIRECTORY}")
baseDirectoryPath?.let { path ->
val dirFile = File(path)
Log.e(TAG, "dirFile: ${dirFile}")
Log.e(TAG, "dirFile: ${dirFile.exists()}")
if (dirFile.exists() && dirFile.isDirectory && dirFile.listFiles(FileFilter{file ->file.isFile && file.canRead()}) != null) {
Log.e(TAG, "dirFile: ${dirFile}")
Log.e(TAG, "dirFile: ${dirFile.listFiles()}")
val files = dirFile.listFiles(FileFilter{file ->file.isFile && file.canRead()})
files?.forEach { file ->
Log.e(TAG, "file.absolutePath: ${file.absolutePath}")
val fileInfo = mutableMapOf<String, Any>()
fileInfo["name"] = file.name
fileInfo["path"] = file.absolutePath
fileInfo["size"] = file.length()
fileInfo["format"] = getFileFormat(file.name)
fileInfo["source"] = appType
fileInfo["mediaByte"] = ByteArray(0)
statusFilesInfo.add(fileInfo)
}
} else {
Log.e(TAG, "Directory does not exist or is empty")
}
}
/*
STATUS_DIRECTORY?.let { directoryUri ->
Log.d(TAG, "directoryUri ${directoryUri}")
val dirFile = DocumentFile.fromTreeUri(context, directoryUri)
if (dirFile?.exists() == true) {
Log.e(TAG, "dirFile: ${dirFile}")
Log.e(TAG, "dirFile: ${dirFile.exists()}")
val files = dirFile.listFiles()
files?.filter { !it.name.isNullOrBlank() }?.forEach { file ->
val fileInfo = mutableMapOf<String, Any>()
fileInfo["name"] = file.name ?: "UnknownFileName"
fileInfo["path"] = getFilePathFromUri(file.uri).toString() // file.uri.toString()
fileInfo["size"] = file.length()
fileInfo["format"] = getFileFormat(file.name ?: "UnknownFileName")
fileInfo["source"] = appType
fileInfo["mediaByte"] = ByteArray(0)
statusFilesInfo.add(fileInfo)
}
} else {
Log.e(TAG, "Directory does not exist")
}
}
*/
return statusFilesInfo
}
private fun isPermissionGranted(): Boolean {
return try {
val directoryUri = STATUS_DIRECTORY
Log.e(TAG, "STATUS_DIRECTORY: $directoryUri")
if (directoryUri != null) {
val directory = DocumentFile.fromTreeUri(context, directoryUri)
val tempFile = directory?.createFile("text/plain", "temp.txt")
val fileCreated = tempFile != null
// Attempt to delete the temporary file
val fileDeleted = tempFile?.delete() ?: false
Log.e(TAG, "directory exists: ${fileCreated && fileDeleted}")
if(fileCreated && fileDeleted){
true
}
} else {
Log.e(TAG, "STATUS_DIRECTORY is null")
false
}
} catch (e: Exception) {
Log.e(TAG, "Exception occurred: ${e.message}", e)
false
}
}
private fun requestSpecificFolderAccess(callback: (Boolean) -> Unit) {
folderAccessCallback = callback
val baseDirectory = getBaseDirectoryUri()
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// For Android 10 (Q) and above
val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent()
} else {
// For versions below Android 10 (Q)
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
}
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, baseDirectory)
startActivityForResult(intent, PICK_DIRECTORY_REQUEST_CODE)
}
private fun getBaseDirectoryUri(): Uri {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// For Android 10 (Q) and above
Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia/document/primary%3AAndroid%2Fmedia")
} else {
// For versions below Android 10 (Q)
Uri.fromFile(File("/storage/emulated/0/Android/media"))
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if (requestCode == PICK_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val treeUri: Uri? = resultData?.data
treeUri?.let {
contentResolver.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
STATUS_DIRECTORY = it
Log.d(TAG, "Selected directory: $STATUS_DIRECTORY")
folderAccessCallback?.invoke(true)
}
} else {
folderAccessCallback?.invoke(false)
}
}
private fun getSavedStatusesPath(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Status Saver").absolutePath
} else {
File(Environment.getExternalStorageDirectory(), "Pictures/Status Saver").absolutePath
}
}
private fun getWhatsAppPath(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
File(Environment.getExternalStorageDirectory(), "/Android/media/com.whatsapp/WhatsApp/Media/.Statuses").absolutePath
} else {
File(Environment.getExternalStorageDirectory(), "/WhatsApp/Media/.Statuses").absolutePath
}
}
private fun getWhatsApp4BPath(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
File(Environment.getExternalStorageDirectory(), "/Android/media/com.whatsapp.w4b/WhatsApp Business/Media/.Statuses").absolutePath
} else {
File(Environment.getExternalStorageDirectory(), "/WhatsApp Business/Media/.Statuses").absolutePath
}
}
private fun saveStatus(sourceFilePath: String): String {
val sourceFile = File(sourceFilePath)
return if (sourceFile.exists()) {
try {
val galleryDirectory = File(getSavedStatusesPath())
if (!galleryDirectory.exists()) {
galleryDirectory.mkdirs()
}
val originalFileName = sourceFile.name
val originalExtension = getExtension(sourceFile)
val newImageFile = File(galleryDirectory, originalFileName)
// If a file with the same name already exists, return "Already Saved"
if (newImageFile.exists()) {
return "Already Saved"
}
FileInputStream(sourceFile).use { inputStream ->
FileOutputStream(newImageFile).use { outputStream ->
val buffer = ByteArray(4 * 1024)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } >= 0) {
outputStream.write(buffer, 0, bytesRead)
}
}
}
// File saved successfully
"Status Saved"
} catch (e: IOException) {
// e.printStackTrace()
// Error saving file
"Not Saved"
}
} else {
// Source file doesn't exist
"Not Saved"
}
}
private fun deleteStatus(filePath: String): String {
return try {
val uri = MediaStore.Files.getContentUri("external")
val selection = "${MediaStore.Files.FileColumns.DATA} = ?"
val selectionArgs = arrayOf(filePath)
val deletedCount = contentResolver.delete(uri, selection, selectionArgs)
if (deletedCount > 0) {
"Deleted"
} else {
"Not deleted"
}
} catch (e: Exception) {
// Handle any exceptions
"Error: ${e.message}"
}
}
checking https://github.com/pg598595/ScopedStorageDemo i had similar code missing FLAG_GRANT_PERSISTABLE_URI_PERMISSION
i should be able to fix this with below code from above link
class OpenFolderActivity : AppCompatActivity() {
var fileList = ArrayList<DocumentFile>()
val adpater = FileViewAdpater(this, fileList)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_open_folder)
val layoutManager = StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL)
rvTree.layoutManager = layoutManager
}
fun openDocTree(view: View) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
}
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == OPEN_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
fileList.clear()
val directoryUri = data?.data ?: return
contentResolver.takePersistableUriPermission(
directoryUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
loadDirectory(directoryUri)
}
}
fun loadDirectory(directoryUri: Uri) {
val documentsTree = DocumentFile.fromTreeUri(application, directoryUri) ?: return
val childDocuments = documentsTree.listFiles().asList()
for (i in 0 until childDocuments.size) {
Log.i("TAG", "${childDocuments[i].type}")
fileList.add(childDocuments[i])
}
rvTree.adapter = adpater
}
}