I'm trying to add prepopulated SQLITE tables to my content provider using SQLiteAssetHelper but the uri matcher won't match. I can access/modify the tables through standard SQL but using a cursor loader throws an exception. Here is the relevant code in the content provider/cursor loader.
//Content provider code
private PantryDbHelper dbHelper;
private static final int PANTRY = 1;
private static final int INFO = 5;
public static final String AUTHORITY = "com.battlestarmathematica.stayfresh.pantryprovider";
//path to db
public static final String URL = "content://" + AUTHORITY;
public static final Uri CONTENT_URI = Uri.parse(URL);
public static final Uri CONTENT_URI_PANTRY = Uri.withAppendedPath(CONTENT_URI,"pantry");
public static final Uri CONTENT_URI_INFO = Uri.withAppendedPath(CONTENT_URI,"info");
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"info",INFO);
uriMatcher.addURI(AUTHORITY, "pantry", PANTRY);
}
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){
SQLiteDatabase db = dbHelper.getReadableDatabase();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
switch (uriMatcher.match(uri)) {
case PANTRY:
builder.setTables(PantryContract.PANTRY_TABLE_NAME);
break;
case INFO:
builder.setTables("info");
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
}
//Cursor loader code
public Loader<Cursor> onCreateLoader(int id, Bundle args){
return new CursorLoader(
//context
this,
//content URI
PantryContentProvider.CONTENT_URI_INFO,
//columns to return
new String[] {"_id","itemname"},
//selection
null,
//selection args
null,
//sort order
"itemname");
}
I know the cursor loader works because I use the exact same code for another activity with the pantry uri and it works perfectly. When I try to load it using the info uri though I get this exception.
java.lang.IllegalArgumentException: Unsupported URI content://com.battlestarmathematica.stayfresh.pantryprovider/info
Any help would be greatly appreciated.
You're just missing a break statement in your code.
The UriMatcher matches and the switch statement jumps to case INFO:, but since there is no break; the default: case is executed as well.
Try to replace this
case INFO:
builder.setTables("info");
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
by this:
case INFO:
builder.setTables("info");
break;
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
Related
I am new to Android. I wanted to practice my knowledge about Content Provide, but my app somehow crush and tell me there is an unknown URL content. I try to search for solution but still can't fix the problem...
The logcat message:
2020-05-25 20:10:24.547 15120-15120/com.example.punchinandout E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.punchinandout, PID: 15120
java.lang.IllegalArgumentException: Unknown URL content://com.example.punchinandout.data/time
at android.content.ContentResolver.insert(ContentResolver.java:1535)
at com.example.punchinandout.MainActivity$1.onClick(MainActivity.java:65)
at android.view.View.performClick(View.java:6256)
at android.view.View$PerformClick.run(View.java:24701)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
My provider in Mainifest:
<provider
android:authorities="com.example.punchinandout"
android:name=".data.TimeProvider"
android:exported="false"/>
My Content Provider class:
package com.example.punchinandout.data;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import com.example.punchinandout.data.TimeContract.TimeEntry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class TimeProvider extends ContentProvider {
private static final String TAG = TimeProvider.class.toString();
private static final int TIME = 100;
private static final int TIME_ID = 101;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Determine which content URI we have.
static{
sUriMatcher.addURI(TimeContract.CONTENT_AUTHORITY, TimeContract.CONTENT_PATH, TIME);
sUriMatcher.addURI(TimeContract.CONTENT_AUTHORITY, TimeContract.CONTENT_PATH, TIME_ID);
}
private TimeHelper mDbHelper;
#Override
public boolean onCreate() {
mDbHelper = new TimeHelper(getContext());
return true;
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
SQLiteDatabase database = mDbHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
cursor = database.query(TimeEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
cursor = database.query(TimeEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query with unknown URI: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri); //Set notification URI on cursor, so that the cursor will update when the URI is updated.
return cursor;
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, #Nullable ContentValues values) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return insertTimeRecord(uri, values);
default:
throw new IllegalArgumentException("Insertion is no support for " + uri);
}
}
private Uri insertTimeRecord(Uri uri, ContentValues values){
//Sanity check
if (values.containsKey(TimeEntry.COLUMN_RECORD)){
String time_record = values.getAsString(TimeEntry.COLUMN_RECORD);
if (time_record == null){
throw new IllegalArgumentException("Time record cannot be empty.");
}
}
//If there are no values to update, then don't try to update the database.
if (values.size() == 0){
return null;
}
SQLiteDatabase database = mDbHelper.getWritableDatabase();
long newRowId = database.insert(TimeEntry.TABLE_NAME, null, values);
//Check if the insertion is success.
if (newRowId == -1){
Log.e(TAG, "Insertion fail with " + uri);
return null;
}
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
return ContentUris.withAppendedId(uri, newRowId);
}
#Override
public int update(#NonNull Uri uri, #Nullable ContentValues values, #Nullable String selection, #Nullable String[] selectionArgs) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return updateTimeRecord(uri, values, selection, selectionArgs);
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
return updateTimeRecord(uri, values, selection, selectionArgs);
default:
throw new IllegalArgumentException("Update fail with " + uri);
}
}
private int updateTimeRecord(Uri uri, ContentValues values, String selection, String[] selectionArgs){
//Sanity check
if (values.containsKey(TimeEntry.COLUMN_RECORD)){
String time_record = values.getAsString(TimeEntry.COLUMN_RECORD);
if (time_record == null){
return 0; //If the data is not inputted, update nothing in database.
}
}
//If there are no values to update, then don't try to update the database.
if (values.size() == 0){
return 0;
}
SQLiteDatabase database = mDbHelper.getWritableDatabase();
int rowUpdated = database.update(TimeEntry.TABLE_NAME, values, selection, selectionArgs);
if (rowUpdated != 0){
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
}
return rowUpdated;
}
#Override
public int delete(#NonNull Uri uri, #Nullable String selection, #Nullable String[] selectionArgs) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return deleteTimeRecord(uri, selection, selectionArgs);
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
return deleteTimeRecord(uri, selection, selectionArgs);
default:
throw new IllegalArgumentException("Delete fail with " + uri);
}
}
private int deleteTimeRecord(Uri uri, String selection, String[] selectionArgs){
SQLiteDatabase database = mDbHelper.getWritableDatabase();
int rowDelected = database.delete(TimeEntry.TABLE_NAME, selection, selectionArgs);
if (rowDelected != 0){
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
}
return rowDelected;
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return TimeEntry.CONTENT_LIST_TYPE;
case TIME_ID:
return TimeEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri + " with match " + match);
}
}
}
My Contract class:
package com.example.punchinandout.data;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.BaseColumns;
public class TimeContract {
private TimeContract(){};
public static final String CONTENT_AUTHORITY = "com.example.punchinandout.data";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String CONTENT_PATH = "time";
public static final class TimeEntry implements BaseColumns {
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
//MIME type of all records.
public static final String CONTENT_LIST_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + CONTENT_PATH;
//MIME type of single record.
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + CONTENT_PATH;
public static final String TABLE_NAME = "time";
public static final String _ID = BaseColumns._ID;
public static final String COLUMN_RECORD = "record";
}
}
Can someone help me? :(
I have fixed the problem!!
My provider in Mainifest is that:
<provider
android:authorities="com.example.punchinandout"
android:name=".data.TimeProvider"
android:exported="false"/>
And I change to :
<provider
android:authorities="com.example.punchinandout.data"
android:name=".data.TimeProvider"
android:exported="false"/>
Let the authorities be the same as the authority in Contract class and the problem is fixed!
Sorry for not placed the Mainifest in the question at first time as I had no idea where the problem in my app before.
I have a problem with my Content Provider. I use the content provider in one of my Activities with a CursorLoader and an adapter to show a list.
The list shows the contents of the Task table if and only if i do NOT specify a selection and selectionArgs parameters in the CursorLoader constructor.
I have tried passing in nulls for the selection and selection args and this returns the contents of the whole table. What i am trying to do is select the full projection of the table based on the C_TASK_CALLID column and passing in a callID.
So what i want to do is return a cursor base on some sql like this:
select * from table TASK where C_TASK_CALLID = callID;
Can anyone tell me why i am getting the folloing error?
I have looked at the following SO post, but it indicates there is a mis-match between the selection and selectionArgs. In my case, this is not true as i am passing in only one param.
link
.
I get the following error:
06-01 14:05:56.631 32037-32037/com.carefreegroup.rr3 E/ViewCompletedTasksActivity: about to create CursorLoader.....LoginValidate.C_TASK_CALLID = taskcallid callID = d5f5482f-b240-4eb6-8be1-489a8d75af9b
06-01 14:05:56.636 32037-2691/com.carefreegroup.rr3 E/RR3ContentProvider: inside RR3ContentProvider query method
06-01 14:05:56.636 32037-2691/com.carefreegroup.rr3 E/LoginValidate: NfcScannerApplication.getSecretKey() = 12345
06-01 14:05:56.636 32037-2691/com.carefreegroup.rr3 E/RR3ContentProvider: CASE TASKS
06-01 14:05:56.636 32037-2691/com.carefreegroup.rr3 E/RR3ContentProvider: About to do the query method in content provider
06-01 14:05:56.645 32037-2691/com.carefreegroup.rr3 E/CustomExceptionHandler: stack = java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:762)
Caused by: net.sqlcipher.database.SQLiteException: bind or column index out of range: handle 0x7bf058da88
at net.sqlcipher.database.SQLiteProgram.native_bind_string(Native Method)
at net.sqlcipher.database.SQLiteProgram.bindString(SQLiteProgram.java:245)
at net.sqlcipher.database.SQLiteQuery.bindString(SQLiteQuery.java:183)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:48)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1820)
at net.sqlcipher.database.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)
at net.sqlcipher.database.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:280)
at com.carefreegroup.rr3.RR3ContentProvider.query(RR3ContentProvider.java:396)
at com.carefreegroup.rr3.RR3ContentProvider.query(RR3ContentProvider.java:23)
at android.content.ContentProvider.query(ContentProvider.java:1027)
at android.content.ContentProvider$Transport.query(ContentProvider.java:243)
at android.content.ContentResolver.query(ContentResolver.java:536)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:64)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:56)
at android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:312)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:69)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:66)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:762)
.
Below is my Task table:
public static final String C_ID_TASK_QUEST_COMM = BaseColumns._ID;
public static final String C_TASK_QUEST_COMM_QUESTSION = "taskquestcommquestion";
public static final String C_TASK_QUEST_COMM_COMMENTS = "taskquestcommcomments";
public static final String C_TASK_QUEST_ID = "taskquestid";
public static final String C_TASK_QUEST_TYPE = "taskquesttype";
public static final String C_TASK_CALLID = "taskcallid";
public static final String C_TASK_SENT_TO_SERVER = "tasksenttoserver";
public static final String C_TASK_VALUE = "taskvalue";
.
below is my CursorLoader constructor in my Activity:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.e(TAG, "inside3 onCreateLoader in ViewCompletedTasksActivityAsync");
String[] projection = { LoginValidate.C_ID_TASK_QUEST_COMM, LoginValidate.C_TASK_QUEST_COMM_QUESTSION, LoginValidate.C_TASK_QUEST_COMM_COMMENTS,
LoginValidate.C_TASK_QUEST_ID,
LoginValidate.C_TASK_QUEST_TYPE , LoginValidate.C_TASK_CALLID, LoginValidate.C_TASK_SENT_TO_SERVER, LoginValidate.C_TASK_VALUE};
String [] selectionArgs = {callID};
Log.e(TAG, "about to create CursorLoader.....LoginValidate.C_TASK_CALLID = " + LoginValidate.C_TASK_CALLID + " callID = " + callID);
cursorLoader = new CursorLoader(this, RR3ContentProvider.CONTENT_URI_TASKS, projection, LoginValidate.C_TASK_CALLID, selectionArgs , null);
return cursorLoader;
}
.
Below is my contentProvider:
public class RR3ContentProvider extends ContentProvider {
private static final String TAG = RR3ContentProvider.class.getSimpleName();
NfcScannerApplication nfcAppObj;
static final String PROVIDER_NAME = "com.xxxxx.xxx.ContentProvider";
static final String URLTASKS = "content://" + PROVIDER_NAME + "/tasks";
static final Uri CONTENT_URI_TASKS = Uri.parse(URLTASKS);
static final String _ID_TASKS = "_id";
static final String TASK_QUESTION = "taskquestcommquestion";
static final String TABLETASKS = "tabletaskquestion";
private static HashMap<String, String> TASK_PROJECTION_MAP;
static final int TASKS = 9;
static final int TASK_ID = 10;
static final UriMatcher uriMatcher;
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "tasks", TASKS);
uriMatcher.addURI(PROVIDER_NAME, "tasks/#", TASK_ID);
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
#Override
public boolean onCreate() {
Context applicationContext = getContext().getApplicationContext();
nfcAppObj = getRealApplication(applicationContext);
Log.e(TAG, "inside RR3ContentProvider onCreate");
return (nfcAppObj == null)? false:true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.e(TAG, "inside RR3ContentProvider query method");
db = nfcAppObj.getDb();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (uriMatcher.match(uri)) {
case TASKS:
Log.e(TAG, "CASE TASKS");
qb.setTables(TABLETASKS);
qb.setProjectionMap(TASK_PROJECTION_MAP);
/*if (sortOrder == null || sortOrder == ""){
sortOrder = LOG_CREATED_TIME + " DESC";
}*/
break;
case TASK_ID:
Log.e(TAG, "CASE TASK_ID");
qb.setTables(TABLETASKS);
qb.appendWhere( _ID_TASKS + "=" + uri.getPathSegments().get(1));
/*if (sortOrder == null || sortOrder == ""){
sortOrder = LOG_CREATED_TIME + " DESC";
}*/
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Log.e(TAG, "About to do the query method in content provider");
Cursor c = qb.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
/**
* register to watch a content URI for changes
*/
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}//end of query
#Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
/**
* Get all records
*/
case TASKS:
return "vnd.android.cursor.dir/vnd.example.tasks";
/**
* Get a particular record
*/
case TASK_ID:
return "vnd.android.cursor.item/vnd.example.tasks";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
.
Your sql query error! "select * from table TASK where C_TASK_CALLID = callID;" redundant "table". Change "select * from TASK where C_TASK_CALLID = callID;"
I have the next Content Provider
To insert a row in Table AGENDA, I do:
ContentValues values = new ContentValues(1);
values.put("MSG", "test");
context.getContentResolver().insert(DataProvider.CONTENT_URI_AGENDA, values);
and all works well.
But now, I´d like to use the uri with AGENDA_INSERTWITHCONFLICT to insert a row.
Please, How can I modify the line :
context.getContentResolver().insert(DataProvider.CONTENT_URI_AGENDA, values);
to do it?
Here is my Provider:
public class DataProvider extends ContentProvider {
public static final String TAGPROVIDER = "net.techabout.medappointment.provider";
public static final Uri CONTENT_URI_AGENDA = Uri.parse("content://"+TAGPROVIDER+"/agenda");
public static final String TABLE_AGENDA = "agenda";
private DbHelper dbHelper;
private static final int AGENDA_ALLROWS = 5;
private static final int AGENDA_INSERTWITHCONFLICT=7;
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_ALLROWS);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_INSERTWITHCONFLICT);
}
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
long id;
switch (uriMatcher.match(uri)) {
case AGENDA_ALLROWS:
id = db.insertOrThrow(TABLE_AGENDA, null, values);
break;
case AGENDA_INSERTWITHCONFLICT:
id=db.insertWithOnConflict(TABLE_AGENDA, BaseColumns._ID, values, SQLiteDatabase.CONFLICT_REPLACE);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Uri insertUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(insertUri, null);
return insertUri;
}
}
make following changes, Please use naming convetions as required.
// content provider
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_ALLROWS);
uriMatcher.addURI(TAGPROVIDER, "agenda_insert_conflicts", AGENDA_INSERTWITHCONFLICT);
}
calling mechanism
String URL = "net.techabout.medappointment.provider/agenda_insert_conflicts";
Uri uri = Uri.parse(URL);
context.getContentResolver().insert(uri , values);
I have setup a fragment to pull data from a custom content provider using a CursorLoader.
The problem is that when i update a record in the SQLite table using the content resolver, the cursor does not refresh i.e. the getContext().getContentResolver().notifyChange(myUri, null) has no effect. I have to exit the fragment and open it again to see the change.
I think the problem is that the URI i have used to update a row is not being observed by the loader :
URI to create loader -content://com.myapp.provider/MyTable/Set/22
URI to update row -content://com.myapp.provider/MyTable/167
167 identifies a unique row in the table. 22 identifies a set of rows in the table. Is there some way to tell the loader that the row 167 comes within the set 22, so it should reset the cursor?
Here is the code in case it brings more clarity :
Creating CursorLoader in Fragment :
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle queryBundle) {
CursorLoader cursorLoader = new CursorLoader(getActivity(), Uri.parse("content://com.myapp.provider/MyTable/Set/22"), myProjection, null, null, null);
return cursorLoader;
}
on button click in fragment :
mContext.getContentResolver().update("content://com.myapp.provider/MyTable/167", values, null, null);
Content Provider class :
private static final String AUTHORITY = "com.myapp.provider";
private static final String TABLE_PATH = "MyTable";
public static final String CONTENT_URI_BASEPATH = "content://" + AUTHORITY + "/" + TABLE_PATH;
private static final int URITYPE_TABLE = 1;
private static final int URITYPE_SINGLE_SET = 2;
private static final int URITYPE_SINGLE_ROW = 3;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static{
sUriMatcher.addURI(AUTHORITY, TABLE_PATH,URITYPE_TABLE);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#", URITYPE_SINGLE_SET);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/#", URITYPE_SINGLE_ROW);
}
#Override
public int update(Uri myUri, ContentValues values, String selection, String[] selectionArgs){
int rowCount = 0;
String id;
SQLiteDatabase db = localDB.getWritableDatabase();
int uriType = sUriMatcher.match(myUri);
switch(uriType){
case URITYPE_SINGLE_ROW :
id = uri.getLastPathSegment();
//selection and selectionArgs are ignored since the URI itself identifies a unique row.
rowCount = db.update(MyTable.TABLE_NAME, values, MyTable.COLUMN_ID + " = ?", new String[] {id});
}
getContext().getContentResolver().notifyChange(myUri, null);
return rowCount;
}
I has a similar issue and found the solution here.
In brief, it turned out I needed to call setNotificationUri(ContentResolver cr, Uri uri) on the cursor returned by the query() method of my content provider.
The solution is to call notifyChange() on the Uri that is being observed i.e. the set and not on the row.
To achieve this, we need to make some changes :
Include the set ID in the URI when calling the update :
mContext.getContentResolver().update("content://com.myapp.provider/MyTable/Set/22/167", values, null, null);
Change the URI pattern of a single row from "/#" to "/Set/#/#"
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static{
sUriMatcher.addURI(AUTHORITY, TABLE_PATH,URITYPE_TABLE);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#", URITYPE_SINGLE_SET);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#/#", URITYPE_SINGLE_ROW);
}
Then in the update function, construct a new Uri that has to be notified :
List<String> pathSegments = uri.getPathSegments();
String mySetID = pathSegments.get(2);
Uri mySetUri = Uri.parse("content://" + AUTHORITY + "/" + TABLE_PATH + "/Set/" + mySetID);
getContext().getContentResolver().notifyChange(mySetUri, null);
This was working! I'm not sure what changed. I do use git and I have looked over my commits and the code for hours now.
As the title indicates my uri matcher stopped matching.
Below I have pasted the relevant parts of my content provider.
public class Provider extends ContentProvider {
private static final String TAG = "Provider";
private static final String SCHEME = ContentResolver.SCHEME_CONTENT;
private static final String AUTHORITY = "com.snot.bodyweightworkout.database.provider";
private static final String BASE_URI = SCHEME + AUTHORITY;
public static final Uri URI_EXERCISES = Uri.parse(BASE_URI + "/exercise");
public static final Uri URI_PROGRAMS = Uri.parse(BASE_URI + "/program");
public static final Uri URI_PROGRAM_EXERCISES = Uri.parse(BASE_URI + "/program/#/exercise");
private static final int EXERCISE = 1;
private static final int EXERCISES = 2;
private static final int PROGRAM = 3;
private static final int PROGRAMS = 4;
private static final int PROGRAM_EXERCISES = 5;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static
{
sURIMatcher.addURI(AUTHORITY, "/exercise", EXERCISES);
sURIMatcher.addURI(AUTHORITY, "/exercise/#", EXERCISE);
sURIMatcher.addURI(AUTHORITY, "/program", PROGRAMS);
sURIMatcher.addURI(AUTHORITY, "/program/#", PROGRAM);
sURIMatcher.addURI(AUTHORITY, "/program/#/exercise", PROGRAM_EXERCISES);
}
...
And then the part where the actual matching should take place.
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.v(TAG, "URI: " + uri);
Cursor result = null;
int match = sURIMatcher.match(uri);
switch(match)
{
case PROGRAMS:
result = DatabaseHandler
.getInstance(getContext())
.getReadableDatabase()
.query(Program.TABLE_NAME, Program.FIELDS, null, null, null, null, null, null);
result.setNotificationUri(getContext().getContentResolver(), URI_PROGRAMS);
break;
case PROGRAM:
final long pid = Long.parseLong(uri.getLastPathSegment());
result = DatabaseHandler
.getInstance(getContext())
.getReadableDatabase()
.query(Program.TABLE_NAME, Program.FIELDS,
Program.COL_ID + " IS ?",
new String[] { String.valueOf(pid) }, null, null, null, null);
result.setNotificationUri(getContext().getContentResolver(), URI_PROGRAMS);
break;
...
default:
throw new UnsupportedOperationException("Unmatched(" + match + ") URI: " + uri.toString());
I'm trying to query using a cursor loader like this:
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), Provider.URI_PROGRAMS, Program.FIELDS, null, null, null);
}
Everytime default is hit. Not match is made the I end up with a FC and the following line in my log.
E/AndroidRuntime( 1979): Caused by: java.lang.UnsupportedOperationException: Unmatched(-1) URI: content://com.snot.bodyweightworkout.database.provider/program
I've been starring at this for hours and I really need some fresh eyes to take a peak at it. So if some kind soul could take a look at it I will appreciate it very much. Thanks in advance.
There's no need for the starting /, so define your addUri() method calls like this:
static {
sURIMatcher.addURI(AUTHORITY, "exercise", EXERCISES);
sURIMatcher.addURI(AUTHORITY, "exercise/#", EXERCISE);
sURIMatcher.addURI(AUTHORITY, "program", PROGRAMS);
sURIMatcher.addURI(AUTHORITY, "program/#", PROGRAM);
sURIMatcher.addURI(AUTHORITY, "program/#/exercise", PROGRAM_EXERCISES);
}