Ive tried a thousand things. As of right now the only way for me to query anything is to get the entire list and look through it that way! which takes way to much time. How can I query something in google app engine, for example pull only the entities that have > 100 votes for example.
Im following the tic-tac-toe example https://github.com/GoogleCloudPlatform/appengine-endpoints-tictactoe-java and https://developers.google.com/eclipse/docs/endpoints-addentities
In the example I just switched notes for quotes.
Heres my current code for example on how im getting the entities
this is an async task and its loading each one which takes to long
protected CollectionResponseQuotes doInBackground(Context... contexts) {
Quotesendpoint.Builder endpointBuilder = new Quotesendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
Quotesendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
try {
quotes = endpoint.listquotes().execute();
for (Quotes quote : quotes.getItems()) {
if (quote.getVotes() > 3) {
quoteList.add(quote);
}
}
Here is the code that Google generated in the app engine for me when I created the endpoint. It looks like it will query somehow but I cant figure it out. They are two different projects.
#Api(name = "quotesendpoint", namespace = #ApiNamespace(ownerDomain = "projectquotes.com", ownerName = "projectquotes.com", packagePath = ""))
public class quotesEndpoint {
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* #return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
#SuppressWarnings({ "unchecked", "unused" })
#ApiMethod(name = "listquotes")
public CollectionResponse<quotes> listquotes(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
Cursor cursor = null;
List<quotes> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from quotes as quotes");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<quotes>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (quotes obj : execute)
;
} finally {
mgr.close();
}
return CollectionResponse.<quotes> builder().setItems(execute)
.setNextPageToken(cursorString).build();
}
/**
* This method gets the entity having primary key id. It uses HTTP GET method.
*
* #param id the primary key of the java bean.
* #return The entity with primary key id.
*/
#ApiMethod(name = "getquotes")
public quotes getquotes(#Named("id") String id) {
EntityManager mgr = getEntityManager();
quotes quotes = null;
try {
quotes = mgr.find(quotes.class, id);
} finally {
mgr.close();
}
return quotes;
}
Tried to user cursor but now sure how it works. Ive tried
Cursor cursor = db.rawQuery("select * from Votes WHERE Votes >" + 250 , null);
quotes = endpoint.listquotes().setCursor(cursor).execute();
Did you try to pass parameters to endpoint.listquotes()?
Specifically parameter "limit" to limit a number of results and parameter "cursor" to change selection criteria?
Related
So this is where im trying to get the data from the database, but my sqlRowSet keeps returning null.
I defined it
private SqlRowSet sqlRowSet;
#Override
public Timeseddel read() {
String sql = "SELECT * FROM timeseddel";
sqlRowSet = jdbc.queryForRowSet(sql);
while (sqlRowSet.next()) {
return new Timeseddel(sqlRowSet.getInt("timeseddel_id"), sqlRowSet.getDouble("appendix"), sqlRowSet.getDouble("c_time_fredag"),
sqlRowSet.getDouble("c_time_lordag"), sqlRowSet.getDouble("c_time_mandah"), sqlRowSet.getDouble("c_time_onsdag"),
sqlRowSet.getDouble("c_time_sondag"), sqlRowSet.getDouble("c_time_tirsdag"), sqlRowSet.getDouble("c_time_torsdag"),
sqlRowSet.getInt("fk_user_id"), sqlRowSet.getDouble("i_alt_timer"), sqlRowSet.getString("kunde"),
sqlRowSet.getDouble("o_time_fredag"), sqlRowSet.getDouble("o_time_lordag"), sqlRowSet.getDouble("o_time_mandag"),
sqlRowSet.getDouble("o_time_onsdag"), sqlRowSet.getDouble("o_time_sondag"), sqlRowSet.getDouble("o_time_tirsdag"),
sqlRowSet.getDouble("o_time_torsdag"), sqlRowSet.getDouble("p_time_fredag"), sqlRowSet.getDouble("p_time_lordag"),
sqlRowSet.getDouble("p_time_mandag"), sqlRowSet.getDouble("p_time_onsdag"), sqlRowSet.getDouble("p_time_sondag"),
sqlRowSet.getDouble("p_time_tirsdag"), sqlRowSet.getDouble("p_time_torsdag"), sqlRowSet.getInt("uge_nr"));
}
return null;
}
Make sure your tables aren't empty.
You can't return from function multiple times. Collect the data into container and return container. Something like this:
ArrayList<Timeseddel> ret = new ArrayList<Timeseddel>();
while (sqlRowSet.next()) {
ret.add( new Timeseddel(sqlRowSet.getInt("timeseddel_id"), sqlRowSet.getDouble("appendix"), sqlRowSet.getDouble("c_time_fredag"),
sqlRowSet.getDouble("c_time_lordag"), sqlRowSet.getDouble("c_time_mandah"), sqlRowSet.getDouble("c_time_onsdag"),
sqlRowSet.getDouble("c_time_sondag"), sqlRowSet.getDouble("c_time_tirsdag"), sqlRowSet.getDouble("c_time_torsdag"),
sqlRowSet.getInt("fk_user_id"), sqlRowSet.getDouble("i_alt_timer"), sqlRowSet.getString("kunde"),
sqlRowSet.getDouble("o_time_fredag"), sqlRowSet.getDouble("o_time_lordag"), sqlRowSet.getDouble("o_time_mandag"),
sqlRowSet.getDouble("o_time_onsdag"), sqlRowSet.getDouble("o_time_sondag"), sqlRowSet.getDouble("o_time_tirsdag"),
sqlRowSet.getDouble("o_time_torsdag"), sqlRowSet.getDouble("p_time_fredag"), sqlRowSet.getDouble("p_time_lordag"),
sqlRowSet.getDouble("p_time_mandag"), sqlRowSet.getDouble("p_time_onsdag"), sqlRowSet.getDouble("p_time_sondag"),
sqlRowSet.getDouble("p_time_tirsdag"), sqlRowSet.getDouble("p_time_torsdag"), sqlRowSet.getInt("uge_nr"));
}
return ret;
I am using Google App Engine as a backend for my Android app.I am able to insert entities into the datastore using the method(auto-generated when i created the endpoint for my Note class)
/**
* This inserts a new entity into App Engine datastore. If the entity already
* exists in the datastore, an exception is thrown.
* It uses HTTP POST method.
*
* #param note the entity to be inserted.
* #return The inserted entity.
*/
#ApiMethod(name = "insertNote")
public Note insertNote(Note note) {
EntityManager mgr = getEntityManager();
try {
if (containsNote(note)) {
throw new EntityExistsException("Object already exists");
}
mgr.persist(note);
} finally {
mgr.close();
}
return note;
}
So to use this method from my Android app and insert an Entity i use this from my AsyncTask class
try {
Note note = new Note();
String descrptn = descriptionTF.getText().toString();
String email = emailTF.getText().toString();
String noteID = new Date().toString();
note.setDescription(descrptn);
note.setId(noteID);
note.setEmailAddress(email);
Note result = endpoint.insertNote(note).execute();
} catch (IOException e) {
e.printStackTrace();
}
It's all sunshine and rainbows till i try to retrieve the entity from the datastore.I get FATAL Exception:...Caused by Java.lang.NullPointerException: required parameter id must be specified
This is the method to call when trying to retrieve an Entity(Auto-generated also)
/**
* This method gets the entity having primary key id. It uses HTTP GET method.
*
* #param id the primary key of the java bean.
* #return The entity with primary key id.
*/
#ApiMethod(name = "getNote")
public Note getNote(#Named("id") String id) {
EntityManager mgr = getEntityManager();
Note note = null;
try {
note = mgr.find(Note.class, id);
} finally {
mgr.close();
}
return note;
}
and this is what am trying from my Android activity
try {
Note newNote = new Note();
String noteID = newNote.getId();
Note newResult = endpoint.getNote(noteID).execute();
String descrptn = newResult.getDescription();
String email = newResult.getEmailAddress();
descriptionTV.setText(descrptn);
emailTV.setText(email);
} catch (IOException e) {
e.printStackTrace();
}
return (long) 0;
}
Can any help me figure this out?
In your Android activity, I see the following code :
Note newNote = new Note();
String noteID = newNote.getId();
Note newResult = endpoint.getNote(noteID).execute();
If you see, you are creating an instance of the Note() class in the first line. This still means that other attributes of the Note class and which includes Id are null. So actually in your second line, you are assigning null to noteID and that is being passed along to the endpoint getNote method. Hence the server side gets a null for the Id and hence throws the exception.
In your Android code, you might be showing a list of notes in some ListActivity and then when you are selecting one of the notes, you will be able to get the ID from the selectedItem. So you should pass this value instead, to retrieve the details from the Server endpoint implementation.
Hope this makes things clear.
after generating Endpoint from a model class I edit the ClassnameEndpoint file to make a method that will return a list of Entities using a modified SELECT string.
Somehow this makes a clash and Generate Client Libraries fails like this:
INFO: Successfully processed C:\Users\1177\AndroidStudioProjects\N5\appN5-AppEngine\target/generated-sources/appengine-endpoints\WEB-INF/appengine-web.xml
[ERROR]
com.google.api.server.spi.config.validation.ApiConfigInvalidException: Multiple methods with same rest path "GET eventdata": "listUserEventData" and "listEventData"
here is the ClassnameEndpoint.java, which is edited by hand after being generated:
note listEventData and listUserEventData are identical except for SELECT string...
So how can I create an endpoint method to get ALL the entities and another method to get SOME entities without the clash?
package sic.example.appn5;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.datanucleus.query.JPACursorHelper;
import java.util.List;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
#Api(name = "eventdataendpoint", namespace = #ApiNamespace(ownerDomain = "example.sic", ownerName = "example.sic", packagePath = "appn5"))
public class EventDataEndpoint {
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* #return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
#SuppressWarnings({"unchecked", "unused"})
#ApiMethod(name = "listEventData")
public CollectionResponse<EventData> listEventData(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
List<EventData> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from EventData as EventData");
Cursor cursor;
if (cursorString != null && cursorString.trim().length() > 0) {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<EventData>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null) cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (EventData obj : execute) ;
} finally {
if (mgr != null) {
mgr.close();
}
}
return CollectionResponse.<EventData>builder()
.setItems(execute)
.setNextPageToken(cursorString)
.build();
}
#SuppressWarnings({"unchecked", "unused"})
#ApiMethod(name = "listUserEventData")
public CollectionResponse<EventData> listUserEventData(
#Nullable #Named("username") String username,
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
List<EventData> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery(String.format("select from EventData as EventData where BelongsTo = '%s'", username));
Cursor cursor;
if (cursorString != null && cursorString.trim().length() > 0) {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<EventData>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null) cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (EventData obj : execute) ;
} finally {
if (mgr != null) {
mgr.close();
}
}
return CollectionResponse.<EventData>builder()
.setItems(execute)
.setNextPageToken(cursorString)
.build();
}
/**
* This method gets the entity having primary key id. It uses HTTP GET method.
*
* #param id the primary key of the java bean.
* #return The entity with primary key id.
*/
#ApiMethod(name = "getEventData")
public EventData getEventData(#Named("id") String id) {
EntityManager mgr = getEntityManager();
EventData eventData = null;
try {
eventData = mgr.find(EventData.class, id);
} finally {
mgr.close();
}
return eventData;
}
/**
* This inserts a new entity into App Engine datastore. If the entity already
* exists in the datastore, an exception is thrown.
* It uses HTTP POST method.
*
* #param eventData the entity to be inserted.
* #return The inserted entity.
*/
#ApiMethod(name = "insertEventData")
public EventData insertEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
try {
if (containsEventData(eventData)) {
throw new EntityExistsException("Object already exists");
}
mgr.persist(eventData);
} finally {
mgr.close();
}
return eventData;
}
/**
* This method is used for updating an existing entity. If the entity does not
* exist in the datastore, an exception is thrown.
* It uses HTTP PUT method.
*
* #param eventData the entity to be updated.
* #return The updated entity.
*/
#ApiMethod(name = "updateEventData")
public EventData updateEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
try {
if (!containsEventData(eventData)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(eventData);
} finally {
mgr.close();
}
return eventData;
}
/**
* This method removes the entity with primary key id.
* It uses HTTP DELETE method.
*
* #param id the primary key of the entity to be deleted.
* #return The deleted entity.
*/
#ApiMethod(name = "removeEventData")
public EventData removeEventData(#Named("id") String id) {
EntityManager mgr = getEntityManager();
EventData eventData = null;
try {
eventData = mgr.find(EventData.class, id);
mgr.remove(eventData);
} finally {
mgr.close();
}
return eventData;
}
private boolean containsEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
boolean contains = true;
try {
EventData item = mgr.find(EventData.class, eventData.getEventKey());
if (item == null) {
contains = false;
}
} finally {
mgr.close();
}
return contains;
}
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
}
The end of the automatically generated path looks like this: _ah/api/endpoint_name/version_name/return_type/{named_parameter_1}/{named_parameter_2}/…
The problem you're facing is that both methods are in the same endpoint class, of the same version, have the same return type, and use GET. Therefore, the URLs will be identical and conflicting. In this case, they're both _ah/api/eventdataendpoint/v1/eventdata.
The solution is to add the path attribute to one of the classes, like so:
#ApiMethod(name = "listUserEventData", path="eventdata/user")
public CollectionResponse<EventData> listUserEventData(…
Now, the first method with have URL _ah/api/eventdataendpoint/v1/eventdata and the second one will have path _ah/api/eventdataendpoint/v1/eventdata/user.
As you increase the number of methods in your endpoint, you will come across conflicts like this a lot, so if you plan on making many new methods, it's a good idea to use the path attribute every time rather than rely on CE to auto-generate a path.
Edit: you'll find a lot of info pertaining to your API and each method's path at YOUR_APP_ID.appspot.com/_ah/api/discovery/v1/apis/ENDPOINT_NAME/v1/rest
The Answer given by #willlma is definately right !
But a very simple way of fixing this temporarily and avoid a lot of code change is to add another named parameter to be passed in the method.
public Event getFirstEvent(#Named("mainEventId") Long mainEventId,
#Named("useless") Boolean useless ,
User auth) throws UnauthorizedException {
if (auth!=null){
...
return event
} else throw new UnauthorizedException("Please authenticate first.");
}
public Event getEvent(#Named("eventID") Long eventID, User auth) throws UnauthorizedException {
if (auth != null) {
...
return event;
} else throw new UnauthorizedException("Please authenticate first.");
}
I've a question on hibernate operation: update.
Here a bit of code:
Campaign campaign = campaignDAO.get(id);
campaign.setStatus(true);
campaignDAO.update(campaign);
If I just have all the data of the campaign object, is there any way to perform an update without perform the first select (campaignDAO.get(id)) ?
Thanks,
Alessio
HQL will definitely help you.
In order to maintain the separation of concerns, you can add a more specialized method in you DAO object:
public void updateStatusForId(long id, boolean status){
//provided you obtain a reference to your session object
session.createQuery("UPDATE Campaign SET status = " + status + " WHERE id = :id").setParameter("id", id).executeUpdate();
//flush your session
}
Then you could simply call this method from your business method. You can check the generated SQL statements inside the logs of your app by setting the show_sql hibernate property to true.
You can use session.load(). It will not hit the database. Here you can find its details and example code.
I had worte a extension to solve this issue in Nhibernate
how to use!
first of all you need enable dynamic-update="true"
using (ISession session = sessionFactory.OpenSession())
{
Customer c1 = new Customer();
c1.CustomerID = c.CustomerID;
session.Mark(c1);
// c1.Name = DateTime.Now.ToString();
c1.Phone = DateTime.Now.ToString();
//需要开启动态更新
session.UpdateDirty(c1);
session.Flush();
}
UpdateExtension.cs
public static class UpdateExtension
{
static readonly Object NOTNULL = new Object();
public static void UpdateDirty<TEntity>(this ISession session, TEntity entity)
{
SessionImpl implementor = session as SessionImpl;
EntityEntry entry = implementor.PersistenceContext.GetEntry(entity);
if (entry == null)
{
throw new InvalidOperationException("找不到对应的实例,请先使用Mask方法标记");
}
IEntityPersister persister = entry.Persister;
// 如果某列不可以为空,新的Entity里也不想更新他。
// 那么LoadState 里的值应该和Entity 中的值相同
Object[] CurrentState = entry.Persister.GetPropertyValues(entity, EntityMode.Poco);
Object[] LoadedState = entry.LoadedState;
int[] dirtys = persister.FindDirty(CurrentState
, LoadedState
, entity
, (SessionImpl)session);
if (dirtys == null || dirtys.Length == 0)
{
return;
}
persister.Update(entry.Id
, CurrentState
, dirtys
, true
, LoadedState
, entry.Version
, entity
, entry.RowId
, (SessionImpl)session);
implementor.PersistenceContext.RemoveEntry(entity);
implementor.PersistenceContext.RemoveEntity(entry.EntityKey);
session.Lock(entity, LockMode.None);
// 防止(implementor.PersistenceContext.EntityEntries.Count == 0)
}
public static void Mark<TEntity>(this ISession session, TEntity entity)
{
session.Lock(entity, LockMode.None);
}
}
here is update sql
command 0:UPDATE Customers SET Phone = #p0 WHERE CustomerID = #p1;#p0 = '2014/12/26 0:12:56' [Type: String (4000)], #p1 = 1 [Type: Int32 (0)]
Only update Phone column .
event Name property can not be null. we can work very well.
A month ago, I dropped-in ActionBarSherlock 4.2 into my project. I got everything to work, except the search suggestions for my SearchView. The way I was creating search suggestions was using the method in the Android documentation.
Does ActionBarSherlock support search suggestions? I tried to dig through the issue list on the Github page but the issue seems closed but I can't seem to follow the discussion and understand whether it really is a resolved or not. I thought that some of you who've been using ActionBarSherlock might know better.
It doesn't. But I have found a way to make it query your ContentProvider.
I looked into the source of SuggestionsAdapter from API 17 where the query executes and got an idea of replacing this method. Also I found that ActionbarSherlock's SuggestionsAdapter does not use your SearchableInfo.
Edit com.actionbarsherlock.widget.SuggestionsAdapter in your ActionBarSherlock project:
Add a line
private SearchableInfo searchable;
in constructor, add
this.searchable = mSearchable;
Replace getSuggestions method with this one:
public Cursor getSuggestions(String query, int limit) {
if (searchable == null) {
return null;
}
String authority = searchable.getSuggestAuthority();
if (authority == null) {
return null;
}
Uri.Builder uriBuilder = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
.fragment(""); // TODO: Remove, workaround for a bug in Uri.writeToParcel()
// if content path provided, insert it now
final String contentPath = searchable.getSuggestPath();
if (contentPath != null) {
uriBuilder.appendEncodedPath(contentPath);
}
// append standard suggestion query path
uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
// get the query selection, may be null
String selection = searchable.getSuggestSelection();
// inject query, either as selection args or inline
String[] selArgs = null;
if (selection != null) { // use selection if provided
selArgs = new String[] { query };
} else { // no selection, use REST pattern
uriBuilder.appendPath(query);
}
if (limit > 0) {
uriBuilder.appendQueryParameter("limit", String.valueOf(limit));
}
Uri uri = uriBuilder.build();
// finally, make the query
return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
}
Now it queries my ContentProvider but crashes with default adapter saying that no layout_height loading some xml file from support library. So you have to use custom SuggestionsAdapter. This is what worked for me:
import com.actionbarsherlock.widget.SearchView;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public final class DrugsSearchAdapter extends CursorAdapter
{
private static final int QUERY_LIMIT = 50;
private LayoutInflater inflater;
private SearchView searchView;
private SearchableInfo searchable;
public DrugsSearchAdapter(Context context, SearchableInfo info, SearchView searchView)
{
super(context, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
this.searchable = info;
this.searchView = searchView;
this.inflater = LayoutInflater.from(context);
}
#Override
public void bindView(View v, Context context, Cursor c)
{
String name = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
TextView namet = (TextView) v.findViewById(R.id.list_item_drug_name);
namet.setText(name);
String man = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2));
TextView manuf = (TextView) v.findViewById(R.id.list_item_drug_manufacturer);
manuf.setText(man);
}
#Override
public View newView(Context arg0, Cursor arg1, ViewGroup arg2)
{
return this.inflater.inflate(R.layout.list_item_drug_search, null);
}
/**
* Use the search suggestions provider to obtain a live cursor. This will be called
* in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
* The results will be processed in the UI thread and changeCursor() will be called.
*/
#Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
String query = (constraint == null) ? "" : constraint.toString();
/**
* for in app search we show the progress spinner until the cursor is returned with
* the results.
*/
Cursor cursor = null;
if (searchView.getVisibility() != View.VISIBLE
|| searchView.getWindowVisibility() != View.VISIBLE) {
return null;
}
try {
cursor = getSuggestions(searchable, query, QUERY_LIMIT);
// trigger fill window so the spinner stays up until the results are copied over and
// closer to being ready
if (cursor != null) {
cursor.getCount();
return cursor;
}
} catch (RuntimeException e) {
}
// If cursor is null or an exception was thrown, stop the spinner and return null.
// changeCursor doesn't get called if cursor is null
return null;
}
public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {
if (searchable == null) {
return null;
}
String authority = searchable.getSuggestAuthority();
if (authority == null) {
return null;
}
Uri.Builder uriBuilder = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.query("")
.fragment("");
// if content path provided, insert it now
final String contentPath = searchable.getSuggestPath();
if (contentPath != null) {
uriBuilder.appendEncodedPath(contentPath);
}
// append standard suggestion query path
uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
// get the query selection, may be null
String selection = searchable.getSuggestSelection();
// inject query, either as selection args or inline
String[] selArgs = null;
if (selection != null) { // use selection if provided
selArgs = new String[] { query };
} else { // no selection, use REST pattern
uriBuilder.appendPath(query);
}
if (limit > 0) {
uriBuilder.appendQueryParameter("limit", String.valueOf(limit));
}
Uri uri = uriBuilder.build();
// finally, make the query
return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
}
}
And set this adapter in SearchView
searchView.setSuggestionsAdapter(new DrugsSearchAdapter(this, searchManager.getSearchableInfo(getComponentName()), searchView));
I'm the one that opened the github issue for this. It is working on the dev branch. The current version (4.2) doesn't have the fix. It was completely fixed by this commit, but I would suggest just checking out the dev branch and trying it.
I don't know if I'm wrong here or I changed something on accident, but the above answer does not work and the ActionBarSherlock SuggestionsAdapter does not work. All I get are null pointers in runQueryOnBackgroundThread. It never goes into bindView etc. either, yet it manages to display suggestion results. I think android.app.SearchManager is somehow overriding ABS with getSuggestions() but I'm not sure. I'm still trying things out...