Fetching only first 100 rows of image from mediastore - android

Is there a way to limit the result retrieved from mediastore using managedQuery function on Android. Since I currently have a grid that displaying all photos found on the sd card but it is too intensive of fetching it so I decide to limit the result retrieved from the media store but could not find a limit function that can reduce the resulting set of data.
Please help

use order in contentresolver's query method to implement your function,
such as 'columnname asc limit number'
in my case:
cursor = resolver.query(STORAGE_URI, projection,
Media.BUCKET_DISPLAY_NAME + "=?",
new String[] { folderName },
" _id asc limit " + num);

You can limit the result using the sortOrder parameter in query method. Something like this
ContentResolver contentResolver = getContentResolver();
Cursor androidCursor = null;
String sortOrder = String.format("%s limit 100",BaseColumns._ID);
androidCursor = contentResolver.query(IMAGE_URI,PROJECTION, null, null, sortOrder);
This will order the result set by id and limit the result.

When targeting Android 11 the suggested answer will cause an exception, with java.lang.IllegalArgumentException: Invalid token LIMIT
Instead you should provide a "limit" query parameter on the URI that the MediaProvider will read.
val queryUri = IMAGE_URI.buildUpon().appendQueryParameter("limit", limit.toString()).build()
I haven't found documentation for this, but it's present in the sources

Related

Invalid token SELECT

Hi I am trying to get the content of bucket Id using content resolver in android Q I am getting this Error
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #7
Process: com.dev.newtermain, PID: 13048
java.lang.IllegalArgumentException: Invalid token SELECT
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:472)
at android.content.ContentResolver.query(ContentResolver.java:1183)
at android.content.ContentResolver.query(ContentResolver.java:1115)
at android.content.ContentResolver.query(ContentResolver.java:1071)
My Selection query is
selection = "bucket_id = ?) UNION SELECT _data, date_added, 0 as isImage FROM video WHERE (bucket_id = ?";
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
selectionArgs = new String[]{bucketIdString};
String[] projection = new String[]{
MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.DATE_ADDED, MediaStore.Video.VideoColumns.DATE_ADDED
};
Cursor cur = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
Any idea how I can fix this query
The selection parameter in ContentResolver::query does only support WHERE clauses (without the WHERE keyword). docs
Your approach is including the UNION clause in the selection which is invalid. If you need a union, you may have to do two separate queries and combine the two results yourself.
EDIT
For your specifc case the selection should be defined as follows
selection = "bucket_id = ?"
check out this selection
"SELECT _data, date_added, 0 as isImage FROM video WHERE bucket_id = ?";
removed bucket_id = ?) on start - this is just wrong, not proper format, doesn't fit to beginning of query...
removed following UNION as there is no union two selectors at all, just simple query for single video table
at the end removed unneeded opening bracket (WHERE (bucket_id = ? to WHERE bucket_id = ?)
but note that query is encapsulated and won't accept full single-string sqlite query, you have to split it for parts. variable selection would be "bucket_id = ?" for matching selectionArgs
also note that projection is a bit weird...
String[] projection = new String[]{
MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.DATE_ADDED, MediaStore.Video.VideoColumns.DATE_ADDED
};
first two values are using MediaStore.Images instead of MediaStore.Video and all three doesn't match params in selection SELECT _data, date_added, 0 as isImage FROM - I see two params and one probably not needed static value
edit: I've just noticed you are selecting by Images - MediaStore.Images.Media.EXTERNAL_CONTENT_URI - but selection is pointing on video column... also I doubt that bucket_id column exists in MediaStore database, there is no such value in static declarations of columns... I would suggest you read a bit about sql and querying, because your snippet looks like every line is comming from another piece of code....

Android Content Uri Format

I have been retrieving images from MediaStore in the following way...
Uri uriExternal = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {
MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.DATE_ADDED
};
Cursor cursor = getContentResolver()
.query(uriExternal, projection,
MediaStore.MediaColumns.DATA + " IS NOT NULL",
null,
MediaStore.MediaColumns.DATE_ADDED + " DESC");
if(cursor != null) {
while (cursor.moveToNext()) {
String _id = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
paths.add(uriExternal.toString() + "/" + _id);
}
cursor.close();
}
Basically, I'm simply appending the file id to the external content provider uri. This makes a uri that I can use with content providers...
content://media/external/images/media/{id}
It all works perfectly fine, all external images are displayed and loaded flawlessly. However, since I've failed to find proper documentation, I'm a little concerned I'm not doing things the proper way. Especially because of the way I'm constructing the uri...kind of hard-coding it...
The questions are...
Is this the correct way to construct a content uri for an external image?
Is there a more reliable way to achieve this?
Personally, I use ContentUris.withAppendedId(). That way, I don't have to worry about whether I am starting with a Uri that ends in / or not. :-)
In general, MediaStore adheres to the original ContentProvider vision of using the content ID as the last path segment of a Uri pointing to the content. However, that is not a general rule, and it will not work for all providers.

CalendarContract query with limit

How can I query CalendcarContract.Instances with a LIMIT clause?
I would like to query starting with a particular start date for a LIMIT of "n" rows.
What I've tried is:
final Uri uri = Uri.parse(CalendarContract.Instances.CONTENT_URI + "/" +
Long.toString(startDate) + "/" +
Long.MAX_VALUE);
final String sortOrder = Instances.BEGIN;
String selection = " limit " + rows;
Cursor cursor = context.getContentResolver().query (
uri,
projection,
selection,
null,
sortOrder);
This generates an error, reported in the log file:
...while compiling: SELECT Instances._id...WHERE (begin<=? AND end>=? AND (limit 1)...
I believe the error is the "AND" before (limit 1). The service is adding that, not me. So, is there another URI I can use or another technique?
NB: I specifically want the Instances versions, which joins single events with recurring events.
Thanks.
Ok, never mind unless you have a better answer.
I realized this is a more general URI problem, not specifically related to CalendarContract. In searching for other results, I found one suggestion to append LIMIT n to the sort clause, e.g.
final String sortOrder = Instances.BEGIN + " limit " + 10;
Credit to
How to add limit clause using content provider

How to add limit clause using content provider [duplicate]

This question already has answers here:
Specifying limit / offset for ContentProvider queries
(4 answers)
Closed 4 years ago.
Is there a way to limit the number of rows returned from content provider?
I found this solution, however, it did not work for me. All of the rows are still being returned.
Uri uri = Playlists.createIdUri(playlistId); //generates URI
uri = uri.buildUpon().appendQueryParameter("limit", "3").build();
Cursor cursor = activity.managedQuery(playlistUri, null, null, null, null);
I have had this issue and had to break my head till I finally figured it out, or rather got a whay that worked for me. Try the following
Cursor cursor = activity.managedQuery(playlistUri, null, null, null, " ASC "+" LIMIT 2");
The last parameter is for sortOrder. I provided the sort order and also appended the LIMIT to it. Make sure you give the spaces properly. I had to check the query that was being formed and this seemed to work.
Unfortunately, ContentResolver can't query having limit argument. Inside your ContentProvider, your MySQLQueryBuilder can query adding the additional limit parameter.
Following the agenda, we can add an additional URI rule inside ContentProvider.
static final int ELEMENTS_LIMIT = 5;
public static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher( UriMatcher.NO_MATCH );
........
uriMatcher.addURI(AUTHORITY, "elements/limit/#", ELEMENTS_LIMIT);
}
Then in your query method
String limit = null; //default....
switch( uriMatcher.match(uri)){
.......
case ELEMENTS_LIMIT:
limit = uri.getPathSegments().get(2);
break;
......
}
return mySQLBuilder.query( db, projection, selection, selectionArgs, null, null, sortOrder, limit );
Querying ContentProvider from Activity.
uri = Uri.parse("content://" + ContentProvider.AUTHORITY + "/elements/limit/" + 1 );
//In My case I want to sort and get the greatest value in an X column. So having the column sorted and limiting to 1 works.
Cursor query = resolver.query(uri,
new String[]{YOUR_COLUMNS},
null,
null,
(X_COLUMN + " desc") );
A content provider should on general principle pay attention to a limit parameter.
Unfortunately, it is not universally implemented.
For instance, when writing a content provider to handle SearchManager queries:
String limit = uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);
Where it isn't implemented you can only fall back on the ugly option of gluing a limit on the sort clause.

