Issue 5811: folder deletion for api 29.

This commit is contained in:
why-lab 2024-11-03 20:55:17 +01:00
parent af649c8082
commit d03e7bcf70
2 changed files with 62 additions and 29 deletions

View file

@ -7,6 +7,8 @@ import android.content.ContentUris
import android.content.Context
import android.content.IntentSender
import android.content.pm.PackageManager
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.appcompat.app.AlertDialog
@ -20,7 +22,7 @@ object FolderDeletionHelper {
* Main function to confirm and delete a folder.
*/
fun confirmAndDeleteFolder(context: Context, folder: File, onDeletionComplete: (Boolean) -> Unit) {
val itemCount = countItemsInFolder(folder)
val itemCount = countItemsInFolder(context, folder)
val folderPath = folder.absolutePath
// Show confirmation dialog
@ -42,7 +44,7 @@ object FolderDeletionHelper {
/**
* Delete the folder based on the Android version.
*/
private fun deleteFolder(context: Context, folder: File): Boolean {
fun deleteFolder(context: Context, folder: File): Boolean {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> deleteFolderScopedStorage(context, folder)
Build.VERSION.SDK_INT == Build.VERSION_CODES.Q -> deleteFolderMediaStore(context, folder)
@ -53,8 +55,16 @@ object FolderDeletionHelper {
/**
* Count the number of items in the folder, including subfolders.
*/
private fun countItemsInFolder(folder: File): Int {
return folder.listFiles()?.size ?: 0
private fun countItemsInFolder(context: Context, folder: File): Int {
val contentResolver = context.contentResolver
val folderPath = folder.absolutePath
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val selection = "${MediaStore.Images.Media.DATA} LIKE ?"
val selectionArgs = arrayOf("$folderPath/%")
return contentResolver.query(uri, arrayOf(MediaStore.Images.Media._ID), selection, selectionArgs, null)?.use { cursor ->
cursor.count
} ?: 0
}
/**
* Deletes a folder using the Scoped Storage API for Android 11 (API level 30) and above.
@ -70,9 +80,7 @@ object FolderDeletionHelper {
}
}
/**
* Deletes a folder using the MediaStore API for Android 10 (API level 29).
*/
/**
* Deletes a folder using the MediaStore API for Android 10 (API level 29).
*/
@ -135,6 +143,18 @@ object FolderDeletionHelper {
Timber.tag("FolderAction").e("RecoverableSecurityException requires API 29 or higher")
}
}
fun refreshMediaStore(context: Context, folder: File) {
MediaScannerConnection.scanFile(
context,
arrayOf(folder.absolutePath),
null
) { path, uri ->
Timber.tag("FolderAction").d("MediaStore updated for path: $path, URI: $uri")
}
}
/**
* Handles deletion for devices running Android 9 (API level 28) and below.
@ -202,6 +222,4 @@ object FolderDeletionHelper {
Timber.tag("PermissionsStatus").d("$permission: $status")
}
}
}
}

View file

@ -54,6 +54,8 @@ import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.SHOULD_REFRESH
import fr.free.nrw.commons.customselector.helper.FolderDeletionHelper
import fr.free.nrw.commons.customselector.helper.FolderDeletionHelper.getFolderPath
import fr.free.nrw.commons.customselector.helper.FolderDeletionHelper.refreshMediaStore
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
import fr.free.nrw.commons.customselector.model.Image
@ -168,6 +170,7 @@ class CustomSelectorActivity :
/**
* onCreate Activity, sets theme, initialises the view model, setup view.
*/
@ -263,17 +266,24 @@ class CustomSelectorActivity :
imageFragment?.passSelectedImages(selectedImages, shouldRefresh)
}
if (requestCode == 2) {
if (requestCode == 2) { // Consistent with handleRecoverableSecurityException
if (resultCode == Activity.RESULT_OK) {
Timber.tag("FolderAction").d("User confirmed deletion")
// Retry deletion or refresh UI if needed
} else {
Timber.tag("FolderAction").e("User denied deletion request")
// Retry deletion for the pending files
val folderPath = FolderDeletionHelper.getFolderPath(this, bucketId)
if (folderPath != null) {
val folder = File(folderPath)
FolderDeletionHelper.deleteFolder(this, folder)
navigateToMainScreen()
} else {
Timber.tag("FolderAction").e("User denied deletion request")
}
}
}
}
/**
* Show Custom Selector Welcome Dialog.
*/
@ -495,7 +505,6 @@ class CustomSelectorActivity :
}
}
/**
* Checks and requests necessary permissions using Dexter library.
*/
@ -558,7 +567,8 @@ class CustomSelectorActivity :
Toast.makeText(this, "Folder deleted", Toast.LENGTH_SHORT).show()
Timber.tag("FolderAction").d("Folder deleted successfully")
reloadFolderList()
// Navigate back to main FolderFragment after deletion
navigateToMainScreen()
} else {
Toast.makeText(this, "Failed to delete folder", Toast.LENGTH_SHORT).show()
Timber.tag("FolderAction").e("Failed to delete folder")
@ -574,30 +584,35 @@ class CustomSelectorActivity :
}
}
private fun reloadFolderList() {
// Clear any saved state for the last open folder
prefs.edit()
.remove(FOLDER_ID)
.remove(FOLDER_NAME)
.apply()
/**
* Navigate back to the main FolderFragment.
*/
private fun navigateToMainScreen() {
val folderPath = getFolderPath(this, bucketId) ?: ""
val folder = File(folderPath)
supportFragmentManager.popBackStack(null, androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE)
supportFragmentManager
.beginTransaction()
// Refresh MediaStore for the deleted folder path to ensure metadata updates
refreshMediaStore(this, folder)
// Replace the current fragment with FolderFragment to go back to the main screen
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, FolderFragment.newInstance())
.commit()
// Reset the toolbar and other flags
.commitAllowingStateLoss() // Ensure this transaction executes, even if the activity state is not fully stable
// Reset toolbar and flags
isImageFragmentOpen = false
showOverflowMenu = false
fetchData()
setUpToolbar()
changeTitle(getString(R.string.custom_selector_title), 0)
// Fetch updated folder data
fetchData()
}
/**
/**
* override on folder click,
* change the toolbar title on folder click, make overflow menu visible
*/