Android - how to delete item from a cursor? - java

Let's say I make the following cursor to get the call log of someone:
String[] strFields = {
android.provider.CallLog.Calls.NUMBER,
android.provider.CallLog.Calls.TYPE,
android.provider.CallLog.Calls.CACHED_NAME,
android.provider.CallLog.Calls.CACHED_NUMBER_TYPE
};
String strOrder = android.provider.CallLog.Calls.DATE + " DESC";
Cursor mCallCursor = getContentResolver().query(
android.provider.CallLog.Calls.CONTENT_URI,
strFields,
null,
null,
strOrder
);
Now how would I go about deleted the ith item in this cursor? This could also be a cursor getting list of music, etc. So then I must ask - is this even possible? I can understand for certain cursors that 3rd party apps wouldn't be allowed to delete from.
Thanks.

Sorry mate you can't delete from a cursor.
You must either use your ContentResolver or a SQL call of some sort..

You can to a trick with a MatrixCursor. With this strategy, you copy the cursor, and leave out the one row you want to exclude. This is - obviously, not very efficient for large cursors as you will keep the entire dataset in memory.
You also have to repeat the String array of column names in the constructor of the MatrixCursor. You should keep this as a Constant.
//TODO: put the value you want to exclude
String exclueRef = "Some id to exclude for the new";
MatrixCursor newCursor = new MatrixCursor(new String[] {"column A", "column B");
if (cursor.moveToFirst()) {
do {
// skip the copy of this one ....
if (cursor.getString(0).equals(exclueRef))
continue;
newCursor.addRow(new Object[]{cursor.getString(0), cursor.getString(1)});
} while (cursor.moveToNext());
}
I constantly battle with this; trying to make my apps with cursors and content providers only, keeping away from object mapping as long as I can. You should see some of my ViewBinders ... :-)

Related

Android - SQLite database not updating row

