0

I am able to store an image and display it ImageView using the same filePath but get an error when trying to access it for thumbnail creation using ThumbnailUtils

I have a fragment, That captures the image using ActivityResultContract

class TakePictureWithUriReturnContract : ActivityResultContract<Uri, Pair<Boolean, Uri>>() {
    private lateinit var imageUri: Uri

    @CallSuper
    override fun createIntent(context: Context, input: Uri): Intent {
        imageUri = input
        return Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, input)
    }

    override fun getSynchronousResult(
        context: Context,
        input: Uri
    ): SynchronousResult<Pair<Boolean, Uri>>? = null

    @Suppress("AutoBoxing")
    override fun parseResult(resultCode: Int, intent: Intent?): Pair<Boolean, Uri> {
        return (resultCode == Activity.RESULT_OK) to imageUri
    }
}

The above mentioned code is used to handle the contract

val photoFile = File.createTempFile(
            "IMG_",
            ".jpg",
            requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        )

        val uri = FileProvider.getUriForFile(
            requireContext(),
            "${requireContext().packageName}.fileprovider",
            photoFile
        )

        sampleActivityRes.launch(uri)

This method is used to launch the Contract and I am able to get the URI successfully.

I am also able to view the image by setting it to ImageView.

But when I try to access it for thumbnail creation using

private fun generateImageThumbnail(imagePath: String): Bitmap {
        val THUMB_SIZE = 64

        val cancellationToken = CancellationSignal()


        return ThumbnailUtils.createImageThumbnail(
            File(imagePath),
            Size(THUMB_SIZE, THUMB_SIZE),
            cancellationToken
        )
    }

I get java.io.FileNotFoundException exception.

My file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="my_images"
        path="Android/data/com.example.name/files/Pictures" />
    <external-path
        name="external"
        path="." />
    <external-files-path
        name="external_files"
        path="." />
    <cache-path
        name="cache"
        path="." />
    <external-cache-path
        name="external_cache"
        path="." />
    <files-path
        name="files"
        path="." />
    <files-path
        name="images"
        path="." />
</paths>

Android Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-feature android:name="android.hardware.camera.any" />
    <uses-feature android:name="android.hardware.location.gps" />
    <uses-feature android:name="android.hardware.location.network" />

    <application
        android:name=".TripMemoryApp"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:grantUriPermissions="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.TripMemory"
        tools:targetApi="31">

        <provider
            android:name="com.android.tripmemory.utils.MyFileProvider"
            android:authorities="com.android.tripmemory.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        <!--
             TODO: Before you run your application, you need a Google Maps API key.

             To get one, follow the directions here:

                https://developers.google.com/maps/documentation/android-sdk/get-api-key

             Once you have your API key (it starts with "AIza"), define a new property in your
             project's local.properties file (e.g. MAPS_API_KEY=Aiza...), and replace the
             "YOUR_API_KEY" string in this file with "${MAPS_API_KEY}".
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="AIzaSyAriNq0SqdYMLgBkShF7E0xaPrGpMf4YZ8" />

        <activity
            android:name=".ui.MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

sampleResActivity

val sampleActivityRes = registerForActivityResult(
        TakePictureWithUriReturnContract()
    ) { (isSuccess, imageURI) ->
        if (isSuccess) {
            Log.d(LOG_TAG, "Image URI: $imageURI")
            binding.sampleImage.bringToFront()
            binding.sampleImage.setImageURI(imageURI)

            navigateToImagePreview(
                imageURI = imageURI.toString(),
                pathId = viewModel.pathInstance.id
            )
        }
    }
Sanjay Kapilesh
  • 269
  • 1
  • 4
  • 16
  • 1
    Note that this approach of using a `lateinit var imageUri: Uri` is going to fail on many, many devices due to low memory (as the Camera app is often enough to push every other app out of memory) or any device if the user rotates their device while the camera is up (thus causing your activity to go through a config change, destroying your contract instance). That's why `ActivityResultContracts.TakePicture` is written as it is and why [the docs](https://developer.android.com/training/basics/intents/result#launch) specifically mention saving that additional state separately. – ianhanniballake Nov 28 '22 at 04:59
  • 1
    But the real question is: why are you using the `Uri` you send to the Camera app and not the `File` that you already have access to? Going through the Uri is never going to work with `File` based APIs – ianhanniballake Nov 28 '22 at 04:59
  • 1
    `sampleActivityRes.launch(uri)` You did not show how you declared sampleActivityRes. Further we dont know what you used as imagePath.. – blackapps Nov 28 '22 at 06:01
  • 1
    `also able to view the image by setting it to ImageView.` Unclear what you did. Did you use the uri? The file path? Or what? The image? How? – blackapps Nov 28 '22 at 06:03
  • 1
    `file_paths.xml` It is unclear why you post all your FileProvider stuff as you do not use it at generating a thumbnail. Well not that i see you do. – blackapps Nov 28 '22 at 06:06
  • @Ianhanniballake What do you mean by "Going through Uri is never going to work with `File` based APIs " – Sanjay Kapilesh Nov 28 '22 at 11:41
  • @blackapps added `sampleRes` to the question Also I posted `file_paths.xml` because it is used in `createTempFile` – Sanjay Kapilesh Nov 28 '22 at 11:44
  • `imagePath` is the uri from the `ActivityResultContract` converted to string @blackapps – Sanjay Kapilesh Nov 28 '22 at 11:48
  • 1
    That is not a path but a scheme. So File(imagePath) would be File("content://.....")? Well that will not go. You cannot use a content scheme with the File class. You should use photoFile instead. – blackapps Nov 28 '22 at 11:58
  • @blackapps thanks, if File("content://...") is a schema, What would be the URI for the file. – Sanjay Kapilesh Nov 28 '22 at 13:24
  • 1
    You do not need an Uri to begin with. `if File("content://...") is a schema,` No. "content://...." is a content scheme. Which cannot be used for the File class as the File class expects a file path. Wrong question. I already told you to replace `File(imagePath),` by `photoFile,`. ( the one from val photoFile = File.crea.....). – blackapps Nov 28 '22 at 13:28
  • @blackapps thanks, But I am actually passing the URI in fragment directions as you can see on sampleResActivity, I am unable to pass the entire `File` object in directions, I am hoping it will work if I pass the absolute path of the file – Sanjay Kapilesh Nov 28 '22 at 13:43
  • 1
    Yes then it will work. And you have that path in photoFile like photoFile.getAbsolutePath(). – blackapps Nov 28 '22 at 13:44
  • @blackapps and it worked , Thanks again for helping. I am pretty new to android. Thanks for helping me to work it out – Sanjay Kapilesh Nov 28 '22 at 13:45
  • Reread the second comment of @ianhanniballake as already there it was told you to use your file path. – blackapps Nov 28 '22 at 13:49

1 Answers1

0

Thanks to @blackapps

The issue was that the imagePath in generateImageThumbnail was a Schema instead of being an URI/ path.

It would work if we pass the absolutePath of the file

so the change would be calling generateImageThumbnail(photoFile.absolutePath) with the absolute path of the File

val photoFile = File.createTempFile(
            "IMG_",
            ".jpg",
            requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        )

generateImageThumbnail(photoFile.absolutePath)
Sanjay Kapilesh
  • 269
  • 1
  • 4
  • 16