mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 14:23:55 +01:00
Issue 5811: folder deletion for api 29.
This commit is contained in:
parent
af649c8082
commit
d03e7bcf70
2 changed files with 62 additions and 29 deletions
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue