Weird thing is my content provider doesn't delete the row I've asked to. As far as I see it should work and I don't understand why it doesn't.
This is the delete method in my provider:
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = helper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
String rowID = uri.getPathSegments().get(1);
selection = KEY_ROWID
+ "="
+ rowID
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : "");
Log.i("Selection", "" + selection);
break;
case ALLROWS:
Log.i("Deleted", "All rows");
break;
default:
Log.i("Switch case", "default value");
break;
}
if (selection == null) {
selection = "1";
}
int deleteCount = db.delete(helper.DATABASE_TABLE, selection,
selectionArgs);
Log.i("Deleted rows",
"" + db.delete(helper.DATABASE_TABLE, selection, selectionArgs));
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
Here is the Log output which indicates no row was deleted which makes no sense to me.
can anyone help?
03-24 22:36:34.809: I/Selection(27648): _id=2
**03-24 22:36:34.809: I/Deleted rows(27648): 0**
03-24 22:36:34.814: I/Delete Uri(27648): content://com.shifts.provider/shiftsTEST/2
I've found the issue myself - I've refactored the method like this:
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = helper.getWritableDatabase();
int deleteCount = db.delete(helper.DATABASE_TABLE, KEY_ID + "="
+ Integer.valueOf(selection), null);
Log.i("Selection", "" + selection);
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
Related
To query joins of table for cursorloader, i use ContentProvider at the bottom, however it is inside view pager and thus often recreates. This take considerable time to generate new table join.
Is it possible to create permanent join table, which will be automaticly updated its data, when source table get data update?
public class ContactsWithCategorisProvider extends ContentProvider {
private MySQLiteHelper database;
// used for the UriMacher
private static String TABLE_NAME = buildTableName();
private static String TABLE_ID = ContactsTable._ID;
private static String AUTHORITY = "app.ContactsWithCategorisProvider";
private static final int ITEMS = 10;
private static final int ITEM_ID = 20;
private static final String BASE_PATH = "app";
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static String buildTableName() {
StringBuilder sb = new StringBuilder();
sb.append(ContactsTable.TABLE_NAME);
sb.append(" LEFT OUTER JOIN ");
sb.append(ContactsCategoriesTable.TABLE_NAME);
sb.append(" ON(");
sb.append(ContactsCategoriesTable.CONTACT_ID);
sb.append(" = ");
sb.append(ContactsTable.CONTACT_ID);
sb.append(" AND ");
sb.append(ContactsCategoriesTable.CURRENT_USER);
sb.append(" = ");
sb.append(ContactsTable.CURRENT_USER);
sb.append(") ");
return sb.toString();
}
static {
sURIMatcher.addURI(AUTHORITY, BASE_PATH, ITEMS);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", ITEM_ID);
}
#Override public boolean onCreate() {
database = new MySQLiteHelper(getContext());
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Uisng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// check if the caller has requested a column which does not exists
// checkColumns(projection);
queryBuilder.setTables(TABLE_NAME);
if (projection == null || projection.length == 0) {
projection = new String[] {
ContactsTable.TABLE_NAME + "." + ContactsTable._ID + " AS " + ContactsTable._ID,
ContactsTable.NAME, ContactsTable.NAME_SHORT,ContactsTable.NUMBER, ContactsTable.NOTE,
ContactsTable.CATEGORIES, ContactsCategoriesTable.NAME, ContactsTable.CONTACT_ID,ContactsCategoriesTable.CATALOG_ID
};
}
if (TextUtils.isEmpty(sortOrder)) {
sortOrder =
ContactsCategoriesTable.NAME + " = 'UNSORTED' DESC, " + ContactsCategoriesTable.NAME
+ " ASC, " + ContactsTable.NAME + " ASC ";
}
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case ITEMS:
break;
case ITEM_ID:
// adding the _ID to the original query
queryBuilder.appendWhere(TABLE_ID + "=" + uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
SQLiteDatabase db = database.getWritableDatabase();
Cursor cursor =
queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
// make sure that potential listeners are getting notified
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
#Override public String getType(Uri uri) {
return null;
}
#Override public Uri insert(Uri uri, ContentValues values) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
long id;
switch (uriType) {
case ITEMS:
id = sqlDB.insert(TABLE_NAME, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + "/" + id);
}
public void insert(Uri uri, ArrayList<ContentValues> values) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
switch (uriType) {
case ITEMS:
for (ContentValues c : values) {
sqlDB.insert(TABLE_NAME, null, c);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
}
#Override public int delete(Uri uri, String selection, String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsDeleted = 0;
switch (uriType) {
case ITEMS:
rowsDeleted = sqlDB.delete(TABLE_NAME, selection, selectionArgs);
break;
case ITEM_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(TABLE_NAME, TABLE_ID + "=" + id, null);
} else {
rowsDeleted =
sqlDB.delete(TABLE_NAME, TABLE_ID + "=" + id + " and " + selection, selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsDeleted;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsUpdated = 0;
switch (uriType) {
case ITEMS:
rowsUpdated = sqlDB.update(TABLE_NAME, values, selection, selectionArgs);
break;
case ITEM_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(TABLE_NAME, values, TABLE_ID + "=" + id, null);
} else {
rowsUpdated = sqlDB.update(TABLE_NAME, values, TABLE_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
}
I am using Loader to get data from db and I have some trouble whit distinct, in simple words - distinct doesn't work. here is my code:
private String[] CONTACTS_COLUMNS ={
"DISTINCT " + ContactsEntry.CONTACT_ID + " AS _id",
ContactsEntry.CONTACT_FROM,
ContactsEntry.CONTACT_NAME,
CardsPhonesEntry.CONTACT_PHONE,
CardsPhonesEntry.CARD_NUMBER
};
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(),
ContactsEntry.CONTENT_URI,
CONTACTS_COLUMNS,
mSelection, null, null);
}
here is some code from my contentProvider:
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor retCursor;
int match = sUriMatcher.match(uri);
switch (match){
case PEOPLE:
retCursor = getAllContacts(projection, selection, selectionArgs, sortOrder);
break;
default:
throw new UnsupportedOperationException("Unknown uri in query(): " + uri);
}
return retCursor;
}
private Cursor getAllContacts(String[] projection, String selection, String[] selectionArgs,
String sortOrder){
return sPeopleWithCardsAndPhones.query(
mDbHelper.getReadableDatabase(),
projection,
selection,
selectionArgs,
null, null,
sortOrder
);
}
static {
sPeopleWithCardsAndPhones = new SQLiteQueryBuilder();
sPeopleWithCardsAndPhones.setTables(
ContactsEntry.TABLE_NAME + " LEFT JOIN " + CardsPhonesEntry.TABLE_NAME + " ON " +
ContactsEntry.TABLE_NAME + "." + ContactsEntry.CONTACT_ID + " = " +
CardsPhonesEntry.TABLE_NAME + "." + CardsPhonesEntry.OWNER_ID
);
}
what i am doing wrong and why distinct doesn't want to work?
EDIT:
For example: I have 1 contact in first table, and in the second table there are 2 cards linked to this contact by contact_id;
I need to show list with only 1 item (first contact) without depending how much cards it has. and now when init loader - it shows me this contact twice, but not once.
I have a listview with some elements that are saved on a database and I access them using a custom content provider.
In my main activity, I have implemented a ResourceCursorAdapter.
When I long click on a element of the list, I get a context menu where I have the edit-update options.
The edit option starts other activity with some edittexts where I should be able to update the selected item's values (I use this activity too to create a new item, and I do this right).
The problem I'm getting is that I'm not getting the items updated nor deleted, so i think that I'm not mannaging right the ID to access the database. This is my code:
Custom Content Provider - Update and delete methods
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
database = mDbHelper.getWritableDatabase();
int match = mUriMatcher.match(uri);
switch (match){
case URI_TRAVELS:
//nada
break;
case URI_TRAVEL_ITEM:
String id = uri.getPathSegments().get(1);
count = database.update(TravelsDatabaseHelper.TABLE_NAME, values, Travels._ID +
" = " + id + (!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
database = mDbHelper.getWritableDatabase();
int match = mUriMatcher.match(uri);
switch (match){
case URI_TRAVELS:
//nada
break;
case URI_TRAVEL_ITEM:
String id = uri.getPathSegments().get(1);
count = database.delete(TravelsDatabaseHelper.TABLE_NAME, Travels._ID + " = " + id +
(!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
Main Activity - Update and delete methods
public void updateTravel(String city, String country, int year, String note, String id){
ContentValues updateValues = new ContentValues();
updateValues.put(Travels.CITY, city);
updateValues.put(Travels.COUNTRY, country);
updateValues.put(Travels.YEAR, year);
updateValues.put(Travels.NOTE, note);
getContentResolver().update(TravelsProvider.CONTENT_URI, updateValues, Travels._ID+"="+id, null);
}
private void deleteTravel(String id){
/**Accede a la funcion delete() del Content Provider*/
getContentResolver().delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);
}
Main Activity - Context menus where I call delete and update methods
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo menu_info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int itemPos = menu_info.position;
Cursor cursor = mAdapter.getCursor();
cursor.moveToPosition(itemPos);
switch (item.getItemId()) {
case R.id.edit_travel:
Intent intent = new Intent(this, EditTravelActivity.class);
intent.putExtra(TravelActivity.EXTRA_ID, cursor.getString(cursor.getColumnIndex(Travels._ID)));
startActivityForResult(intent, REQUEST_CODE_UPDATE_TRAVEL);
return true;
case R.id.delete_travel:
String ids = cursor.getString(cursor.getColumnIndex(Travels._ID));
deleteTravel(ids);
return true;
default:
return super.onContextItemSelected(item);
}
}
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
//...
case REQUEST_CODE_UPDATE_TRAVEL:
String ucity = data.getExtras().getString(TravelActivity.EXTRA_CITY);
String ucountry = data.getExtras().getString(TravelActivity.EXTRA_COUNTRY);
int uyear = data.getExtras().getInt(TravelActivity.EXTRA_YEAR);
String unote = data.getExtras().getString(TravelActivity.EXTRA_NOTE);
String uid = data.getExtras().getString(TravelActivity.EXTRA_ID);
updateTravel(ucity, ucountry, uyear, unote, uid);
break;
}
}
}
UPDATE - According to NigelK's answer
This is my "UriMatcher" and other relevant definitions to take care:
private static final String AUTHORITY = "com.example.travellist";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/travels");
private static final int URI_TRAVELS = 1;
private static final int URI_TRAVEL_ITEM = 2;
private static final UriMatcher mUriMatcher;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "travels", URI_TRAVELS);
mUriMatcher.addURI(AUTHORITY, "travels/#", URI_TRAVEL_ITEM);
}
I don't need to do it in booth ways, I'm learning about this so just doing it in the best way is enough for me to learn.
According to your answer, I've tryed doing it the 2 ways:
private void deleteTravel(long id){
/*METHOD 1*/
getContentResolver().delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);
/*METHOD 2*/
Uri uri = ContentUris.withAppendedId(TravelsProvider.CONTENT_URI, id);
getContentResolver().delete(uri, null, null);
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
//...
switch (match){
case URI_TRAVELS:
//nada
break;
case URI_TRAVEL_ITEM:
/*METHOD 1*/
count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
break;
/*METHOD 2*/
String rowId = uri.getPathSegments().get(1);
selection = Travels._ID +" = "+ rowId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
break;
//...
}
In the first way, it continues whithout deleting the item. In the second way, in the line of String rowId = uri.getPathSegments().get(1); throws me this error: Unreachable code!
Most Content Providers include a shortcut URI pattern that allows you to address a particular row by appending a row ID to the content URI. Where you do the following, you are providing the selection "WHERE _id = id", which is perfectly valid:
.delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);
The uri above will be something like: content://com.package.provider/content
The short cut, which applies the action to just the id appended to the uri, is of the form:
uri = ContentUris.withAppendedId(TravelsProvider.CONTENT_URI, id);
.delete(uri, null, null);
The uri above will now be something like: content://com.package.provider/content/1234
The problem you have is that in your delete() and update() methods, you are trying to deal with the 2nd form, not the 1st (which is the URI form you're using in the call). Expand your uriMatcher to tell the difference, something like:
uriMatcher.addURI("your.package.provider", "content", URI_TRAVEL_ITEM);
uriMatcher.addURI("your.package.provider", "content/#", URI_TRAVEL_ITEM_ID);
In delete(), and similarly for update():
switch (match){
case URI_TRAVELS:
//nada
break;
case URI_TRAVEL_ITEM:
count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
break;
case URI_TRAVEL_ITEM_ID:
String rowID = uri.getPathSegments().get(1);
selection = Travels._ID + "=" + rowID
+ (!TextUtils.isEmpty(selection) ?
" AND (" + selection + ')' : "");
count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
So where there is no id appended to the URI (it matches URI_TRAVEL_ITEM) it is a straightforward delete based upon the selection and arguments.
Where there is an id appended to the URI (it matches URI_TRAVEL_ITEM_ID), get the id from the URI using getPathSegments().get(1) and use the value to augment the selection passed in (so it applies to just that row) before doing the delete.
You've then got both types of URI covered and the rest of your code should (I hope) work.
So I'm getting an unreachable statement in the following switch statement. I can't figure out why this is happening, any help would be much appreciated.
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = psHelper.getWritableDatabase();
int delCount = 0;
return 0;
switch (URI_MATCHER.match(uri)) {
case PRODUCT_LIST:
// THE LINE BELOW IS UNREACHABLE
delCount = db.delete(TABLE_NAME_PRODUCTS, selection, selectionArgs);
break;
case PRODUCT_ID:
String idStr = uri.getLastPathSegment();
String where = PSContract.Products.ID_COLUMN + " = " + idStr;
if (!TextUtils.isEmpty(selection)) {
where += " AND " + selection;
}
delCount = db.delete(TABLE_NAME_PRODUCTS, where, selectionArgs);
break;
case SUPPLIER_LIST:
delCount = db.delete(TABLE_NAME_SUPPLIERS, selection, selectionArgs);
break;
case SUPPLIER_ID:
String idStr2 = uri.getLastPathSegment();
String where2 = PSContract.Products.ID_COLUMN + " = " + idStr2;
if (!TextUtils.isEmpty(selection)) {
where2 += " AND " + selection;
}
delCount = db.delete(TABLE_NAME_SUPPLIERS, where2, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return delCount;
}
You're returning 0 right before the switch. There is no way that the switch can be reached if you return from the method right before, so the error is generated.
Under int delCount = 0 you are returning 0 which is going end the function call. Since that return is in a place where it will happen 100% of the time nothing below it will ever execute. Looks like a typo to me.
I have looked at this for a couple of days now and I completely can't work out why my content provider return 0 using the arguments I am passing it.
Here's my contentResolver code:
String[] expenditureProjection = {
BusinessOpsDatabase.COL_EXPEND_CAT_ID,
BusinessOpsDatabase.COL_EXPEND_DATE,
BusinessOpsDatabase.COL_EXPEND_AMOUNT,
BusinessOpsDatabase.COL_EXPEND_DESC,
BusinessOpsDatabase.COL_STERLING_EXCHANGE,
BusinessOpsDatabase.COL_COMPANY_ID,
BusinessOpsDatabase.CURRENCY_ID,
BusinessOpsDatabase.COL_MOD_DATE
};
// Defines a string to contain the selection clause
String selectionClause = null;
// An array to contain selection arguments
String[] selectionArgs = {expend_id.trim()};
selectionClause = BusinessOpsExpenditureProvider.EXPENDITURE_ID + "=?";
Log.d(TAG, expend_id+" Selected from list.");
Cursor expendCursor = getContentResolver().query(
BusinessOpsExpenditureProvider.CONTENT_URI, expenditureProjection, selectionClause, selectionArgs, null);
if (null == expendCursor) {
Log.d(TAG, "Expenditure cursor: Is null");
} else if (expendCursor.getCount() < 1) {
Log.d(TAG,"Expenditure cursor: Search was unsuccessful: "+expendCursor.getCount());
} else {
Log.d(TAG,"Expenditure cursor: Contains results");
int i=0;
expendCursor.moveToFirst();
// loop through cursor and populate country array
while (expendCursor.isAfterLast() == false)
{
expend_date_edit.setText(expendCursor.getString(1));
expend_amount_edit.setText(expendCursor.getString(3));
expend_desc_edit.setText(expendCursor.getString(4));
i++;
expendCursor.moveToNext();
}
}
Here's my content provider query method:
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mDB.getWritableDatabase();
// A convenience class to help build the query
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(BusinessOpsDatabase.TABLE_EXPENDITURE);
switch (sURIMatcher.match(uri)) {
case EXPENDITURE:
if(selection != null && selectionArgs != null){
//values.get("company_contact");
String segment = uri.getLastPathSegment();
Log.d(TAG, "Last path segment: "+ segment);
String whereClause = BusinessOpsDatabase.EXPENDITURE_ID + "="+ selectionArgs[0];
Log.d(TAG, "Where clause: "+whereClause);
}
break;
case EXPENDITURE_ID:
// If this is a request for an individual status, limit the result set to that ID
qb.appendWhere(BusinessOpsDatabase.EXPENDITURE_ID + "=" + uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
// Query the underlying database
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
// Notify the context's ContentResolver if the cursor result set changes
c.setNotificationUri(getContext().getContentResolver(), uri);
// Return the cursor to the result set
return c;
}
I'm printing the whereclause to the log and I see '_id=3' which should be fine because I have pulled off a copy of my SQLite database and I can see that the expenditure table has an _id 3 row in it. Any Ideas?
What an epic problem this has been. I found the error in my ContentResolver code.
selectionClause = BusinessOpsExpenditureProvider.EXPENDITURE_ID + "=?";
I was using the EXPENDITURE_ID variable from the provider rather than the database class. The line now reads.
selectionClause = BusinessOpsDatabase.EXPENDITURE_ID + "=?";
And works!