Search code examples
androidandroid-widgetandroid-fileprovider

setImageViewUri in widget from Room


I have an app that allows the users to take images. I then store the image in a local file and save the URI to Room. I then have a widget associated with the app that has an image view. I inject my database into the widget using Dagger-Hilt and successfully pass that URI to my updateAppWidget method

The URI in question:

file:///storage/emulated/0/Android/media/com.sudharsanravikumar.myapplication/AlbumExpo/2021-11-08-07-37-55-596.jpg

the problem is that the app crashes with the following error:

java.lang.IllegalArgumentException: Failed to find configured root that contains /file:/storage/emulated/0/Android/media/com.sudharsanravikumar.myapplication/AlbumExpo/2021-11-08-07-37-55-596.jpg

I have tried many things on similar SO questions, but it feels like I am missing some important information. I think I just don't clearly understand how to use File provider tags on my manifest to correctly point the app to the directory where the app stores my images. I am not sure what to show in terms of code so if you need anything please ask in the comments.

Originally my error was:

    android.os.FileUriExposedException: file:///storage/emulated/0/Android/media/com.sudharsanravikumar.myapplication/AlbumExpo/2021-11-08-07-37-55-596.jpg exposed beyond app through RemoteViews.setUri()

which was generated when I directly called

 views.setImageViewUri(R.id.img_1, Uri.parse(uri.uri))

with my original uri from above. To attempt to solve this issue I followed the accepted answer in this So question

that lead me to do:

val photoURI = FileProvider.getUriForFile(
    context,
    "com.sudharsanravikumar.myapplication.provider",
    File(uri.uri)
)
 views.setImageViewUri(R.id.img_1, photoURI)

I am assuming that the process of creating a file from my original uri then re-transforming it to a uri using the file provider makes it's path mutate to what the error says.


Solution

  • val photoURI = FileProvider.getUriForFile(
        context,
        "com.sudharsanravikumar.myapplication.provider",
        File(uri.uri)
    )
    

    uri.uri appears to be a String, with a value of: file:///storage/emulated/0/Android/media/com.sudharsanravikumar.myapplication/AlbumExpo/2021-11-08-07-37-55-596.jpg.

    This is a string representation of a Uri, one with the file scheme. It is not a filesystem path. You cannot pass it to the File constructor and get something useful.

    You seem to have originally gotten your value via getExternalMediaDirs(). You might consider persisting that filesystem path value, rather than converting it into a Uri (via Uri.fromFile()) with a file scheme that you cannot use.

    But, given your current uri.uri value, you would need to change your code to:

    val photoURI = FileProvider.getUriForFile(
        context,
        "com.sudharsanravikumar.myapplication.provider",
        File(Uri.parse(uri.uri).path)
    )
    

    Uri.parse(uri.uri) will turn that String back into a Uri, albeit one with the mostly-useless file scheme. Uri.parse(uri.uri).path will extract the filesystem path (/storage/emulated/0/Android/media/com.sudharsanravikumar.myapplication/AlbumExpo/2021-11-08-07-37-55-596.jpg). And you pass that to the File constructor. Always pass filesystem paths to the File constructor.