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);
Related
I'm trying to share some data between my apps using ContentProvider. I have a problem with accessing the data with resolver in the second app: Cursor object is null after query. Can't figure out where I made a mistake in the code so I'm asking for help. Code of the first app which provides the data :
Provider in manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.piotr.planer">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".BroadcastEvent" />
<provider
android:authorities="com.example.piotr.planer.PlansProvider"
android:name=".PlansProvider"
android:exported="true"
android:multiprocess="true" >
</provider>
</application>
Provider class:
package com.example.piotr.planer;
public class PlansProvider extends ContentProvider {
// Unique namespace for provider
static final String PROVIDER_NAME = "com.example.piotr.planer.PlansProvider";
static final String URL = "content://" + PROVIDER_NAME + "/cpplans";
static final Uri CONTENT_URL = Uri.parse(URL);
// Defining columns in database
static final String id = "id";
static final String name = "name";
static final String date = "date";
static final int uriCode = 1;
private static HashMap<String, String> values;
// UriMatcher matches our unique Uris with our ContentProviders
static final UriMatcher uriMatcher;
// assigning these unique Uris
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "cpplans", uriCode);
}
// Defining database
private SQLiteDatabase sqlDB;
static final String DATABASE_NAME = "myPlans";
static final String TABLE_NAME = "plans";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE = "CREATE TABLE " + TABLE_NAME +
" (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" name TEXT NOT NULL, " +
" date INTEGER);";
#Override
public boolean onCreate() {
// Creates Database
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
sqlDB = dbHelper.getWritableDatabase();
if (sqlDB != null) {
return true;
}
return false;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(TABLE_NAME);
switch (uriMatcher.match(uri)) {
case uriCode:
queryBuilder.setProjectionMap(values);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Cursor cursor = queryBuilder.query(sqlDB, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
#Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case uriCode:
return "vnd.android.cursor.dir/cpplans";
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
}
}
#Override
public Uri insert(Uri uri, ContentValues values) {
long rowID = sqlDB.insert(TABLE_NAME, null, values);
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URL, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return uri;
} else {
Toast.makeText(getContext(), "Row insert failed", Toast.LENGTH_SHORT).show();
return null;
}
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int rowsDeleted = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
rowsDeleted = sqlDB.delete(TABLE_NAME, 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 rowsUpdated = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
rowsUpdated = sqlDB.update(TABLE_NAME, values, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase sqlDB) {
sqlDB.execSQL(CREATE_DB_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase sqlDB, int oldVersion, int newVersion) {
sqlDB.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(sqlDB);
}
}
}
The addPlanToProviderFunction() method:
private void addPlanToProvider(long timeInMillis, String eventName) {
ContentValues values = new ContentValues();
values.put(PlansProvider.name, eventName);
values.put(PlansProvider.date, timeInMillis);
Uri uri = mContext.getContentResolver().insert(PlansProvider.CONTENT_URL, values);
}
The class in the second app where I want to get the data:
package com.example.piotr.zadaniowiec;
public class PlanList extends AppCompatActivity {
static final Uri CONTENT_URL = Uri.parse("content://com.example.piotr.planer.PlansProvider.cpplans");
ContentResolver resolver;
TextView planListTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_plans_list);
planListTextView = findViewById(R.id.plan_list_text_view);
resolver = getContentResolver();
getPlans();
}
private void getPlans() {
String[] projection = new String[]{"id", "name", "date"};
Cursor cursor = resolver.query(CONTENT_URL, projection, null, null, null);
String planList = "";
if (cursor.moveToNext()) { // I get an error here
do {
String id = cursor.getString(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
long date = cursor.getLong(cursor.getColumnIndex("date"));
planList = planList + id + name + date + "\n";
} while (cursor.moveToNext());
}
planListTextView.setText(planList);
}
}
I get :
"NullPointerException: Attempt to invoke interface method 'boolean android.database.Cursor.moveToNext()' on a null object reference."
Thanks anyone for the help.
Your CONTENT_URL is in the wrong format - you need a / after the authority, not a .
static final Uri CONTENT_URL =
Uri.parse("content://com.example.piotr.planer.PlansProvider/cpplans");
As it is now, you're looking for the root path for a ContentProvider that has an authority of com.example.piotr.planer.PlansProvider.cpplans, which doesn't exist, hence the null Cursor.
I have a Content Provider that can create one type of database. My database contains 5 columns and one table, and with my program, I want to be able to create a new table with the same columns as my first table. How can I change my Content Provider to do this ? I need to mach some uri, but how can I do this will I am in my program ?
My columns are :
_id, THEME, QUESTION, REPONSE, DIFFICULTE
Here is my Content Provider :
public class CardContentProvider extends ContentProvider {
private StockCard stock;
public static String authority = "com.example.jean.cartememoire.CardContentProvider";
private static String path ="Cartes_table";
public static final String _ID = "_id";
public static final String THEME = "THEME";
public static final String QUESTION = "QUESTION";
public static final String REPONSE = "REPONSE";
public static final String DIFFICULTE = "DIFFICULTE"; //# = un chiffre
public static final String STOCK_TABLE = "Cartes_table";
private static final int ID_STOCK_TABLE = 1;
private static final int ID_THEME = 2;
private static final int ID_QUESTION = 3;
private static final int ID_REPONSE = 4;
private static final int ID_DIFFICULTE = 5;
private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
matcher.addURI(authority, STOCK_TABLE, ID_STOCK_TABLE);
matcher.addURI(authority, STOCK_TABLE+"/*", ID_STOCK_TABLE);
}
#Override
public boolean onCreate() {
stock = StockCard.getInstance(getContext());
return true;
}
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = stock.getReadableDatabase();
int code = matcher.match(uri);
long id;
Cursor cursor;
switch (code)
{
case ID_STOCK_TABLE:
cursor = db.query(STOCK_TABLE, projection, selection,
selectionArgs, null, null, sortOrder);
break;
default:
Log.d("Uri provider =", uri.toString());
throw new UnsupportedOperationException("Pas encore implémenté");
}
return cursor;
}
#Nullable
#Override
public String getType(Uri uri) {
return null;
}
#Nullable
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = stock.getWritableDatabase();
int code = matcher.match(uri);
long id;
Uri.Builder builder = new Uri.Builder();
switch(code)
{
case ID_STOCK_TABLE:
System.out.println(values.toString());
id = db.insert(STOCK_TABLE, null, values);
builder.appendPath(STOCK_TABLE);
break;
default:
throw new UnsupportedOperationException("Pas encore implémenté");
}
builder.authority(authority);
builder = ContentUris.appendId(builder, id);
return builder.build();
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = stock.getWritableDatabase();
int code = matcher.match(uri);
int i;
switch(code)
{
case ID_STOCK_TABLE:
long id = ContentUris.parseId(uri);
i = db.delete(STOCK_TABLE, "_id=" + id, null);
break;
default:
throw new UnsupportedOperationException("Pas encore implémenté");
}
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
Thank you guys !
EDIT : I said rows instead of columns...
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);
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);
}