Android Question Failed to get the path of a video thumbnail using a MediaStore query with SDKVersion >= 30

GeoT

Active Member
Licensed User
Longtime User
Instead of creating a thumbnail of a video saved on my device, I am trying to obtain the path of that thumbnail already created by the system.
This path is saved in the MediaStore Collection.

First, I access the video table and get the ID of a video that I know is on my device by passing it its path.
(I use a query to the SQL library, ContentResolver and the android.provider.MediaStore.Video$Media class)

Then, I try to access the path of the corresponding thumbnail in the video thumbnail table, querying with the obtained Id and comparing it with the Ids saved in that table, in the VIDEO_ID field, and which links the two tables.
(I use a query to the SQL library, ContentResolver and the android.provider.MediaStore.Video.Thumbnails class)

But I only get that route when I use my smartphone with Android 6 (API 23). With my smartphone with Android 11 (API 30) my application does not crash but it does not perform the query.

I guess it has something to do with the new scoped storage starting with Android 11.

I have already stated that if the android SdkVersion < 30, the WRITE_EXTERNAL_STORAGE permission is required, and if it is 30 or greater, the MANAGE_EXTERNAL_STORAGE permission is required. And the way to give permissions with RuntimePermissions and with an intent, respectively.

I also put

B4X:
SetApplicationAttribute(android:requestLegacyExternalStorage, true)

But I still can't get that route with Android 11.

Any ideas?
 
Last edited:

drgottjr

Expert
Licensed User
Longtime User
so, this may or may not help, but check it out.

i have no videos, so i had to use images.
i'm running sdk33 on android14, so if you're not facing certain troubles now, you will soon enough.
there is a reported bug regarding WRITE_EXTERNAL_STORAGE which may have bitten you.
there is a new permission required: READ_MEDIA_VISUAL_USER_SELECTED.

the expected behavior regarding WRITE_EXTERNAL_STORAGE was that it included
READ_EXTERNAL_STORAGE at no extra cost. at a certain point, that ceased to be the case.
there are numerous posts about this on the android bugtracker site. you have to explicitly add
READ_EXTERNAL_STORAGE. it is a runtime permission (aka, dangerous). once i changed
my manifest i was able to list images in mediastore. without the change i got an empty cursor
(not null, empty).

since you're targeting android 11+, you may not have to add READ_MEDIA_VISUAL_USER_SELECTED.
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Hello drgottjr.
Thanks for answering.

Yes, I imagine there will continue to be complications.

On my device with Android 11, I don't have that problem: the system enables READ_EXTERNAL_STORAGE for me when I accept the WRITE_EXTERNAL_STORAGE permission.

I have not yet tried the READ_MEDIA_VISUAL_USER_SELECTED permission but I have tried with READ_MEDIA_IMAGES following the instructions of https://www.b4x.com/android/forum/threads/targetsdkversion-33-and-permissions.148714/#post-942580, although with

B4X:
AddPermission(android.permission.READ_MEDIA_IMAGES)

