How to merge CursorLoader - android

How can I merge cursor loader in the following case to get one Cursor loader. I am calling content provider more than one to create my database adapter to use with simple cursor adapter. Please look my code below.
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
productIdCursor.moveToFirst();
CursorLoader cursorLoader;
// CursorLoader cursorLoader;
int i = 0;
while (productIdCursor.isLast()) {
String[] Projection = { "p.id as _id", "p.name", "p.subTitle",
"p.textColour", "pi.thumb" };
String userSelectionCritera = "p.id = pp.prod_id and pp.imag_id = pi.id and p.id= ? and p.id = pc. prod_id and pc.coun_id = ?";
String[] selecttionArgs = new String[] {
productIdCursor.getString(productIdCursor
.getColumnIndex("prod_id")),
String.valueOf(countryId) };
cursorLoader = new CursorLoader(getActivity(),
DatabaseContentProvider.CONTENT_URI_PRODUCTCATEGORY,
Projection, userSelectionCritera, selecttionArgs, null);
productIdCursor.moveToNext();
}
return cursorLoader;

You can't really merge cursor loaders but you can try a few different things. Your best option would probably be to change your SQL selection statement from p.id = ? to p.id in (...id list here...). You can create a string for the id list by having a look at this answer.
You could also create your own custom loader that accepts an array of product ids and loads them all individually in the background, merging them in a MergeCursor object. This would give you a single loader and a cursor with all the data you need. IMO this second approach is really messy. If this is the only working solution then you probably should go back and review your database table schemas.

Related

Sort list by date and time

I am still a newbie in android developing and have a problem with my ToDo application.
At the moment my listview displaying all saved ToDo's is only sorted by the first two digits (the days!) of the date but the date is stored in this format: "dd.mm.yyyy"
My target is to sort my listview in ascending order by date (ToDoTable.COLUMN_TODO_START) and time (ToDoTable.COLUMN_TIME_START).
I use the following code at the moment:
public void fillData() {
String[] from = new String[] { ToDoTable.COLUMN_TODO_NAME, ToDoTable.COLUMN_TODO_PLACE, ToDoTable.COLUMN_TODO_START, ToDoTable.COLUMN_TODO_END };
int[] to = new int[] { R.id.todo_name, R.id.todo_place, R.id.todo_start };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row_test2, null, from,
to, 0);
setListAdapter(adapter);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { ToDoTable.COLUMN_ID, ToDoTable.COLUMN_TODO_NAME, ToDoTable.COLUMN_TODO_PLACE, ToDoTable.COLUMN_TODO_START, ToDoTable.COLUMN_TIME_START, ToDoTable.COLUMN_TODO_END, };
CursorLoader cursorLoader = new CursorLoader(this,
DataContentProvider.CONTENT_URI, projection, null, null, ToDoTable.COLUMN_TODO_START + " ASC" );
return cursorLoader;
}
I would be very grateful if someone can help me with this issue especially in the point to sort the list by both criteria (date and time).
Thanks in advance.
Best solution would be a database: For lots of data, feel free to use mysql, e.g. sqlite3. Import the library, put all data in the table and run a query:
reslt =mydatabase.find("select name,todo,time_start,time_end from todo_list order by time_start")
In mysql, the data and time is just one field. Use the TIMESTAMP in sql.
SQLite does not have a storage class set aside for storing dates and times. You can use Date And Time Functions.
So raw query can look like:
SELECT * FROM Table ORDER BY datetime(dateColumn) DESC Limit 1

How to sort the CursorLoader results?

I use CursorLoader to query a result, which is not the order that I want to show in the ListFramgenet. How to sort it ?
I use this to set the adapter:
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { "name", "distance"},
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
Create loader :
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
MEMBERS_PROJECTION,
null,
null,
null);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.changeCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
Well, there are latitude and longitude the queried results. I want to calculate the distance between my location and these results. and sort by distance asc.
How to sort it? Any answer will be appricated
It's actually quite easy:
from this:
new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
MEMBERS_PROJECTION,
null,
null,
null);
to this:
// You could have them calculated in the projection like this:
String[] projection = { COLUMN1 + " * " + COLUMN2 + " as data", // Whatever your calculations are
COLUMN1, COLUMN2, COLUMN3, etc.. };
new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
projection,
null,
null,
"data ASC");
Remember that if you have some method in your provider that does a check to the
projection and rise an exception, you would have to comment it out for the moment you are doing the test or add the new column (the one you do the calculation with) to your official projection array.
What you're looking for is supplying a sortOrder argument something like the following:
(the ORDER BY at the start of this is implicait and you do not include it, simply included here for clarity)
ORDER BY 6366000*acos(cos(lat_a / (180/3.14169))*cos(lng_a / (180/3.14169))*cos(lat_b / (180/3.14169))*cos(lng_b / (180/3.14169)) + t2 + t3) ASC
(for this I took the answer here and inlined it - except I didn't bother with t2 and t3, because it's not going to work anyway)
Unfortunately, this is impossible in standard SQLite - there are no operators sin, or cos - or even a square root or power operator (after seeing your comment about only requiring a more simple calculation).
You can add your own functions, but that's a somewhat more complicated route.
Depending on how many rows you have, you might be alright to just read them all and then sort them yourself.
Provide the column names you want to sort on as the last parameter of the CursorLoader() constructor as a string. If you have more than one column to sort by, separate with a comma. If you want ascending as opposed to descending, add DESC after the column name. So pretty much what you would add after 'ORDER BY' in regular SQL syntax.
EDIT: To answer your comment below.
Yes and no. I believe you could use that as your sort order but SQLite does not have a sqrt or power function. However you can define your own SQLite functions or use a 3rd party extension. If you don't want to go that route, you'll have to use your own custom adapter instead of SimpleCursorAdapter, e.g. you would get the cursor results and then sort them in code into another data stucture which would become the datasource of your adapter.
Just add ContactsContract.Contacts.SORT_KEY_PRIMARY.

