Using cursor.respond(Bundle) & cursor.getextras() - android

I am having 2 cursors from different tables in an SQLite database. I am trying to put the data from the two cursors into one ListView but for different formatting for data from each cursor.
What I thought about is using a MergeCursor to combine both cursors, but the ViewBinder for my SimpleCursorAdapter will see them as a single cursor and will not be able to differentiate for formatting (unless I alter my tables, which I do not want to do).
Finally, I found 2 methods called Cursor.repond(Bundle) & Cursor.getExtras(), but the documentation on the developer console is very short and Googling these methods did not clarify their use.
I tested my idea to use resond() in my Database class for the query:
extr.putString("table", the_tab);
Cursor c_in = db.rawQuery(qry, null);
c_in.respond(extr);
return c_in;
And use getExtras() in the ViewBinder to know the table of the query and format the ListView item accordingly:
Bundle extr=cur.getExtras();
String tab= extr.getString("table");
But I am always getting an exception that tab is null.
My question after this long description is: Am I using the respond and getExtras methods correctly? And if not, is there a better approach for my problem?

If you want to use the Bundle in getExtras it seems that AbstractCursor which is extended by AbstractWindowedCursor which is extended by SQLiteCursor defines a setExtras method. It's documentation reads
Sets a android.os.Bundle that will be returned by getExtras(). null will be converted into android.os.Bundle.EMPTY.
respond is used for out-of-band communication with the Cursor according to the documentation.
So the answer is:
((AbstractCursor) cursor).setExtras(bundle);
Then you should be able to call
cursor.getExtras();
at a later time to retrieve that Bundle.
EDIT:
Upon looking further, it appears that setExtras is marked hidden for some reason, so it is public, and is intended to be used as described (and desired). See ContactsProvider2 example
So, I came up with using a CursorWrapper, and overriding the getExtras there to provide the Bundle. In my case, I create the Bundle in the CursorWrapper, so I don't need a method for setting it.
#Override
public Bundle getExtras() {
return _meta_data;
}

Try building the table name in as a field in the two SELECTs.
SELECT "A", * from tableA; SELECT "B", * from tableB;
then compute a merged cursor. Alternately,
SELECT "A", * from tableA UNION ALL SELECT "B", * from tableB;
Now each row of the cursor will have a "A" in the first column if it came from tableA and a "B" if it came from tableB. So it's easy to look at this column in your ViewBinder to make formatting decisions.

I would insist you to have a look at MergeCursor and AbstractCursor to get around your problem and get the solution. For example you can check this answer.

Here's an alternative (though you should not use it):
Class.forName("android.database.AbstractCursor")
.getMethod("setExtras", new Class[]{Bundle.class})
.invoke(c_in, extr);

Related

Sorting search results (from Android SearchView query) by number of matches

i'm trying to find a good way to sort the search results according to relevance after performing a search with a SearchView in Android. For me relevance means the number of matches in two SQLite text columns
I'm using a CursorLoader and there the sort order can be given to the constructor at the end
CursorLoader tLoader = new CursorLoader(
getActivity(), ContentProviderM.ARTICLE_CONTENT_URI,
tProj, tSel, tSelArgs, SORT_ORDER);
(or set using the setSortOrder (String sortOrder) method)
But i need more flexibility than this because i'm looking to sort on the number of matches rather than just on one or two columns
The only solution i can see myself is to add another column in my SQLite table, do some processing, and supply that column as the sort column to the CursorLoader
Now for my question: What is the best way to supply the sort order information to the CursorLoader using SQLite syntax, avoiding having to add a new column? (And what could this SQLite code look like?) Also, i'd like to ask more in general: Is there a different solution to this problem that i've missed?
Grateful for any help! And with kind regards,
Tord
Depending on the content provider, if it just pass to the orderBy field, you can do anything.
SQLiteDatabase query
orderBy How to order the rows, formatted as an SQL ORDER BY clause
(excluding the ORDER BY itself). Passing null will use the default
sort order, which may be unordered.
you can do whatever you want, this is just the line after ORDER BY
P.S. It is totally depending on the Content Provider, it it choose to ignore the parameter, you can do nothing.
i found a "workaround" for this problem.
After investigating different ways to write sqlite code i ended up just adding a new table column just for sorting. This column simply stores an integer and is updated every time that the user performs a search, right before the CursorLoader is created
Advantages:
We can now do all of the relevance calculations in Java code
Drawbacks:
Relevance calculation is done as the search is done so if we have a large number of items it may take some time to process everything

ContentProvider/ContentResolver Query Sorting by ID by default

