As I understood from countless hours of trying to solve the issue, the activity which executes this method (RemoteViews.setImageViewUri()) is the "home screen" activity, and it has no permissions for reading arbitrary files. But my application (widget) has them (), and therefore can read and display images from system gallery (in MainActivity or in "configuration screen" of the widget). And there is no Intent to which you can give any permission flag (to allow the use of Uri or storage read).
So is it possible to display images (from external storage) in the widget at all? I mean, the ImageView is allowed in RemoteViews for something, right?
From https://developer.android.com/reference/android/widget/RemoteViews.html:
RemoteViewsis limited to support for the following layouts:
AdapterViewFlipperFrameLayoutGridLayoutGridViewLinearLayoutListViewRelativeLayoutStackViewViewFlipperAnd the following widgets:
AnalogClockButtonChronometerImageButtonImageViewProgressBarTextClockTextViewAs of API 31, the following widgets and layouts may also be used:
CheckBoxRadioButtonRadioGroupSwitchDescendants of these classes are not supported.
As always, the answer is simple, but the lack of knowledge makes it that much harder. The "Image Uri" that "RemoteViews.setImageViewUri()" is expecting is the non-
FileUriExposedExceptionversion ofUri. Now I can tell the story how I find out what is the "non-FileUriExposedException" version ofUri.Initially, I tried to pass just an absolute path as a
Uri(I knew that I was using abs. path, but then suddenly forgot this):But this didn't work. Then I prepended
"content://"string to the return value — still no good. In the process I was able to have a small experience usingGlideandPicassolibraries. I found a solution like this:The difference is that
"content://"has changed to"file://"or we can useFile()constructor instead. We can also overwrite what goes into.into()method (heh) in such a way that we can overwrite methods that are responsible for error handling etc. (this is just Picasso things). This does work, but probably a bad way to go about the issue (because file'sUriis still kind of exposed, but I'm not sure).And it was a pretty clean solution, but I had to add +1 implementation dependency into
build.gradle(:app).Finally, I saw Uri similar to this one:
content://$provider/files/#. And I also saw that I can actually overrideopenFile(uri: Uri, mode: String): ParcelFileDescriptor?method inContentProvider's child class of mine (apparentlyContentProviderdoes not require an overridden implementation of this method, but I can still do that). Basically, this is the method that should be called when "home screen" activity tries to get the imageUri. This method takes in previously mentioned Uri (content://$provider/files/#), but of course the part after content provider can be anything you like, even the absolute path of the image. And this method does not expose file's real URI (file:///path/to/image.png), that is why everything started to work as it should be. Now the function looks like this:and
get_next_image_uri(Context):and the
update()is called fromonUpdate():So, there are 2 times, when something has to be fetched by using a
Uri:context.contentResolver.query(content_provider_uri, null, null, null, null);views.setImageViewUri(R.id.imageView, image_uri)Both
Uris are handled by the sameclass GalleryImagePathProvider : ContentProvider() {}. In the first case it is handled through theoverride fun query()method, and in the second case — through theoverride fun openFile()method. Now, in order for everything to work properly, we need to add some "magic" to theAndroidManifest.xml:The key point here is
android:exported="true", without it "home screen" activity can't display images (at least in my case). (Permission to read external storage is used when accessing system gallery's images.)That's all, I think I went over all the important things and problems that I had to solve to achieve the goal.
P.S. I don't want to make a whole promotion, but (at this point I kinda have to) when I posted this question, I got the
notification and when I got to the questions about AI tools/language models, I found quite a big list of bots or search engines. I then laid my eyes on
You.comtext. And almost instantly (free and \wo any limits), I finally got to know how it feels when you have a "companion" that tries hard to solve all your coding questions and problems. And thanks to YouBot, I was able to crunch down a whole a lot of time of debugging by simply listening to the advices and suggestions from it. So, as a fellow coder, I can recommend it. (But sometime prompt input lag becomes really big, and after some time in standby mode you have to refresh the tab for it to be able to work properly again. Everything else is super nice and cool.)