Can I access a CursorLoader within a CursorAdapter Button OnClickListener?

I have a list of items in an Android activity. I want users to be able to reorder the list by clicking buttons on the list item that then update a weight value within an SQLite database. The list is populated from a CursorLoader.
I have implemented a solution that doesn't use the CursorLoader. This means that the weight value changes but doesn't refresh the CursorLoader. The list order doesn't refresh until the list activity is restarted.
This is the code in the CursorAdapter that calls the function to adjust the weight when the button is clicked.
final int rowID = cursor.getInt(cursor.getColumnIndex(Storage.COLUMN_ID));
Button upButton = (Button) view.findViewById(R.id.activity_edit_move_up);
upButton.setOnClickListener(new OnClickListener() {
public void onClick(View v){
Storage store = new Storage(v.getContext());
store.increaseActivityWeight(rowID);
}
});
This is the function in the Storage SQLiteOpenHelper class that changes the weight.
public void increaseActivityWeight(int activityID) {
SQLiteDatabase db = getReadableDatabase();
String[] d = { };
Cursor c;
c = db.rawQuery("SELECT _id, weight FROM activity WHERE _id = "+activityID, d);
if (c.moveToFirst()) {
int oldWeight = c.getInt(1);
int newWeight = oldWeight + 1;
ContentValues cvSelectedActivity = new ContentValues();
cvSelectedActivity.put("weight",Integer.toString(newWeight));
db.update(Storage.ACTIVITY_TABLE_NAME, cvSelectedActivity, "_id "+"="+activityID, null);
// EDIT: The following code is incomplete. It needs to identify a displaced activity by weight and swap it's weight with that of the old activity.
int displacedActivityID = c.getInt(0);
ContentValues cvDisplacedActivity = new ContentValues();
cvDisplacedActivity.put("weight",Integer.toString(newWeight));
db.update(Storage.ACTIVITY_TABLE_NAME, cvDisplacedActivity, "_id "+"="+displacedActivityID, null);
}
db.close();
c.close();
}
This initiates the CursorLoader and orders the list in the ListActivity.
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { Storage.COLUMN_ID, Storage.ACTIVITY_NAME, Storage.WEIGHT };
CursorLoader cursorLoader = new CursorLoader(getActivity(),
ActivityContentProvider.CONTENT_URI, projection, null, null, Storage.WEIGHT);
return cursorLoader;
}
Is there a way to access the CursorLoader within the CursorAdapter? Can I add implements
LoaderManager.LoaderCallbacks<Cursor> to the CursorAdapter?
I saw this tutorial that offers an alternative solution.
http://androidforbeginners.blogspot.it/2010/03/clicking-buttons-in-listview-row.html
I started implementing it this way but changed to the custom CursorAdapter. I can't remember why.
Any advice on this would be much appreciated.

CursorLoader with rawQuery

