0

I am developing an image vault app that disguises itself as a calculator; when a secret code is entered in the keypad, and the '=' button is pressed, the vault page opens.

I am using the PhotoPicker API to pick the images to move to the vault. The Uris of the selected images is passed to a function to copy the images to the internal storage and then delete them from the external storage.

I am a facing problem in deleting the images using contentResolver.delete(uri), I get this volume picker not found error in the logcact and the image is not deleted from the external storage

Error saving file: Volume picker not found
java.lang.IllegalArgumentException: Volume picker not found
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
        at android.content.ContentProviderProxy.delete(ContentProviderNative.java:629)
        at android.content.ContentResolver.delete(ContentResolver.java:2370)
        at android.content.ContentResolver.delete(ContentResolver.java:2330)
        at com.example.basiccalculator.HiddenPageKt.HiddenPageGalleryView$moveMediaToInternalStorage(HiddenPage.kt:218)
        at com.example.basiccalculator.HiddenPageKt.access$HiddenPageGalleryView$moveMediaToInternalStorage(HiddenPage.kt:1)
        at com.example.basiccalculator.HiddenPageKt$HiddenPageGalleryView$pickMediaLauncher$1.invoke(HiddenPage.kt:234)
        at com.example.basiccalculator.HiddenPageKt$HiddenPageGalleryView$pickMediaLauncher$1.invoke(HiddenPage.kt:229)
        at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1$1.invoke$lambda$0(ActivityResultRegistry.kt:106)
        at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1$1.$r8$lambda$VLs2Oqd6MzDD-LGilzyhNhDSH_4(Unknown Source:0)
        at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1$1$$ExternalSyntheticLambda0.onActivityResult(D8$$SyntheticClass:0)
        at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.kt:371)
        at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.kt:331)
        at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.kt:786)
        at android.app.Activity.dispatchActivityResult(Activity.java:8471)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:5509)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:5555)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2345)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:233)
        at android.os.Looper.loop(Looper.java:344)
        at android.app.ActivityThread.main(ActivityThread.java:8212)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)

here is the complete function snippet:

fun moveMediaToInternalStorage(context: Context, uris: List<Uri>) {
    uris.forEach { uri ->
        Log.d("URI", "$uri")
        try {
            
            val inputStream = context.contentResolver.openInputStream(uri)
                ?: throw IOException("Unable to open input stream for URI: $uri")

            
            val cursor = context.contentResolver.query(uri, null, null, null, null)
            val displayName = cursor?.use {
                if (it.moveToFirst()) it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
                else "file_${System.currentTimeMillis()}"
            } ?: "file_${System.currentTimeMillis()}"

            
            val targetFile = File(context.filesDir, displayName)

            
            context.openFileOutput(displayName, Context.MODE_PRIVATE).use { outputStream ->
                inputStream.use { input ->
                    input.copyTo(outputStream)
                }
            }

            // Delete the original file from external storage after copying
            context.contentResolver.delete(uri, null, null)
            
        } catch (e: Exception) {
            Log.e("InternalStorage", "Error saving file: ${e.message}", e)
        }
    }
}
0

1 Answer 1

0

Starting with Android 14, deleting files from shared media collections like Photos or Downloads requires explicit user consent. This is done using the MediaStore.createDeleteRequest() API, which generates a PendingIntent.

Here’s how you can implement it:

fun deleteImagesWithUserConsent(context: Context, uris: List<Uri>) {
    val contentResolver: ContentResolver = context.contentResolver

    // Create a PendingIntent for deletion
    val pendingIntent: PendingIntent = MediaStore.createDeleteRequest(contentResolver, uris)

    try {
        // Launch the PendingIntent to request user confirmation
        pendingIntent.send()
    } catch (e: Exception) {
        Log.e("DeleteImages", "Error launching delete confirmation: ${e.message}")
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

I want this to be done, without user consent, what I am planning for is to copy the image into the internal storage directory and then delete it from the original location, I have asked for MANAGE_EXTERNAL_STORAGE permission.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.