I have looked at the plethora of similar topics for the past couple days but none seem to help my actual situation or maybe it's just my inexperience.
I have a simple application that performs CRUD operations. I can successfully enter new rows into the table as well as delete them, however something is wrong with my update code and I can't find why. What has me puzzled is it worked once, then never did again regardless if I create new entries or delete the database entirely and start again. Isolating what I think the relevant code would be the operation flow is as follows.
The individual items are inside a RecyclerView and are sent from an inner class to the activity to be edited and updated:
public void onBindViewHolder(#NonNull ContactViewHolder holder, int position) {
final String contactName = mCursor.getString(mCursor.getColumnIndex(ContactEntry.COLUMN_NAME));
final int contactNumber = mCursor.getInt(mCursor.getColumnIndex(ContactEntry.COLUMN_PHONE_NUMBER));
final String contactMail = mCursor.getString(mCursor.getColumnIndex(ContactEntry.COLUMN_EMAIL));
final int currentPosition = position;
holder.mEditContact.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, ContactUpdate.class);
intent.putExtra(CONTACT_POSITION, currentPosition);
intent.putExtra(EXTRA_NAME, contactName);
intent.putExtra(EXTRA_PHONE, contactNumber);
intent.putExtra(EXTRA_MAIL, contactMail);
mContext.startActivity(intent);
}
});
Then I get them in the retrieving class and organise the values into the appropriate EditText views. I'm sure there is a more efficient way but I'm still learning:
Intent intent = getIntent();
mPosition = intent.getIntExtra(ContactRecyclerAdapter.CONTACT_POSITION, 0);
mContactName = intent.getStringExtra(ContactRecyclerAdapter.EXTRA_NAME);
mContactPhone = intent.getIntExtra(ContactRecyclerAdapter.EXTRA_PHONE, 0);
mContactMail = intent.getStringExtra(ContactRecyclerAdapter.EXTRA_MAIL);
mUpdateName.setText(mContactName);
mUpdatePhone.setText("" + mContactPhone);
mUpdateMail.setText(mContactMail);
Lastly my method to update the row:
private void updateData() {
String name = mUpdateName.getText().toString();
String getPhone = mUpdatePhone.getText().toString();
int phone = Integer.parseInt(getPhone);
String mail = mUpdateMail.getText().toString();
SQLiteDatabase db = new ContactOpenHelper(this).getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ContactEntry._ID, mPosition);
values.put(ContactEntry.COLUMN_NAME, name);
values.put(ContactEntry.COLUMN_PHONE_NUMBER, phone);
values.put(ContactEntry.COLUMN_EMAIL, mail);
String selection = ContactEntry._ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(mPosition)};
db.update(ContactEntry.TABLE_NAME, values, selection, selectionArgs);
db.close();
startActivity(new Intent(this, ContactList.class));
}
What is happening is the row is not being updated. Currently I have three mock entries entered. I put a break point at the update() line to try and find out what is happening. The original name for the this entry is "ted" and it's the third in the list. If I edit the EditText field and run the updateData() method, when the break point is hit it correctly shows the edited value as well the correct position of 2. However the database doesn't actually update. I am clearly misunderstanding how something works here.
To avoid bloat I have only included what I think is pertinent, but if anymore info is required then I'm happy to add.
Lastly here is a screen shot of the debugger at the break point showing what I perceive to be the correct information and where I'm completely confused as to why it is not working (I added a bunch of g's on the end of the name "ted" just to test if it would work).
Chnage your call to update as below
int count = db.update(ContactEntry.TABLE_NAME, values, selection, selectionArgs);
where count shows the number of the rows afftected with the update function, this would help you know exactly if there was any updated row or not.
Note :-We use like when, sometimes, you don’t know exactly the complete keyword that you want to query. For example, you may know that your most favorite song contains the word,elevator but you don’t know exactly the name.
If you know the exact value try to use:-
String selection = ContactEntry._ID + " =? ";
String selection = ContactEntry._ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(mPosition)};
Update this above code to this below code
String selection = ContactEntry._ID + " = ?";
String[] selectionArgs = { String.valueOf(mPosition)};
You want to update an exact row. But 'LIKE' keyword mostly uses for searches.
Besides, You are opening your activity when you update your data. Update or insert in database takes some time. When your next activity start, database may not finish its write operation. It could lead you to see the previous data. Make sure your database is updated and then update the view.
See your database from emulator that database has the new value or not. Then do the further work.
Try to delete you _Id from contentValues:
//values.put(ContactEntry._ID, mPosition);
I think you can't change PRIMARY_KEY, you say what line you want to update in:
String[] selectionArgs = { String.valueOf(mPosition)};
I have now solved this particular problem and the answer was quite simple and a little embarrassing. I was trying to use the cursor position for the row ID not realising that the database isn't using a zero-base index like the cursor, hence the primary key was not matching.
As I am using a RecyclerView I added the following line to my onBindViewHolder and passed the variable to my update activity which then could be used in the selectionArgs
final long id = mCursor.getLong(mCursor.getColumnIndex(ContactEntry._ID));

SQLite Cursor returning the value for one column as the value for all columns

I am attempting to read from an SQLite database I have created containing information on UK universities.
The table has the following columns:
Institution
Rank_2017
Guardian_score100
Satisfied_with_course
Satisfied_with_teaching
Satisfied_with_feedback
Student_to_staff_ratio
Average_entry_tariff
Career_after_6_months
I have populated the table from a CSV file, and then attempted to call a getUni() method that will return the information in each of the columns, and use them to build a University object, however when I check the values of each member variable of University, the name and rank have been set appropriately, however every variable after that has also been set to the value of Rank_2017.
getUni() Method
public University getUni(String id)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_UNIVERSITIES, COLUMNS, " Institution = ?",
new String[] { id }, null, null, null, null);
if(cursor != null){
cursor.moveToFirst();
}
System.out.println("###" + cursor.getCount() + "###");
University uni = new University();
if(cursor != null){
uni.setUni_name(cursor.getString(0));
uni.setRank(cursor.getString(1));
uni.setGuardianScore(cursor.getString(2));
uni.setCourseSatisfaction(cursor.getString(3));
uni.setTeachingSatisfaction(cursor.getString(4));
uni.setFeedbackSatisfaction(cursor.getString(5));
uni.setStudentStaffRatio(cursor.getString(6));
uni.setAverageEntryTariff(cursor.getString(7));
uni.setCareerAfterSixMonths(cursor.getString(8));
}
// return University
return uni;
}
After calling cursor.getColumnCount() i can see that it returns 10 columns as expected, however something isn't quite right.
For example, if I call University uni = getUni("Glasgow"); , it will assign both the University name, and rank_2017 correctly, however assign each member variable after that the same as rank_2017.
CSV row example:
Glasgow,26,70.8,89.3,91.6,67.3,14.5,478.4,78.5
Any help solving this issue would be hugely appreciated.
I suspect when you imported the CSV file, it didn't work correctly. Perhaps you didn't specify a separator, and SQLite created a table for you with only one column. Or it populated the table strangely, and your SQL adapter is "helping" in a surprising way. That could happen if for instance your CSV file had some whitespace.
I would use the SQLite shell to verify that the Glassgow row appears as expected in the database. That at least will let you distinguish between SQLite issues and Java library issues (if any).
HTH.