But it doesn't show me the request for that permission :(
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
so, when you say:
With my smartphone with Android 11 (API 30) my application does not crash but it does not perform the query.
what - exactly - does happen? are you saying "cursor.query()" is a noop? what debugging steps are you employing? (i've seen your other medistore post, so i'm guessing you're using similar code for this project.) if query() is not performed, and the app doesn't crash, where does that leave you? i ask that because your other code references cursor outside of a try/catch block, which would cause a crash if cursor was null, which it would be if query() is not performed. (you wrap cursor in a try/catch block a few steps later, but until that point you would crash if cursor was still null).

if, on the other hand, query() is performed and returns a 0 count, then query() is being performed. that's what was happening in my case: 0 count even though there were qualifying files available. when i changed the manifest, the problem went away. i'm just trying to understand what happens when, as you say, query() is not performed.
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Hi.
When I make the query with Android 11 it always answers thumbCursor.RowCount = 0.
However, with Android 6 it gives me an image path.

And I make sure to change the video path depending on the device.
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
so, the query is being performed and returning no hits, which was what i experienced. what does your manifest look like? and can you post the block where you set up and make the query? i'm assuming you're using code similar to your other post, but it would be good to be sure. i noticed in your other post you ask for the "_data"
column. there is a warning in the documentation that the path specified by that column is actually available. and, can you log the uri before making the query
(uri.toString()) ? is it what you expect it to be?
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Sorry drgottjr.
I'm busy figuring out how this works.
I will post my code later.
It is curious that with Android 6 it only shows me the path of the thumbnail that is in /storage/emulated/0/DCIM/.thumbnails/
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
And browsing my Android 11 device with the manufacturer's file explorer tells me that /storage/emulated/0/DCIM/.thumbnails contains zero elements.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
And browsing my Android 11 device with the manufacturer's file explorer tells me that /storage/emulated/0/DCIM/.thumbnails contains zero elements.
enjoy the access while it lasts. when you get to os 13, you can't do it (permission is ignored). by the way, on my pixel 3a, the thumbnails are in
/Pictures/.thumbnails not /DCIM/.thumbnails. the images are in /DCIM/Camera/. with mediastore you shouldn't have to be concerned with where stuff actually is.

your post was of interest because it rang a bell (the files are plainly there, but cursor count = 0). i know how i resolved it; i'm hopeful i can point you in the right direction.
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Thanks drgottjr.

What permission are you talking about? From WRITE_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE?

It's true! I also have thumbnails in /Pictures/.thumbnails, but they are only for images.

My current battle is with video thumbnails.
I can already capture images from videos, but my idea was to get their routes through MediaStore so I wouldn't have to create them. But I see that it is very difficult and perhaps only the system creates them at runtime.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
if the os doesn't do it, apparently you can. check this:
https://developer.android.com/refer...l#createVideoThumbnail(java.lang.String, int)

i'm having a hard time understanding your goal here (beyond loading thumbnails). does life end for you at android 11? or do you expect what you're doing now to work in android 12 and beyond ('cause it won't)? you're not supposed to know where things are. the os does, and it makes resolvers and providers available ("you want something? you sit here; i'll get it for you").

i can load thumbnails for images using mediastore and not knowing where the thumbnails are. but i'm running on android 14, sdk33. i have it ready to post for you, but it makes no sense based on - apparently - your interest in accessing files directly. i'm going to make a couple videos and see if my code can be pointed to them and their thumbnails. (and, regrettably, almost all the methods involving mediastore and the contentresolver have been deprecated since sdk29.)

i use read external storage and read media images. read external storage is a runtime permission (at least on android 14). i don't use write external storage since i'm not saving anything. in addition, write external storage included read external storage, so you got 2 permissions for the price of 1. but google stopped that, i think, for android 12. if you want read now, you have to ask for read permission separately, and it's a runtime permission. i also use READ_MEDIA_VISUAL_USER_SELECTED, but that's because of android 14.

without testing with some videos, i can't say much more.
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Thanks drgottjr.
Canalrun, Alexander Stolte and other people do something similar to capture video images with:

B4X:
Sub CreateVideoThumbnail(vidfile As String) As Bitmap
    
    Dim rfl As Reflector        'Reflection library
    Dim obj As Object
    Dim b As Bitmap
    
    obj = rfl.CreateObject("android.media.MediaMetadataRetriever")
    rfl.Target = obj
    rfl.RunMethod2("setDataSource", vidfile, "java.lang.String")
    b = rfl.RunMethod3("getFrameAtTime", 0, "java.lang.long", 3, "java.lang.int")
    
    Return b
End Sub

My goal is to make a gallery with Erel's SimpleMediaManager, to be able to send multimedia files just like messaging applications do.

I'm staying on Android 11 because unfortunately it is the latest version I have on my device.
Unfortunately, I don't have Android 14 yet.

If almost all methods involving mediastore and contentresolver have been deprecated since sdk29, I would like to know how to do it.
 
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
I am attaching an unfinished sample that only shows the folders that contain image or video files, and that includes the failed attempt to get the thumbnail paths of the latest videos.
Since SimpleMediaManager doesn't yet read from a list of bitmaps, I need to create an ImageView in the default Panel, in that case.
It displays two columns of images when the device is in a vertical position and five columns when it is in a horizontal position.
 

Attachments

  • MediaStoreShown_3.zip
    15 KB · Views: 126
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i'll look at yours; you can look at mine. i tested on an android 12 device (as close as i could come). see if it gives you any clues. i made a couple videos and downloaded 1 and tried to get their thumbnails. i only had to repoint the code away from Images and to Video.

B4X:
** Activity (main) Create (first time) **
** Activity (main) Resume **
*** Service (thumbnailservice) Create ***
** Service (thumbnailservice) Start **

Uri: content://media/external/video/media/1000000359
thumbnail found. 60x108px
Uri: content://media/external/video/media/1000000360
thumbnail found. 60x108px
Uri: content://media/external/video/media/1000000361
thumbnail found. 108x60px
 

Attachments

  • thumbs.zip
    9.9 KB · Views: 156
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
I have modified your code, and with your permission request and mine, it always returns Thumbnail Path: null
 

Attachments

  • thumbs2.zip
    5.2 KB · Views: 143
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
I'm sorry.
In my example MediaStoreShown_3 you have to put Activity.LoadLayout("Layout") before the call CheckAndRequestPermission.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Of course. I gave it a chance. :)
And it showed me the same as it showed you.
 
Last edited:
Upvote 0

GeoT

Active Member
Licensed User
Longtime User
Java:
static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind, BitmapFactory.Options options)
Return thumbnail representing a specific video item. If a thumbnail doesn't exist, this method will block until it's generated. Callers are responsible for their own in-memory caching of returned values.
It seems to create the thumbnail if it doesn't exist.
But it's not what I'm looking for.

And this method was deprecated in API level 29.
Callers should migrate to using ContentResolver#loadThumbnail, since it offers richer control over requested thumbnail sizes and cancellation behavior.
:rolleyes:
 
Upvote 0
Top