I'm looking into implementing CursorLoader in my app but I'm having a small issue that it seems that there isn't a way to just a pass a raw query to the CursorLoader constructor.
I maybe missing something in the documentation (and google), so if anybody can point me to a simple way to run a raw query with a CursorLoader class I would appreciate it. Otherwise I will have to probably create my own CursorLoader class with the necessary functionality, which I'm trying to avoid.
it seems that there isn't a way to just a pass a raw query to the CursorLoader constructor.
That is because CursorLoader works with content providers, and content providers do not support rawQuery().
so if anybody can point me to a simple way to run a raw query with a CursorLoader class I would appreciate it.
That is impossible, sorry. You are welcome to create your own AsyncTaskLoader that hits a SQLite database and supports rawQuery(). In fact, I will probably write one of these later this year, if I don't see where anyone has beaten me to it.
Raw query is not supported directly, but you can do a dirty hack: from your code call
getContentResolver().query(RAWQUERY_CONTENT_URI, null, rawquery, args, null);
and implement content provider like
#Override
public synchronized Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
int uriType = sURIMatcher.match(uri);
switch (uriType)
{
case RAW_QUERY:
return dbHelper.getReadableDatabase().rawQuery(selection, selectionArgs);
}
[...]
}
**For Custom Search using Content provider **
Change Cursor Loader as Follow (in onCreateLoader )
return new CursorLoader(
getActivity(), // Context
PRODUCT.CONTENT_URI, // URI
PROJECTION, // Projection
PRODUCT.PRODUCT_NAME+ " like ?", // Selection
new String[]{"%" + mCurFilter + "%"}, // Selection args
PRODUCT.PRODUCT_NAME + " asc");
In your Provider Change Accordingly
//C is Cursor object
switch (uriMatch) {
case ROUTE_PRODUCT_ID:
// Return a single entry, by ID.
String id = uri.getLastPathSegment();
builder.where(PRODUCT._ID + "=?", id);
c = builder.query(db, projection, sortOrder);
assert ctx != null;
c.setNotificationUri(ctx.getContentResolver(), uri);
return c;
// break;
case ROUTE_PRODUCT:
// Return all known entries.
builder.table(PRODUCT.PRODUCT_TABLE_NAME)
.where(selection, selectionArgs);
c = builder.query(db, projection, sortOrder);
assert ctx != null;
c.setNotificationUri(ctx.getContentResolver(), uri);
return c;
You can implement your own CursorLoader with raw query. This is the source of the original CursorLoader: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/CursorLoader.java

Query the android Mediastore for Artist names of Music files

I'm torn about how to implement this because Content Provider URI querys do not support the simple SQL "DISTINCT" query method to return a cursor to the Artists of the songs in the mediastore, removing any duplicate entries.
I can query and get a cursor to all the artists, I'm just torn as to how to remove the dupes, or simply not show them.
I've tried using matrixcursor to construct a new cursor with the dupe entries removed, but it's slow (build an array of artists, if in that array don't copy to the new matrixcursor, etc.)
Can anyone recommend a better solution, or point me in the proper direction??
I've also thought about pre-loading the information i need into an array of objects - I'm just concerned with memory overhead in my application.
Thank You for any help you may provide.
The easiest way to get a list of all the artist (and albums is the same method) is to use the MediaStore.Audio.Artist for the quest. For Example something like this would get and show all the artist:
String[] proj = {MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST, MediaStore.Audio.Artists.NUMBER_OF_ALBUMS, MediaStore.Audio.Artists.NUMBER_OF_TRACKS };
musiccursor = managedQuery(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, proj, null, null, MediaStore.Audio.Artists.ARTIST + " ASC");
String[] from= new String[]{ MediaStore.Audio.Artists.ARTIST, MediaStore.Audio.Artists.NUMBER_OF_ALBUMS, MediaStore.Audio.Artists.NUMBER_OF_TRACKS };
int[] to = new int[] { R.id.songname, R.id.rowlength, R.id.rowartist };
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.musicrow, musiccursor, from, to);
SongsView.setAdapter(adapter);
Where SongsView would be your list to display them in. Everything else is using a simple Cursor adapter
Hope this helps
You might want to try creating stateful CursorWrapper, overriding the appropriate methods. Simply ensure every call to next() iterates through the cursor until it finds an artist name you consider appropriately unique, optionally storing seen artists in an ArrayList instance variable.
ArrayList<SongBeen> genresList=new ArrayList<SongBeen>();
String value=search+ "%";
String[] inValue=new String[] {value};
ContentResolver musicResolver = activity.getContentResolver();
/*Uri musicInUri = android.provider.MediaStore.Audio.Genres.INTERNAL_CONTENT_URI;
Cursor mInternalCursor = musicResolver.query(musicInUri, null, null, null, null);
*/
Uri musicExUri = android.provider.MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
Cursor mExternalCursor = musicResolver.query(musicExUri, null, MediaStore.Audio.Genres.NAME+ " like ?",
inValue, "LOWER(" + MediaStore.Audio.Genres.NAME + ") ASC");
Cursor[] cursors = {mExternalCursor};
final MergeCursor mMergeCursor = new MergeCursor(cursors);
if (mMergeCursor.moveToFirst()) {
do {
SongBeen been=new SongBeen();
long thisId= mMergeCursor.getLong(mMergeCursor.getColumnIndexOrThrow(MediaStore.Audio.Genres._ID));
String thisTrack = mMergeCursor.getString(mMergeCursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.NAME));
been.setId(thisId);
been.setTrack(thisTrack);
genresList.add(been);
} while (mMergeCursor.moveToNext());
}
mMergeCursor.close();
return genresList;

Categories

Resources