Android: column _id does not exist [duplicate]

I'm having trouble with something that works in the Notepad example.
Here's the code from the NotepadCodeLab/Notepadv1Solution:
String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };
SimpleCursorAdapter notes = new SimpleCursorAdapter(this,
R.layout.notes_row, c, from, to);
This code seems to work fine. But just to be clear, I ran the ADB
utility and run SQLite 3. I inspected the schema as follows:
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE notes (_id integer primary key autoincrement, title text
not null, body text not null);
All seems good to me.
Now on to my application, which, as far as I can see, is basically the same with
a few minor changes. I've simplified and simplified my code, but the
problem persists.
String[] from = new String[] { "x" };
int[] to = new int[] { R.id.x };
SimpleCursorAdapter adapter = null;
try
{
adapter = new SimpleCursorAdapter(this, R.layout.circle_row, cursor, from, to);
}
catch (RuntimeException e)
{
Log.e("Circle", e.toString(), e);
}
When I run my application, I get a RuntimeException and the following prints
in LogCat from my Log.e() statement:
LogCat Message:
java.lang.IllegalArgumentException: column '_id' does not exist
So, back to SQLite 3 to see what's different about my schema:
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE circles (_id integer primary key autoincrement, sequence
integer, radius real, x real, y real);
I don't see how I'm missing the '_id'.
What have I done wrong?
One thing that's different between my application and the Notepad example is
that I started by creating my application from scratch using the
Eclipse wizard while the sample application comes already put together. Is
there some sort of environmental change I need to make for a new application
to use a SQLite database?
I see, the documentation for CursorAdapter states:
The Cursor must include a column named _id or this class will not
work.
The SimpleCursorAdapter is a derived class, so it appears this statement applies. However, the statement is technically wrong and somewhat misleading to a newbie. The result set for the cursor must contain _id, not the cursor itself.
I'm sure this is clear to a DBA because that sort of shorthand documentation is clear to them, but for those newbies, being incomplete in the statement causes confusion. Cursors are like iterators or pointers, they contain nothing but a mechanism for transversing the data, they contain no columns themselves.
The Loaders documentation contains an example where it can be seen that the _id is included in the projection parameter.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// ...
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
This has been answered and I would like to make it more comprehensive here.
SimpleCursorAdapter requires that the Cursor's result set must include a column named exactly "_id". Don't haste to change schema if you didn't define the "_id" column in your table.
SQLite automatically added an hidden column called "rowid" for every table. All you need to do is that just select rowid explicitly and alias it as '_id' Ex.
SQLiteDatabase db = mHelper.getReadableDatabase();
Cursor cur = db.rawQuery( "select rowid _id,* from your_table", null);
Tim Wu's code really works...
If you are using db.query, then it would be like this...
db.query(TABLE_USER, new String[] {
"rowid _id",
FIELD_USERNAME,
},
FIELD_USERNAME + "=" + name,
null,
null,
null,
null);
Yes , I also change the SELECT string query to fix this issue.
String query = "SELECT t.*,t.id as _id FROM table t ";
What solved my issue with this error was that I had not included the _id column in my DB query. Adding that solved my problem.
This probably isn't relevant anymore, but I just hit the same problem today. Turns out column names are case sensitive. I had an _ID column, but Android expects an _id column.
If you read the docs on sqlite, creating any column of type INTEGER PRIMARY KEY will internally alias the ROWID, so it isn't worth the trouble of adding an alias in every SELECT, deviating from any common utilities that might take advantage of something like an enum of columns defining the table.
http://www.sqlite.org/autoinc.html
It is also more straightforward to use this as the ROWID instead of the AUTOINCREMENT option which can cause _ID can deviate from the ROWID. By tying _ID to ROWID it means that the primary key is returned from insert/insertOrThrow; if you are writing a ContentProvider you can use this key in the returned Uri.
Another way of dealing with the lack of an _id column in the table is to write a subclass of CursorWrapper which adds an _id column if necessary.
This has the advantage of not requiring any changes to tables or queries.
I have written such a class, and if it's of any interest it can be found at https://github.com/cmgharris/WithIdCursorWrapper