I am working on an application that stores everything in a database and it is accessed using a ContentProvider. My scenario is as follows:
I make a web call and receive a list of integers which represent the ids of the objects I need to retrieve from my database on the device.
I make a call to ContentResolver.query() with the following Selection:
Selection: _id=? OR _id=? OR _id=?
Selection Ids: 30; 165; 149;
So, I need to get all items where the id is either 30, 165, or 149. And I need them in that exact order.
This is the exact call I am making on the ContentResolver:
Cursor cursor = mActivity.getContentResolver().query(myUri, null, selection, selectionIds, null);
As you can see, I do not pass in any sorting. However, the result gives me a Cursor with the order being the following: 30, 149, 165. So, it appears it is defaulting the sorting by _id even though I do not specify any sort order. My question is, does anyone know of a way to stop this from happening?
When you select rows, from any database, without specifying an ORDER BY clause, you should consider the order of the results as undefined, i.e. it could come back in any order. The reason you are seeing it sorted by _id here is just due to circumstance - they are likely to be in that order on the underlying database files so that is the order SQLite reads them back in. However it is not safe to assume that will always be so.
So the actual answer to your question is no, you can't assume SQLite will return your rows in any particular order without an ORDER BY clause. If you are unable to provide such a clause (which appears to be the case here) you'll have to sort them in code after getting all the data from the cursor.
It is not defaulting to _id, it is giving you the records as they are in the db (which happen to be sorted by id). Pass your own sorting order if you don't want this.

ORMLite union operator

I have three tables that I have to present in one Android ListView. To get the data I use the SQL UNION operator to "merge" all three tables together, so that in my ViewBinder I can make each timeline item look distinct.
These items need to be sorted in chronological order. These three tables do not have a common base class.
Here is the SQL that I have in mind:
SELECT * FROM (
SELECT id, startTime as time, username, comment, "CustomerInteraction" FROM CustomerInteraction
UNION
SELECT id, date as time, "" as username, "" as comment, "Sale" FROM Sale
UNION
SELECT id, claimDate as time, username, comment, "TravelClaim" FROM TravelClaim)
ORDER BY time DESC LIMIT 100
How can I express the above query in ORMLite?
I know I can use Dao.executeRaw, but I don't want to populate my entire list in one go. I would much rather use the trick to get the underlying cursor from ORMLite, and then just pass that to my Adapter. (Lazy loading, makes initial display of long lists much faster.)
Is there a way I can do something like Dao.getPreparedQueryFromRaw(String statement) ? Or better yet QueryBuilder.union(QueryBuilder qb)?
You can get a Cursor by calling rawQuery on the SQLiteDatabase. I do something like this:
final SQLiteDatabase db = getHelper().getReadableDatabase();
Cursor cursor = db.rawQuery(MY_SQL_QUERY, null);
You don't need to do anything much more than that.
Is there a way I can do something like Dao.getPreparedQueryFromRaw(String statement) ? Or better yet QueryBuilder.union(QueryBuilder qb)?
The best way to do this with ORMLite is with one of the the queryRaw(...) methods. They return a GenericRawResults class which you can iterate across. The iterator gives you a number of different methods to help with moving around the list.
The problem is that the generic results are not of a certain type so I'm not sure if you can map it into the Android ListView. You can provide a RawRowMapper to queryRaw(...). You can get the mapper for a particular type by using the dao.getRawRowMapper() method.
Hope something here is helpful.

ContentProvider How call method

I have a contentprovider, which i am using. Within the contentProvider, I call a method in oncreate(), which sets a lot of default values for the content provider.
I am using the content provider by another table for each entry. So, when I create a new entry in the other table, I would like to override the settings of the content provider. How can I call the method within the contentProvider, which sets default values? Since the onCreate is not called again, I do not get the default settings.
I also thought about the following approach, but this seems to be difficult as well.
Is it possible to generically copy all columns of a row and change only one column? I want to specify the column, which should be replaced, not all of the other ones...
As I have mentioned, I want to insert default values, so I am not planning to copy too much rows, which would be bad database design.
Example Table MyFavorites blob bla, String link, ....., String nameToReplace, ...
Lets say I have 20 columns. I want to copy anything, and I just want to change the nametoReplace.
Best Regards,
Patrick

Content Provider filtering query, filtering Cursor

I got following problem, I need to use a Content Provider to read a
Database of an other App.
first I want all rows, and after analyzing the data only e.g. the rows
from _id = 1, 3 and 5.
how can I call a Content provider and select only these rows?
or is it possible to create a subset Cursor form an given Cursor?
Thanks in advance.
If you're talking to another app, I assume you're querying the other app's ContentProvider to get the data from them in the first place.
In this situation, the cleanest answer seems not to build your own ContentProvider that filters/wraps theirs. Instead query their ContentProvider from your application directly, and use the select clause in your query() to specify the conditions that define the subset of data you want to be given.

Categories

Resources