Using the LIMIT statement in a SQLite query

I have a query that selects rows in a ListView without having a limit. But now that I have implemented a SharedPreferences that the user can select how much rows will be displayed in the ListView, my SQLite query doesn't work. I'm passing the argument this way:
return wDb.query(TABELANOME, new String[] {IDTIT, TAREFATIT, SUMARIOTIT}, CONCLUIDOTIT + "=1", null, null, null, null, "LIMIT='" + limite + "'");
The equals (=) operator is not used with the LIMIT clause. Remove it.
Here's an example LIMIT query:
SELECT column FROM table ORDER BY somethingelse LIMIT 5, 10
Or:
SELECT column FROM table ORDER BY somethingelse LIMIT 10
In your case, the correct statement would be:
return wDb.query(TABELANOME, new String[] {IDTIT, TAREFATIT, SUMARIOTIT}, CONCLUIDOTIT + "=1", null, null, null, null, String.valueOf(limite));
Take a look here at the SQLite select syntax: http://www.sqlite.org/syntaxdiagrams.html#select-stmt
This image is rather useful: http://www.sqlite.org/images/syntax/select-stmt.gif
For anyone stumbling across this answer looking for a way to use a LIMIT clause with an OFFSET, I found out from this bug that Android uses the following regex to parse the limit clause of a query:
From <framework/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java>
LIMIT clause is checked with following sLimitPattern.
private static final Pattern sLimitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
Note that the regex does accept the format offsetNumber,limitNumber even though it doesn't accept the OFFSET statement directly.
Due to this bug which also doesn't allow for negative limits
8,-1
I had to use this workaround
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(table);
String query = builder.buildQuery(projection, selection, null, null, null, sortOrder, null);
query+=" LIMIT 8,-1";

Categories

Resources