How to use a get method for a SQLite Database management class in another class?

What I am trying to do is retrieve an ArrayList from another database manager class. Unfortunately all I can do because the manager class cannot work statically is create an instance in another class, then call the method. Then I got myself into passing that same instance into the method which asked for an SQLiteDatabase object. Now I've worked myself into a bind of confusion, when all I really want is to do is retrieve the arraylist to display a listview of elements from an SQL column.
EDIT: My post lacked clarity, so I'll try to specify exactly what is going wrong and what I am trying to accomplish here:
In a display (output) activity, I am trying to use a ListView to display elements contained in an SQL database. Currently, I am only focusing on one column (Assignment Names). My approach involved using a get method built into the database manager class, but because you cannot reference that method statically, I tried to use the method by creating an instance of that manager class. This would return an ArrayList of Inputted objects (each containing a name). It seemed to have worked, but when running the program, the LogCat protested that I was calling getDatabase recursively. After looking online, people recommended that I fix the issue by changing the method to ask for (SQLiteDatabase db) as parameters so the same database gets tossed around in the manager. Now I get confused here-- I'm not sure what to pass into this method from the display activity. It also doesn't help that from what I've heard from the comments, my get method doesn't traverse the SQL database properly. If you can solve this puzzle THANK YOU!
I'll post my code for diagnosis, hopefully an outside view will show exactly what's wrong with everything I'm trying here.
public Cursor getAssignmentNames(SQLiteDatabase db) {
return db.query(false, ASSIGNMENT_TABLE, COLUMN_TITLES,
" WHERE " + ASSIGNMENT_NAME + " ", null, null, null, " ORDER BY "+ASSIGNMENT_URGENCY_RATING, null);
}
/
public ArrayList<Inputted> getListOfAssignments (SQLiteDatabase db) {
Cursor names = getAssignmentNames(db);
ArrayList<Inputted> assList = new ArrayList<Inputted>();
names.moveToFirst();
while (!cursorsAreAfterLast(names) ) {
int go = 0;
assList.add(new Inputted(names.getString(go))
names.moveToNext();
go++;
}
return assList;
}
/
DBRecordsLayer assignmentRecords = new DBRecordsLayer(this,
"assignment.db", null, 1);
ArrayList<Inputted> assList = DBRecordsLayer.getListOfAssignments(assignmentRecords);
Your code is a bit confusing... In each iteration of the while loop, you are incrementing the cursor (names.moveToNext()); You are also incrementing go.
The result would be:
1st iteration: You are taking the data from the first column of the first query
2nd iteration: You are taking the data from the second column of the second query
etc...
I'm assume that you want to be reading data from the same column of the database for each iteration.
try this:
public ArrayList<Inputted> getListOfAssignments (SQLiteDatabase db) {
Cursor names = getAssignmentNames(db);
ArrayList<Inputted> assList = new ArrayList<Inputted>();
names.moveToFirst();
columnContainingStringToSendToInputtedConstructor = x; //replace the x with column you need from your table
while (!names.isAfterLast()) {
assList.add(new Inputted(names.getString(columnContainingStringToSendToInputtedConstructor));
names.moveToNext();
}
}

An IF statement in a cursor adapter?

private void datafill()
{
Cursor notesCursor = mDbHelper.fetchAllNotes();
startManagingCursor(notesCursor);
/* JournalRowId is the row id from the first database containing all journal names
All notes are kept in database 2. I want only the notes that correspond to each
journal to be listed, KEY_HOMEID is the non visible field that shows where
each note came from.
*
*/
if (editjournalDbAdapter.KEY_HOMEID == journalRowId){
String[] from = new String[]{editjournalDbAdapter.KEY_HEIGHT};
int[] to = new int[]{R.id.detail1};
}
//Error here "from" and "to" are not defined outside of if statement
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.journaldetailrow, notesCursor, from, to);
setListAdapter(notes);
}
"from" and "to" only exist within the scope of the if() statement. Wouldn't make much sense otherwise anyway - even if they did, their contents would be undefined (or, in case of Java, null) and immediately crash your app.
I have no idea what you're trying to accomplish, but you probably want the bottom two statements inside the if() block as well.

Categories