SearchManager - adding custom suggestions - android

I've read all of the documentation online about building search interfaces and adding custom suggestions... but I'm still unclear on how this works. The documentation says that I must "Build a table (such as in an SQLiteDatabase) for your suggestions and format the table with required columns". I'm assuming the system will eventually fill this table with the appropriate suggestions on its own... but which process/class is responsible for this, and when will the actual insertions occur (before any query is made by the user, after a query has been made by the user, etc.)?
And while I'm asking a question up here, if someone could clarify the difference between an AutoCompleteTextView and a SearchView w/ custom suggestions... that'd be awesome. AutoCompleteTextView seems suspiciously easy to implement compared to the SearchView (which requires changes to be made to the ContentProvider, SQLiteDatabase helper class, etc.).

You have to create a content provider which delivers your custom suggestions based on a query so far entered in the search view. In your searchable.xml you configure the minimum length of the search expression, which must be reached before asking for suggestions. This content provider is called a suggestion provider (it still extends ContentProvider). The content provider's authority is also configured in searchable.xml.
There is no limitation on how the suggestion provider computes its suggestions. You can search the web query a database or read a file. But the answer to the query is in the format of a table. If the suggestions is directly queried from a database you can use the cursor answered by the database query to deliver the result in the content provider's query() method. If the result is computed from one or more sources you can create a table on the fly by using a MatrixCursor.
The rows of the answer from the suggestion provider are used by the search mechanism to display the suggestion, they are stored in a table. The format of the rows is as follows:
private static final String[] COLUMNS = {
"_id",
SearchManager.SUGGEST_COLUMN_ICON_1, // ID of a drawable (icon) as String
SearchManager.SUGGEST_COLUMN_TEXT_1, // main text for suggestion display
SearchManager.SUGGEST_COLUMN_TEXT_2, // secondary text for suggestion display
SearchManager.SUGGEST_COLUMN_INTENT_DATA, // this could be an URI to access the suggestion as used in an intent with a VIEW action
SearchManager.SUGGEST_COLUMN_INTENT_ACTION, // this could be Intent.ACTION_VIEW
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID // e.g. SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT
};
Searching is described here in more detail: http://developer.android.com/guide/topics/search/index.html

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

Using Cursor respond and getExtras not working?

I'm designing a ContentProvider which needs to return a cursor with data and I need to know the status of that data.
I need a status for the cursor data since this data was downloaded and I need to know if it only downloaded partial data or if during the download some of the elements weren't downloaded. It's a bit more complex but you might get the idea why I need a status for my cursor data.
So I have reviewed the source code for all Cursor implementations in Android and it seems getExtras and respond functions are plain garbage since they don't do anything internally... Also, there seems to be missing a setExtras function. Any ideas?
I think that the method is not setExtras(), but respond().
I'm not sure what you mean by the "status of the data", but it sounds like you want to know if the columns in the cursor are clean or not. Most content provider implementations I've seen put this type of status into the rows themselves, or store it in a separate table. I don't think you should try attaching the status to the Cursor itself.
Just so you know, if you want to get notification when the data underlying the cursor changes, you can use setNotificationUri(), registerContentObserver, or registerDataSetObserver().
Notice that nothing requires you to make the columns you provide from a content provider be the same as the columns in any underlying database you use. You could add a column that the provider itself generates, and store status in that.

Best practices for joining tables and notifying ContentObservers in Android ContentProvider

I have a ContentProvider which handles all the data insertion and retrieval related to my application, I'm following the pattern suggested by Virgil Dobjanschi on Google I/O. I am using the first pattern.
My problem is that I have a logical entity that was represented by multiple tables in the database.
For example, I have an Articles table and an ArticleExtras table. Articles represents the article itself, while ArticleExtras represents addtional information about certain Article, like number of comments.
I used CursorAdapter in the UI to display the Article title and the number of comments of that Article in one row of ListView.
To implement that, I added a left outer join ArticleExtras on statement in my ContentProvider query method for Article table, in order for CursorAdapter to get ArticleExtras along with the Article itself.
When new Articles are fetched from the web, I insert it into the database via ContentProvider, and then the CursorAdapter got notified and update the UI, this part worked as expected.
But when I fetched the number of comments (ArticleExtras), I want the same CursorAdapter, which is watching for changes in the content://com.myapp.content/Articles, to be notified, too, so I can update my row in the ListView.
My current implementation is like this: After inserting ArticleExtras into the database, I start a new query to check if Articles table has any rows that is related to the ArticleExtras I just inserted. If so I'll make a new uri for that Article( for example: content://com.myapp.cotent/Articles/123), and call getContext().getContentResolver().notifyChange(uri, null), so the corresponding CursorAdapter that is watching for changes of this Article will get notified.
Is the approach correct, or is there any better way to implement what I want?
Checkout ContactsProvider2, in it they set the notification uri to the AUTHORITY_URI which appears to be a catch all for the other URIs in the provider. I had the same probem and I have tried this myself for a provider with multiple tables and joins on those tables, and it works fine.

how to display suggestions from the web

I overwite the SearchRecentSuggestionsProvider to make the custom suggestions for my search, but now I need to return the suggestion data from the server instead of the local provider, how to solve it?
Well, I would say that it is not a good practice but it can be done.
Basically you need to:
- override the method query in your content provider
- query your server or whatever for getting the results
- building a cursor out of the results using a MatrixCursor
From the doc:
If your search suggestions are not stored in a table format (such as an SQLite table)
using the columns required by the system, then you can search your suggestion data for
matches and then format them into the necessary table on each request. To do so, create
a MatrixCursor using the required column names and then add a row for each suggestion
usingaddRow(Object[]). Return the final product from your Content Provider's query()
method.
Hope it will help.

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