Android Content Provider with multiple tables unknow uri error - android

I followed a tutorial about sqlite and content providers but it had only examples for a single table, I eventually managed to figure how to expand that provider to include two tables and everything seems to be working fine.
The app I'm trying to make as a self excercise has right now a todo mode and a notes mode, the classes are almost iedntical, the only difference is a few database column entries.
The todo mode (which was in the example) works perfectly, I can create new entries, view and delete them, while the notes mode can create and delete entries but for some bizarre reason it can't open them as it gives me an exception about the uri not being recognized.
This is the error I'm getting:
02-28 10:08:40.343: E/AndroidRuntime(20079): FATAL EXCEPTION: main
02-28 10:08:40.343: E/AndroidRuntime(20079): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.jackomel.maddonote/com.jackomel.maddonote.NotesDetailActivity}: java.lang.IllegalArgumentException: Unknown URI: content://com.jackomel.maddonote.contentprovider/maddonote/1
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2304)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2354)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread.access$600(ActivityThread.java:150)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1244)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.os.Handler.dispatchMessage(Handler.java:99)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.os.Looper.loop(Looper.java:137)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread.main(ActivityThread.java:5191)
02-28 10:08:40.343: E/AndroidRuntime(20079): at java.lang.reflect.Method.invokeNative(Native Method)
02-28 10:08:40.343: E/AndroidRuntime(20079): at java.lang.reflect.Method.invoke(Method.java:511)
02-28 10:08:40.343: E/AndroidRuntime(20079): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
02-28 10:08:40.343: E/AndroidRuntime(20079): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
02-28 10:08:40.343: E/AndroidRuntime(20079): at dalvik.system.NativeStart.main(Native Method)
02-28 10:08:40.343: E/AndroidRuntime(20079): Caused by: java.lang.IllegalArgumentException: Unknown URI: content://com.jackomel.maddonote.contentprovider/maddonote/1
02-28 10:08:40.343: E/AndroidRuntime(20079): at com.jackomel.maddonote.contentprovider.MyTodoContentProvider.query(MyTodoContentProvider.java:103)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.content.ContentProvider.query(ContentProvider.java:652)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.content.ContentProvider$Transport.query(ContentProvider.java:189)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.content.ContentResolver.query(ContentResolver.java:372)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.content.ContentResolver.query(ContentResolver.java:315)
02-28 10:08:40.343: E/AndroidRuntime(20079): at com.jackomel.maddonote.NotesDetailActivity.fillData(NotesDetailActivity.java:102)
02-28 10:08:40.343: E/AndroidRuntime(20079): at com.jackomel.maddonote.NotesDetailActivity.onCreate(NotesDetailActivity.java:56)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.Activity.performCreate(Activity.java:5104)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
02-28 10:08:40.343: E/AndroidRuntime(20079): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2258)
02-28 10:08:40.343: E/AndroidRuntime(20079): ... 11 more
This is the content provider I have right now (skipping posting all the imports)
public class MyMaddoContentProvider extends ContentProvider {
// Database
private MaddoDatabaseHelper database;
// Used for urlMacher
private static final int TODOS = 10;
private static final int TODO_ID = 20;
private static final int NOTES = 11;
private static final int NOTE_ID = 21;
private static final String AUTHORITY = "com.jackomel.maddonote.contentprovider";
private static final String BASE_PATH = "maddo";
//private static final String NOTE_PATH = "notes";
public static final Uri TODO_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH + "todo");
public static final Uri NOTE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH + "note");
public static final String CONTENT_TYPE_TODO = ContentResolver.CURSOR_DIR_BASE_TYPE + "/todos";
public static final String CONTENT_ITEM_TYPE_TODO = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/todo";
public static final String CONTENT_TYPE_NOTE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/notes";
public static final String CONTENT_ITEM_TYPE_NOTE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/note";
public static final String[] todoAvailable = { TodoTable.COLUMN_CATEGORY,
TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_DESCRIPTION,
TodoTable.COLUMN_ID };
public static final String[] noteAvailable = { NotesTable.COLUMN_CATEGORY,
NotesTable.COLUMN_SUMMARY, NotesTable.COLUMN_CONTENT, NotesTable.COLUMN_DATE,
TodoTable.COLUMN_ID
};
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "todo", TODOS);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "todo" + "/#", TODO_ID);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "note", NOTES);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "note" + "/#", NOTE_ID);
}
#Override
public boolean onCreate() {
database = new MaddoDatabaseHelper(getContext());
return false;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Using SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// Check if the caller has requested a column which does not exists
//checkColumns(projection);
// Set the table
//queryBuilder.setTables(TodoTable.TABLE_NAME);
int uriType = sURIMatcher.match(uri);
checkColumns(projection, uriType);
switch (uriType) {
case TODOS:
queryBuilder.setTables(TodoTable.TABLE_NAME);
break;
case TODO_ID:
queryBuilder.setTables(TodoTable.TABLE_NAME);
// Adding the ID to the original query
queryBuilder.appendWhere(TodoTable.COLUMN_ID + "="
+ uri.getLastPathSegment());
break;
case NOTES:
queryBuilder.setTables(NotesTable.TABLE_NAME);
break;
case NOTE_ID:
queryBuilder.setTables(NotesTable.TABLE_NAME);
queryBuilder.appendWhere(NotesTable.COLUMN_ID + "=" + uri.getLastPathSegment());
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();
int rowsDeleted = 0;
long id = 0;
String curPath;
switch (uriType) {
case TODOS:
System.out.println("Inserimento in tabella TODOS");
id = sqlDB.insert(TodoTable.TABLE_NAME, null, values);
curPath = "todo";
break;
case NOTES:
System.out.println("Inserimento in tabella NOTES");
id = sqlDB.insert(NotesTable.TABLE_NAME, null, values);
curPath = "note";
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + curPath + "/" + id);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsDeleted = 0;
String id;
switch (uriType) {
case NOTES:
rowsDeleted = sqlDB.delete(NotesTable.TABLE_NAME, selection, selectionArgs);
break;
case NOTE_ID:
id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(NotesTable.TABLE_NAME, NotesTable.COLUMN_ID + "=" + id, null);
} else {
rowsDeleted = sqlDB.delete(NotesTable.TABLE_NAME, NotesTable.COLUMN_ID + "=" +
id + " and " + selection, selectionArgs);
break;
}
case TODOS:
rowsDeleted = sqlDB.delete(TodoTable.TABLE_NAME, selection,
selectionArgs);
break;
case TODO_ID:
id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(TodoTable.TABLE_NAME,
TodoTable.COLUMN_ID + "=" + id, null);
} else {
rowsDeleted = sqlDB.delete(TodoTable.TABLE_NAME,
TodoTable.COLUMN_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;
String id;
switch (uriType) {
case NOTES:
rowsUpdated = sqlDB.update(NotesTable.TABLE_NAME, values, selection, selectionArgs);
break;
case NOTE_ID:
id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(NotesTable.TABLE_NAME, values,
NotesTable.COLUMN_ID + "=" + id, null);
} else {
rowsUpdated = sqlDB.update(TodoTable.TABLE_NAME, values,
NotesTable.COLUMN_ID + "=" + id + " and " + selection, selectionArgs);
}
break;
case TODOS:
rowsUpdated = sqlDB.update(TodoTable.TABLE_NAME, values, selection,
selectionArgs);
break;
case TODO_ID:
id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(TodoTable.TABLE_NAME, values,
TodoTable.COLUMN_ID + "=" + id, null);
} else {
rowsUpdated = sqlDB.update(TodoTable.TABLE_NAME, values,
TodoTable.COLUMN_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection, int uriType) {
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<String>(
Arrays.asList(projection));
HashSet<String> availableColumns = null;
switch (uriType) {
case TODOS: case TODO_ID:
availableColumns = new HashSet<String>(
Arrays.asList(todoAvailable));
break;
case NOTES: case NOTE_ID:
availableColumns = new HashSet<String>(
Arrays.asList(noteAvailable));
break;
}
// Check if all columns which are requested are available
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException(
"Unknown columns in projection");
}
}
}
}
And these are the activities in which the error is caused (again skipping imports):
public class NotesOverviewActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>{
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
// private Cursor cursor;
private SimpleCursorAdapter adapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notes_list);
this.getListView().setDividerHeight(2);
fillData();
registerForContextMenu(getListView());
}
// Create the menu based on the XML defintion
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
// Reaction to the menu selection
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.insert:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(MyMaddoContentProvider.NOTE_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
private void createNote() {
Intent i = new Intent(this, NotesDetailActivity.class);
startActivity(i);
}
// Opens the second activity if an entry is clicked
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, NotesDetailActivity.class);
Uri notesUri = Uri.parse(MyMaddoContentProvider.NOTE_URI + "/" + id);
i.putExtra(MyMaddoContentProvider.CONTENT_ITEM_TYPE_NOTE, notesUri);
startActivity(i);
}
private void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { NotesTable.COLUMN_SUMMARY };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.notes_row, null, from,
to, 0);
setListAdapter(adapter);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
// Creates a new loader after the initLoader () call
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { NotesTable.COLUMN_ID, NotesTable.COLUMN_SUMMARY };
CursorLoader cursorLoader = new CursorLoader(this,
MyMaddoContentProvider.NOTE_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// data is not available anymore, delete reference
adapter.swapCursor(null);
}
}
the error seems to happen in the onListenClick method, right after it starts the notesDetailActivity, which I'm including below
public class NotesDetailActivity extends Activity {
private Spinner mCategory;
private EditText mTitleText;
private EditText mBodyText;
private EditText mDateText;
private Uri noteUri;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.notes_edit);
mCategory = (Spinner) findViewById(R.id.category);
mTitleText = (EditText) findViewById(R.id.todo_edit_summary);
mBodyText = (EditText) findViewById(R.id.todo_edit_description);
mDateText = (EditText)findViewById(R.id.date);
mDateText.setText(java.text.DateFormat.getTimeInstance().format(Calendar.getInstance().getTime()));
Button confirmButton = (Button) findViewById(R.id.todo_edit_button);
Bundle extras = getIntent().getExtras();
// Check from the saved Instance
noteUri = (bundle == null) ? null : (Uri) bundle
.getParcelable(MyMaddoContentProvider.CONTENT_ITEM_TYPE_NOTE);
// Or passed from the other activity
if (extras != null) {
noteUri = extras
.getParcelable(MyMaddoContentProvider.CONTENT_ITEM_TYPE_NOTE);
fillData(noteUri);
}
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (TextUtils.isEmpty(mTitleText.getText().toString())) {
makeToast();
} else {
setResult(RESULT_OK);
finish();
}
}
});
}
// Create the menu based on the XML defintion
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.detailsmenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save:
saveData();
return true;
}
return super.onOptionsItemSelected(item);
}
public void saveData() {
if (TextUtils.isEmpty(mTitleText.getText().toString())) {
makeToast();
} else {
setResult(RESULT_OK);
finish();
}
}
private void fillData(Uri uri) {
String[] projection = { NotesTable.COLUMN_SUMMARY,
NotesTable.COLUMN_CONTENT, NotesTable.COLUMN_CATEGORY, NotesTable.COLUMN_DATE };
Cursor cursor = getContentResolver().query(uri, projection, null, null,
null);
if (cursor != null) {
cursor.moveToFirst();
String category = cursor.getString(cursor
.getColumnIndexOrThrow(NotesTable.COLUMN_CATEGORY));
for (int i = 0; i < mCategory.getCount(); i++) {
String s = (String) mCategory.getItemAtPosition(i);
if (s.equalsIgnoreCase(category)) {
mCategory.setSelection(i);
}
}
mTitleText.setText(cursor.getString(cursor
.getColumnIndexOrThrow(NotesTable.COLUMN_SUMMARY)));
mBodyText.setText(cursor.getString(cursor
.getColumnIndexOrThrow(NotesTable.COLUMN_CONTENT)));
mDateText.setText(cursor.getString(cursor
.getColumnIndexOrThrow(NotesTable.COLUMN_DATE)));
// Always close the cursor
cursor.close();
}
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveState();
outState.putParcelable(MyMaddoContentProvider.CONTENT_ITEM_TYPE_NOTE, noteUri);
}
#Override
protected void onPause() {
super.onPause();
saveState();
}
private void saveState() {
String category = (String) mCategory.getSelectedItem();
String summary = mTitleText.getText().toString();
String description = mBodyText.getText().toString();
String date = mBodyText.getText().toString();
// Only save if either summary or description
// is available
if (description.length() == 0 && summary.length() == 0) {
return;
}
ContentValues values = new ContentValues();
values.put(NotesTable.COLUMN_CATEGORY, category);
values.put(NotesTable.COLUMN_SUMMARY, summary);
values.put(NotesTable.COLUMN_CONTENT, description);
values.put(NotesTable.COLUMN_DATE, date);
if (noteUri == null) {
// New note
noteUri = getContentResolver().insert(
MyMaddoContentProvider.NOTE_URI, values);
} else {
// Update note
getContentResolver().update(noteUri, values, null, null);
}
}
private void makeToast() {
Toast.makeText(NotesDetailActivity.this, "Please maintain a summary",
Toast.LENGTH_LONG).show();
}
}
I'm obviously doing something wrong with the uri, still no idea exactly what, considering the todo part is almost exactly the same (save for variable names and database table of course) and doesn't give such errors.
What is the problem?

I have followed the same example.. Here is your fix--
change your authority name in both manifest and MyMaddoContentProvider class as follows:-
private static final String AUTHORITY = "com.jackomel.maddonote.contentprovide.MyMaddoContentProviderr";
and
android:name="com.jackomel.maddonote.contentprovider.MyMaddoContentProvider"
android:authorities="com.jackomel.maddonote.contentprovider.MyMaddoContentProvider"
It should probably work now.

I think I may have found a solution to this problem. I was having a similar problem except for my error was saying that I had invalid tables instead of URI's. I followed Jackomels example above for his set tables queries and was able to get my edit to work. His Uri's are set up differently than mine which I am thinking is what the error is about. He uses the same BASE_PATH for both tables but I think that each table needs to have a different "base path".
I have private static final String BASE_PATH = "todos" & private static final String DEVICE_PATH = "devices";
I am pasting my code below in case it helps someone else.
package com.cossioinsurance.rentalsafety.contentprovider2;
import java.util.Arrays;
import java.util.HashSet;
import com.cossioinsurance.rentalsafety.database.TodoDatabaseHelper;
import com.cossioinsurance.rentalsafety.database.NoteTable;
import com.cossioinsurance.rentalsafety.database.DeviceTable;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class MyNoteContentProvider extends ContentProvider{
// database
private TodoDatabaseHelper database;
// used for the UriMacher
private static final int TODOS = 10;
private static final int TODO_ID = 20;
private static final int DEVICES = 30;
private static final int DEVICE_ID = 40;
private static final String AUTHORITY = "com.cossioinsurance.rentalsafety.contentprovider2";
private static final String BASE_PATH = "todos";
private static final String DEVICE_PATH = "devices";
public static final Uri CONTENT_URI_TODOS = Uri.parse("content://" + AUTHORITY
+ "/" + BASE_PATH);
public static final Uri CONTENT_URI_DEVICES = Uri.parse("content://" + AUTHORITY
+ "/" + DEVICE_PATH);
public static final String CONTENT_TYPE_TODOS = ContentResolver.CURSOR_DIR_BASE_TYPE
+ "/todos";
public static final String CONTENT_TYPE_DEVICES = ContentResolver.CURSOR_DIR_BASE_TYPE
+ "/devices";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ "/todo";
public static final String CONTENT_ITEM_TYPE2 = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ "/device";
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, BASE_PATH, TODOS);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", TODO_ID);
sURIMatcher.addURI(AUTHORITY, DEVICE_PATH, DEVICES);
sURIMatcher.addURI(AUTHORITY, DEVICE_PATH + "/#", DEVICE_ID);
}
#Override
public boolean onCreate() {
database = new TodoDatabaseHelper(getContext());
return false;
}
#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);
// Set the table
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case TODOS:
queryBuilder.setTables(NoteTable.TABLE_TODO);
break;
case TODO_ID:
queryBuilder.setTables(NoteTable.TABLE_TODO);
// adding the ID to the original query
queryBuilder.appendWhere(NoteTable.COLUMN_ID + "="
+ uri.getLastPathSegment());
break;
case DEVICES:
queryBuilder.setTables(DeviceTable.TABLE_DEVICE);
break;
case DEVICE_ID:
queryBuilder.setTables(DeviceTable.TABLE_DEVICE);
// adding the ID to the original query
queryBuilder.appendWhere(DeviceTable.COLUMN_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();
int rowsDeleted = 0;
long id = 0;
switch (uriType) {
case TODOS:
id = sqlDB.insert(NoteTable.TABLE_TODO, null, values);
break;
case DEVICES:
id = sqlDB.insert(DeviceTable.TABLE_DEVICE, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + "/" + id);
}
//Deleted Rows
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsDeleted = 0;
String id = uri.getLastPathSegment();
switch (uriType) {
case TODOS:
rowsDeleted = sqlDB.delete(NoteTable.TABLE_TODO, selection,
selectionArgs);
break;
case TODO_ID:
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(NoteTable.TABLE_TODO,
NoteTable.COLUMN_ID + "=" + id,
null);
} else {
rowsDeleted = sqlDB.delete(NoteTable.TABLE_TODO,
NoteTable.COLUMN_ID + "=" + id
+ " and " + selection,
selectionArgs);
}
break;
case DEVICES:
rowsDeleted = sqlDB.delete(DeviceTable.TABLE_DEVICE, selection,
selectionArgs);
break;
case DEVICE_ID:
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(DeviceTable.TABLE_DEVICE,
DeviceTable.COLUMN_ID + "=" + id,
null);
} else {
rowsDeleted = sqlDB.delete(NoteTable.TABLE_TODO,
DeviceTable.COLUMN_ID + "=" + id
+ " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsDeleted;
}
//Update Rows
#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 TODOS:
rowsUpdated = sqlDB.update(NoteTable.TABLE_TODO,
values,
selection,
selectionArgs);
break;
case TODO_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(NoteTable.TABLE_TODO,
values,
NoteTable.COLUMN_ID + "=" + id,
null);
} else {
rowsUpdated = sqlDB.update(NoteTable.TABLE_TODO,
values,
NoteTable.COLUMN_ID + "=" + id
+ " and "
+ selection,
selectionArgs);
}
break;
case DEVICES:
rowsUpdated = sqlDB.update(DeviceTable.TABLE_DEVICE,
values,
selection,
selectionArgs);
break;
case DEVICE_ID:
id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(DeviceTable.TABLE_DEVICE,
values,
DeviceTable.COLUMN_ID + "=" + id,
null);
} else {
rowsUpdated = sqlDB.update(DeviceTable.TABLE_DEVICE,
values,
DeviceTable.COLUMN_ID + "=" + id
+ " and "
+ selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection) {
String[] available = { NoteTable.COLUMN_CATEGORY,
NoteTable.COLUMN_SUMMARY, NoteTable.COLUMN_DESCRIPTION, NoteTable.COLUMN_EMAIL, NoteTable.COLUMN_RENTALDATES,
NoteTable.COLUMN_ENDDATES, NoteTable.COLUMN_TIME, NoteTable.COLUMN_LOCATION, NoteTable.COLUMN_NOTES,
NoteTable.COLUMN_ID, DeviceTable.COLUMN_ID, DeviceTable.COLUMN_CATEGORY, DeviceTable.COLUMN_NAME, DeviceTable.COLUMN_TYPE };
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
// check if all columns which are requested are available
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}
}
}

Related

SQL Database error

I have problem while deleting data from database. I have button which is toggle between two states. Adding data to database and removing data from database. Here's the code:
//Method for adding or removing movies in favorite movie database
public long toggleFav(MovieData movieData) {
ContentValues cv = new ContentValues();
boolean favorite = isFav(movieData.getTitle());
if(favorite) {
favDb.delete(FavoriteContract.FavoriteEntry.TABLE_NAME,
FavoriteContract.FavoriteEntry.COLUMN_ID, null);
mFavoriteImage.setImageResource(R.drawable.fav_ic_no);
movieData.setIsFav(false);
Toast.makeText(MovieDetails.this, getString(R.string.remove_fav),
Toast.LENGTH_SHORT).show();
} else {
cv.put(FavoriteContract.FavoriteEntry.COLUMN_ID, movieData.getMovieId());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_POSTER, movieData.getPoster());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_TITLE, movieData.getTitle());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_RELEASE_DATE, movieData.getReleaseDate());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_AVERAGE_VOTE, movieData.getRating());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_SYNOPSIS, movieData.getSynopsis());
mFavoriteImage.setImageResource(R.drawable.fav_ic_selected);
Toast.makeText(MovieDetails.this, getString(R.string.add_fav),
Toast.LENGTH_SHORT).show();
}
return favDb.insert(FavoriteContract.FavoriteEntry.TABLE_NAME, null, cv);
}
On first click data is saved perfectly, but on second click data ''is removed'' but I get this strange error ...
04-05 14:57:18.540 11162-11162/com.example.android.popularmovies1 E/SQLiteLog: (1) near "null": syntax error
04-05 14:57:18.541 11162-11162/com.example.android.popularmovies1 E/SQLiteDatabase: Error inserting
android.database.sqlite.SQLiteException: near "null": syntax error (code 1): , while compiling: INSERT INTO fav_movies(null) VALUES (NULL)
Also, I have activity were I can see saved data, and if click button for saving it is listed on that activity. The problem is if I from database activity remove data from database, that data removes only when I leave that activity and then go back ...
Here's provider for database
public class FavoritesProvider extends ContentProvider {
public static final int FAVORITES = 100;
public static final int FAVORITES_WITH_ID = 101;
private FavoriteDbHelper mFavoriteDbHelper;
private static final UriMatcher sUriMatcher = buildUriMatcher();
public static UriMatcher buildUriMatcher() {
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(FavoriteContract.AUTHORITY, FavoriteContract.FAV_PATH, FAVORITES);
uriMatcher.addURI(FavoriteContract.AUTHORITY,
FavoriteContract.FAV_PATH + "/#", FAVORITES_WITH_ID);
return uriMatcher;
}
#Override
public boolean onCreate() {
Context context = getContext();
mFavoriteDbHelper = new FavoriteDbHelper(context);
return true;
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection,
#Nullable String[] selectionArgs, #Nullable String sortOrder) {
final SQLiteDatabase db = mFavoriteDbHelper.getReadableDatabase();
int match = sUriMatcher.match(uri);
Cursor retCursor;
switch(match) {
case FAVORITES:
retCursor = db.query(FavoriteContract.FavoriteEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
break;
case FAVORITES_WITH_ID:
String id = uri.getPathSegments().get(1);
String mSelection = FavoriteContract.FavoriteEntry.COLUMN_ID;
String[] mSelectionArgs = new String[]{id};
retCursor = db.query(FavoriteContract.FavoriteEntry.TABLE_NAME,
projection,
mSelection,
mSelectionArgs,
null,
null,
sortOrder);
break;
default:
throw new UnsupportedOperationException("Uknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
int match = sUriMatcher.match(uri);
switch(match) {
case FAVORITES:
return "vnd.android.cursor.dir" + "/" + FavoriteContract.AUTHORITY + "/" +
FavoriteContract.FAV_PATH;
case FAVORITES_WITH_ID:
return "vnd.android.cursor.item" + "/" + FavoriteContract.AUTHORITY + "/" +
FavoriteContract.FAV_PATH;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, ContentValues values) {
final SQLiteDatabase db = mFavoriteDbHelper.getWritableDatabase();
int match = sUriMatcher.match(uri);
Uri retUri;
switch(match) {
case FAVORITES:
long id = db.insert(FavoriteContract.FavoriteEntry.TABLE_NAME,
null, values);
if(id > 0) {
retUri = ContentUris.withAppendedId(FavoriteContract
.FavoriteEntry.CONTENT_URI, id);
} else {
throw new android.database.SQLException("Failed to insert row into " + id);
}
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return retUri;
}
#Override
public int delete(#NonNull Uri uri, String selection,
String[] selectionArgs) {
final SQLiteDatabase db = mFavoriteDbHelper.getWritableDatabase();
int match = sUriMatcher.match(uri);
int favDeleted;
switch(match) {
case FAVORITES_WITH_ID:
String id = uri.getPathSegments().get(1);
favDeleted = db.delete(FavoriteContract.FavoriteEntry.TABLE_NAME,
FavoriteContract.FavoriteEntry.COLUMN_ID, new String[]{id});
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if(favDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return favDeleted;
}
#Override
public int update(#NonNull Uri uri, #Nullable ContentValues values,
#Nullable String selection, #Nullable String[] selectionArgs) {
int match = sUriMatcher.match(uri);
int favUpdated;
switch(match) {
case FAVORITES_WITH_ID:
String id = uri.getPathSegments().get(1);
favUpdated = mFavoriteDbHelper.getWritableDatabase().update(
FavoriteContract.FavoriteEntry.TABLE_NAME, values,
FavoriteContract.FavoriteEntry.COLUMN_ID, new String[]{id});
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if(favUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return favUpdated;
}
}
When the if statement is executed (favourite = true), nothing is added to your content values, and then in the last line you try insert into the database with no values, maybe just return -1 in the last bit of the if statement and move the final return statement into the else
maybe like this
public long toggleFav(MovieData movieData) {
boolean favorite = isFav(movieData.getTitle());
if(favorite) {
favDb.delete(FavoriteContract.FavoriteEntry.TABLE_NAME, FavoriteContract.FavoriteEntry.COLUMN_ID, null);
mFavoriteImage.setImageResource(R.drawable.fav_ic_no);
movieData.setIsFav(false);
Toast.makeText(MovieDetails.this, getString(R.string.remove_fav), Toast.LENGTH_SHORT).show();
return -1; // favourite deleted
} else {
ContentValues cv = new ContentValues();
cv.put(FavoriteContract.FavoriteEntry.COLUMN_ID, movieData.getMovieId());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_POSTER, movieData.getPoster());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_TITLE, movieData.getTitle());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_RELEASE_DATE, movieData.getReleaseDate());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_AVERAGE_VOTE, movieData.getRating());
cv.put(FavoriteContract.FavoriteEntry.COLUMN_SYNOPSIS, movieData.getSynopsis());
mFavoriteImage.setImageResource(R.drawable.fav_ic_selected);
Toast.makeText(MovieDetails.this, getString(R.string.add_fav), Toast.LENGTH_SHORT).show();
return favDb.insert(FavoriteContract.FavoriteEntry.TABLE_NAME, null, cv);
}
}

Simple Cursor Adapter, Content Provider with Inner Join displays data only one time

I have the problem, that my ListView shows the data only one time. If am closing the application and open it again, it doesn't show anything.
I have a Content Provider for retrieving the data from a SQLite database. I have a SyncAdapter which is getting the data from a Server and puts it into the SQLite database. Then I use a SimpleCursorAdapter for showing the data into a ListView. The query is an 'inner join'.
MainActivity Code:
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int URL_LOADER = 1;
private ListView employeeList;
public static SimpleCursorAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
employeeList = (ListView) findViewById(R.id.listView);
/**
* Creating a dummyAccount for the content provider
*/
Account account = new Account("dummyAccount", TrackingContentProvider.AUTHORITY);
AccountManager accountManager = (AccountManager) this.getSystemService(ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(account, null, null);
/**
* Set the sync adapter to an automatic state
*/
ContentResolver.setSyncAutomatically(account, TrackingContentProvider.AUTHORITY, true);
/**
* The projection for the SimpleCursorLoader
*/
String[] from = new String[] {EmployeesTable.COLUMN_FIRST_NAME, EmployeesTable.COLUMN_LAST_NAME, StatesTable.COLUMN_STATE, EmployeesTable.COLUMN_ID};
/**
* The ids of the views where the content is going to be put in
*/
int[] to = new int[] {R.id.tV_emplFirstNameInsert, R.id.tV_emplLastNameInsert, R.id.tV_stateInsert};
/**
* Make a new cursor adapter for the list view
*/
adapter = new SimpleCursorAdapter(this, R.layout.employee_list_item, null, from, to, 0);
/**
* Calls the loader manager and query another time
*/
getSupportLoaderManager().restartLoader(URL_LOADER, null, MainActivity.this);
/**
* Sets the list view to the SimpleCursorAdapter
*/
employeeList.setAdapter(adapter);
registerClickCallback();
}
/**
* Registers the clicks on the elements
*/
private void registerClickCallback() {
employeeList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
startActivity(new Intent(MainActivity.this, LocationActivity.class));
}
});
}
/**
* Is called by the loader manager
* #param id The unique id of the loader
* #param args Bundle of arguments
* #return The cursor of the query
*/
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = new String[] {"E." + EmployeesTable.COLUMN_ID, "E." + EmployeesTable.COLUMN_FIRST_NAME,
"E." + EmployeesTable.COLUMN_LAST_NAME, "S." + StatesTable.COLUMN_STATE};
String clause = "S." + StatesTable.COLUMN_ID + " = " + "E." + EmployeesTable.COLUMN_STATE_ID;
return new CursorLoader(this,
TrackingContentProvider.EMPLOYEE_LIST_CONTENT_URI, projection, clause, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
#Override
protected void onRestart() {
super.onRestart();
getSupportLoaderManager().restartLoader(URL_LOADER, null, MainActivity.this);
}
}
The Content Provider queries the data with a SQLiteQueryBuilder. The table I used for the query looks like this:
private static final String EMPLOYEE_LIST_INNER_JOIN = "" +
EmployeesTable.TABLE_NAME + " AS E " +
"INNER JOIN " +
StatesTable.TABLE_NAME + " AS S";
The onPerformSync looks like this:
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
deleteDatabase(provider);
insertEmployeesTable(provider);
insertStatesTable(provider);
insertLocationsTable(provider);
} catch (RemoteException | IOException e) {
Log.e("Sync: Error:", e.toString());
}
}
So every sync, it deletes all entries of the database and inserts the data new. It works, I checked the database after sync.
After the application is started for the first time, it doesn't shows anything, since the sync adapter is syncing. Then I close the app and opens it again and it shows the data. Then the sync adapter syncs another time (same data) but it doesn't shows the data anymore. Any suggestions? I am now trying to find the error for 3 days...
EDIT 1: ContentProvider Code:
public class TrackingContentProvider extends ContentProvider {
private static final int TRACKING_CONTENT = 100;
private static final int TRACKING_CONTENT_ID = 200;
private static final int EMPLOYEE_LIST_CONTENT = 300;
private static final int EMPLOYEE_LIST_CONTENT_ID = 400;
/**
* Table declaration
*/
private static final int EMPLOYEES_TABLE_CONTENT = 500;
private static final int EMPLOYEES_TABLE_CONTENT_ID = 600;
private static final int STATES_TABLE_CONTENT = 700;
private static final int STATES_TABLE_CONTENT_ID = 800;
private static final int LOCATIONS_TABLE_CONTENT = 900;
private static final int LOCATIONS_TABLE_CONTENT_ID = 1000;
public static final String AUTHORITY = "***";
private static final String TRACKING_BASE_PATH = "tracking";
private static final String EMPLOYEE_LIST_BASE_PATH = "employees";
/**
* Table base paths
*/
private static final String EMPLOYEES_TABLE_BASE_PATH = "employeesTable";
private static final String STATES_TABLE_BASE_PATH = "statesTable";
private static final String LOCATIONS_TABLE_BASE_PATH = "locationsTable";
/**
* Table INNER JOIN declarations
*/
private static final String EMPLOYEE_LIST_INNER_JOIN = "" +
EmployeesTable.TABLE_NAME + " AS E " +
"INNER JOIN " +
StatesTable.TABLE_NAME + " AS S";
public static final Uri TRACKING_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TRACKING_BASE_PATH);
public static final Uri EMPLOYEE_LIST_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + EMPLOYEE_LIST_BASE_PATH);
/**
* Tables Uris
*/
public static final Uri EMPLOYEES_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + EMPLOYEES_TABLE_BASE_PATH);
public static final Uri STATES_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + STATES_TABLE_BASE_PATH);
public static final Uri LOCATIONS_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + LOCATIONS_TABLE_BASE_PATH);
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, TRACKING_BASE_PATH, TRACKING_CONTENT);
sUriMatcher.addURI(AUTHORITY, TRACKING_BASE_PATH + "/#", TRACKING_CONTENT_ID);
sUriMatcher.addURI(AUTHORITY, EMPLOYEE_LIST_BASE_PATH, EMPLOYEE_LIST_CONTENT);
sUriMatcher.addURI(AUTHORITY, EMPLOYEE_LIST_BASE_PATH + "/#", EMPLOYEE_LIST_CONTENT_ID);
/**
* Table matcher
*/
sUriMatcher.addURI(AUTHORITY, EMPLOYEES_TABLE_BASE_PATH, EMPLOYEES_TABLE_CONTENT);
sUriMatcher.addURI(AUTHORITY, EMPLOYEES_TABLE_BASE_PATH + "/#", EMPLOYEES_TABLE_CONTENT_ID);
sUriMatcher.addURI(AUTHORITY, STATES_TABLE_BASE_PATH, STATES_TABLE_CONTENT);
sUriMatcher.addURI(AUTHORITY, STATES_TABLE_BASE_PATH + "/#", STATES_TABLE_CONTENT_ID);
sUriMatcher.addURI(AUTHORITY, LOCATIONS_TABLE_BASE_PATH, LOCATIONS_TABLE_CONTENT);
sUriMatcher.addURI(AUTHORITY, LOCATIONS_TABLE_BASE_PATH + "/#", LOCATIONS_TABLE_CONTENT_ID);
}
private SQLiteHelper database;
#Override
public boolean onCreate() {
database = new SQLiteHelper(getContext());
return false;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
int uriType = sUriMatcher.match(uri);
checkColumns(projection);
switch (uriType) {
case TRACKING_CONTENT:
break;
case TRACKING_CONTENT_ID:
break;
case EMPLOYEE_LIST_CONTENT:
queryBuilder.setTables(EMPLOYEE_LIST_INNER_JOIN);
break;
case EMPLOYEE_LIST_CONTENT_ID:
queryBuilder.setTables(EMPLOYEE_LIST_INNER_JOIN);
queryBuilder.appendWhere("E." + EmployeesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
break;
case STATES_TABLE_CONTENT:
queryBuilder.setTables(StatesTable.TABLE_NAME);
break;
case STATES_TABLE_CONTENT_ID:
queryBuilder.setTables(StatesTable.TABLE_NAME);
queryBuilder.appendWhere(StatesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
break;
case EMPLOYEES_TABLE_CONTENT:
queryBuilder.setTables(EmployeesTable.TABLE_NAME);
break;
case EMPLOYEES_TABLE_CONTENT_ID:
queryBuilder.setTables(EmployeesTable.TABLE_NAME);
queryBuilder.appendWhere(EmployeesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
break;
case LOCATIONS_TABLE_CONTENT:
queryBuilder.setTables(LocationsTable.TABLE_NAME);
break;
case LOCATIONS_TABLE_CONTENT_ID:
queryBuilder.setTables(LocationsTable.TABLE_NAME);
queryBuilder.appendWhere(LocationsTable.COLUMN_ID + " = " + uri.getLastPathSegment());
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
SQLiteDatabase db = database.getReadableDatabase();
Cursor cursor = queryBuilder.query(
db,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
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 db = database.getWritableDatabase();
long id;
String path;
switch (uriType) {
case STATES_TABLE_CONTENT:
id = db.insert(StatesTable.TABLE_NAME, null, values);
path = STATES_TABLE_BASE_PATH;
break;
case LOCATIONS_TABLE_CONTENT:
id = db.insert(LocationsTable.TABLE_NAME, null, values);
path = LOCATIONS_TABLE_BASE_PATH;
break;
case EMPLOYEES_TABLE_CONTENT:
id = db.insert(EmployeesTable.TABLE_NAME, null, values);
path = EMPLOYEES_TABLE_BASE_PATH;
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(path + "/" + id);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int uriType = sUriMatcher.match(uri);
SQLiteDatabase db = database.getWritableDatabase();
int rowsDeleted = 0;
switch (uriType) {
case EMPLOYEES_TABLE_CONTENT:
rowsDeleted = db.delete(EmployeesTable.TABLE_NAME,
selection,
selectionArgs);
break;
case EMPLOYEES_TABLE_CONTENT_ID:
String employeeID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = db.delete(EmployeesTable.TABLE_NAME,
EmployeesTable.COLUMN_ID + " = " + employeeID, null);
}
break;
case STATES_TABLE_CONTENT:
rowsDeleted = db.delete(StatesTable.TABLE_NAME,
selection,
selectionArgs);
break;
case STATES_TABLE_CONTENT_ID:
String stateID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = db.delete(StatesTable.TABLE_NAME,
StatesTable.COLUMN_ID + " = " + stateID, null);
}
break;
case LOCATIONS_TABLE_CONTENT:
rowsDeleted = db.delete(StatesTable.TABLE_NAME,
selection,
selectionArgs);
break;
case LOCATIONS_TABLE_CONTENT_ID:
String locationID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = db.delete(LocationsTable.TABLE_NAME,
LocationsTable.COLUMN_ID + " = " + locationID, null);
}
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;
switch (uriType) {
case EMPLOYEES_TABLE_CONTENT:
rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
values,
selection,
selectionArgs);
break;
case EMPLOYEE_LIST_CONTENT_ID:
String employeeID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
values,
EmployeesTable.COLUMN_ID + "=" + employeeID
+ " AND "
+ selection,
selectionArgs);
} else {
rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
values,
EmployeesTable.COLUMN_ID + "=" + employeeID
+ " AND "
+ selection,
selectionArgs);
}
break;
case STATES_TABLE_CONTENT:
rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
values,
selection,
selectionArgs);
break;
case STATES_TABLE_CONTENT_ID:
String stateID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
values,
StatesTable.COLUMN_ID + "=" + stateID
+ " AND "
+ selection,
selectionArgs);
} else {
rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
values,
StatesTable.COLUMN_ID + "=" + stateID
+ " AND "
+ selection,
selectionArgs);
}
break;
case LOCATIONS_TABLE_CONTENT:
rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
values,
selection,
selectionArgs);
break;
case LOCATIONS_TABLE_CONTENT_ID:
String locationID = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
values,
LocationsTable.COLUMN_ID + "=" + locationID
+ " AND "
+ selection,
selectionArgs);
} else {
rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
values,
LocationsTable.COLUMN_ID + "=" + locationID
+ " AND "
+ selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
/**
* Checks if the column exists
* #param projection The array of the existing columns
*/
private void checkColumns(String[] projection) {
String[] available = {
"E." + EmployeesTable.COLUMN_ID,
"E." + EmployeesTable.COLUMN_LAST_NAME,
"E." + EmployeesTable.COLUMN_FIRST_NAME,
"E." + EmployeesTable.COLUMN_STATE_ID,
"E." + EmployeesTable.COLUMN_ENTRY_DATE,
"S." + StatesTable.COLUMN_ID,
"S." + StatesTable.COLUMN_STATE,
"L." + LocationsTable.COLUMN_ID,
"L." + LocationsTable.COLUMN_DATE,
"L." + LocationsTable.COLUMN_EMPLOYEE_ID,
"L." + LocationsTable.COLUMN_LONGITUDE,
"L." + LocationsTable.COLUMN_LATITUDE,
EmployeesTable.COLUMN_ID,
EmployeesTable.COLUMN_LAST_NAME,
EmployeesTable.COLUMN_FIRST_NAME,
EmployeesTable.COLUMN_STATE_ID,
EmployeesTable.COLUMN_ENTRY_DATE,
StatesTable.COLUMN_ID,
StatesTable.COLUMN_STATE,
LocationsTable.COLUMN_ID,
LocationsTable.COLUMN_DATE,
LocationsTable.COLUMN_EMPLOYEE_ID,
LocationsTable.COLUMN_LONGITUDE,
LocationsTable.COLUMN_LATITUDE,
};
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<>(Arrays.asList(available));
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}
}
}
UPDATE:
I finally found the mistake. During the synchronization, there is a mistake with the auto increment. So, he won't find a entry with this Inner Join statement. finished.

Is it possible to create table on Join?

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;
}
}

How to fix content provider url not found in android Content provider?

I have followed the below tutorial http://www.vogella.de/articles/AndroidSQLite/article.htm
But getting this exception after clicking on "confirm" button
01-20 10:18:14.585: E/AndroidRuntime(2006): Caused by: java.lang.IllegalArgumentException: Unknown URL content://com.example.todos.contentprovider/todos
01-20 10:18:14.585: E/AndroidRuntime(2006): at android.content.ContentResolver.insert(ContentResolver.java:910)
01-20 10:18:14.585: E/AndroidRuntime(2006): at com.example.todos.TodoDetailActivity.saveState(TodoDetailActivity.java:122)
01-20 10:18:14.585: E/AndroidRuntime(2006): at com.example.todos.TodoDetailActivity.onPause(TodoDetailActivity.java:100)
TodoDetailActivity
public class TodoDetailActivity extends Activity {
private Spinner mCategory;
private EditText mTitleText;
private EditText mBodyText;
private Uri todoUri;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.todo_edit);
mCategory = (Spinner) findViewById(R.id.category);
mTitleText = (EditText) findViewById(R.id.todo_edit_summary);
mBodyText = (EditText) findViewById(R.id.todo_edit_description);
Button confirmButton = (Button) findViewById(R.id.todo_edit_button);
Bundle extras = getIntent().getExtras();
// check from the saved Instance
todoUri = (bundle == null) ? null : (Uri) bundle
.getParcelable(MyTodoContentProvider.CONTENT_ITEM_TYPE);
// Or passed from the other activity
if (extras != null) {
todoUri = extras
.getParcelable(MyTodoContentProvider.CONTENT_ITEM_TYPE);
fillData(todoUri);
}
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (TextUtils.isEmpty(mTitleText.getText().toString())) {
makeToast();
} else {
setResult(RESULT_OK);
finish();
}
}
});
}
private void fillData(Uri uri) {
String[] projection = { TodoTable.COLUMN_SUMMARY,
TodoTable.COLUMN_DESCRIPTION, TodoTable.COLUMN_CATEGORY };
Cursor cursor = getContentResolver().query(uri, projection, null, null,
null);
if (cursor != null) {
cursor.moveToFirst();
String category = cursor.getString(cursor
.getColumnIndexOrThrow(TodoTable.COLUMN_CATEGORY));
for (int i = 0; i < mCategory.getCount(); i++) {
String s = (String) mCategory.getItemAtPosition(i);
if (s.equalsIgnoreCase(category)) {
mCategory.setSelection(i);
}
}
mTitleText.setText(cursor.getString(cursor
.getColumnIndexOrThrow(TodoTable.COLUMN_SUMMARY)));
mBodyText.setText(cursor.getString(cursor
.getColumnIndexOrThrow(TodoTable.COLUMN_DESCRIPTION)));
// always close the cursor
cursor.close();
}
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveState();
outState.putParcelable(MyTodoContentProvider.CONTENT_ITEM_TYPE, todoUri);
}
#Override
protected void onPause() {
super.onPause();
saveState();
}
private void saveState() {
String category = (String) mCategory.getSelectedItem();
String summary = mTitleText.getText().toString();
String description = mBodyText.getText().toString();
// only save if either summary or description
// is available
if (description.length() == 0 && summary.length() == 0) {
return;
}
ContentValues values = new ContentValues();
values.put(TodoTable.COLUMN_CATEGORY, category);
values.put(TodoTable.COLUMN_SUMMARY, summary);
values.put(TodoTable.COLUMN_DESCRIPTION, description);
if (todoUri == null) {
// New todo
todoUri = getContentResolver().insert(
MyTodoContentProvider.CONTENT_URI, values);
} else {
// Update todo
getContentResolver().update(todoUri, values, null, null);
}
}
private void makeToast() {
Toast.makeText(TodoDetailActivity.this, "Please maintain a summary",
Toast.LENGTH_LONG).show();
}
}
MyTodoContentProvider
public class MyTodoContentProvider extends ContentProvider{
//database
private TodoDatabaseHelper database;
//Used for the uriMatcher
private static final int TODOS = 10;
private static final int TODO_ID = 20;
private static final String AUTHORITY = "com.example.todos.contentprovider";
private static final String BASE_PATH = "todos";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY+ "/" + BASE_PATH);
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +"/todos";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/todo";
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, BASE_PATH, TODOS);
sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", TODO_ID);
}
#Override
public boolean onCreate()
{
database = new TodoDatabaseHelper(getContext());
return false;
}
#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);
//set the table
queryBuilder.setTables(TodoTable.TABLE_TODO);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case TODOS:
break;
case TODO_ID:
// adding the ID to the original query
queryBuilder.appendWhere(TodoTable.COLUMN_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 = 0;
switch (uriType) {
case TODOS:
id = sqlDB.insert(TodoTable.TABLE_TODO, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + "/" + id);
}
#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 TODOS:
rowsDeleted = sqlDB.delete(TodoTable.TABLE_TODO,selection, selectionArgs);
break;
case TODO_ID:
String id = uri.getLastPathSegment();
if(TextUtils.isEmpty(selection))
{
rowsDeleted = sqlDB.delete(TodoTable.TABLE_TODO,TodoTable.COLUMN_ID + "=" + id,null);
}
else
{
rowsDeleted = sqlDB.delete(TodoTable.TABLE_TODO,TodoTable.COLUMN_ID + "=" + id + "and" + selection ,selectionArgs);
}
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 TODOS:
rowsUpdated = sqlDb.update(TodoTable.TABLE_TODO, values, selection, selectionArgs);
break;
case TODO_ID:
String id = uri.getLastPathSegment();
if(TextUtils.isEmpty(selection))
{
rowsUpdated = sqlDb.update(TodoTable.TABLE_TODO, values, TodoTable.COLUMN_ID + "=" + id, null);
}
else
{
rowsUpdated = sqlDb.update(TodoTable.TABLE_TODO, values, TodoTable.COLUMN_ID + "=" + id + "and" + selection, selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection)
{
String[] available = {TodoTable.COLUMN_CATEGORY,TodoTable.COLUMN_SUMMARY,TodoTable.COLUMN_DESCRIPTION,TodoTable.COLUMN_ID};
HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
if(projection != null)
{
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}
}
}
TodoDatabaseHelper
public class TodoDatabaseHelper extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "todotable.db";
private static final int DATABASE_VERSION = 1;
public TodoDatabaseHelper(Context context)
{
super(context,DATABASE_NAME,null,DATABASE_VERSION);
}
//Method is called during creation of database
#Override
public void onCreate(SQLiteDatabase database)
{
TodoTable.onCreate(database);
}
// Method is called during an upgrade of the database,
// e.g. if you increase the database version
#Override
public void onUpgrade(SQLiteDatabase database, int oldVersion,
int newVersion) {
TodoTable.onUpgrade(database, oldVersion, newVersion);
}
}
You are using
private static final String AUTHORITY = "com.example.todos.contentprovider";
// It should same as you defined in manifest
So this
Caused by: java.lang.IllegalArgumentException: Unknown URL content://com.example.todos.contentprovider/todos
So make sure you define your ContentProvider with same authority in manifest.xml
<provider
android:authorities="com.example.todos.contentprovider"
android:name=".YOUR_ContentProvider" >
</provider>
Hope this will work for you.
Also ensure that you give android:exported="true" in manifest.xml, also ensure they are placed inside </application> not inside </activity>.
<provider
android:name="com.example.todos.contentprovider"
android:authorities="com.example.todos.contentprovider.MyTodoContentProvider"
android:exported="true">
</provider>

Dual purpose of Android ContentProvider: 1. Search provider and 2. List content provider

I have a ContentProvider that abstracts the info I'm using for a CursorAdapter to populate a list.
I'm building a SearchView in the action bar and want to attach a search ContentProvider to it. Can I use the same provider for this? Is it good design?
The SearchView content would be the same as in #1, it's just used to search a massive listview of items.
The ContentProvider code is below:
public class WebSitesContentProvider extends ContentProvider {
// database
private UserDatabaseHelper database;
// Used for the UriMacher
private static final int TOTAL_ELEMENTS = 10;
private static final int ELEMENT_ID = 20;
private static final String BASE_PATH = "websites";
public static final Uri CONTENT_URI = Uri.parse("content://" + Consts.AUTHORITY + "/" + BASE_PATH);
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/website";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + Consts.TABLE_WEBSITES_INFO;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(Consts.AUTHORITY, BASE_PATH, TOTAL_ELEMENTS);
sURIMatcher.addURI(Consts.AUTHORITY, BASE_PATH + "/#", ELEMENT_ID);
}
#Override
public boolean onCreate() {
database = new UserDatabaseHelper(getContext());
return false;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// UsIng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// Check if the caller has requested a column which does not exists
checkColumns(projection);
// Set the table
queryBuilder.setTables(Consts.TABLE_WEBSITES_INFO);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case TOTAL_ELEMENTS:
break;
case ELEMENT_ID:
// Adding the ID to the original query
queryBuilder.appendWhere(Consts.COLUMN_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 Uri insert(Uri uri, ContentValues values) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsDeleted = 0;
long id = 0;
switch (uriType) {
case TOTAL_ELEMENTS:
id = sqlDB.insert(Consts.TABLE_WEBSITES_INFO, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + "/" + id);
}
#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 TOTAL_ELEMENTS:
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO,
selection,
selectionArgs);
break;
case ELEMENT_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO,
Consts.COLUMN_ID + "=" + id,
null);
} else {
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO,
Consts.COLUMN_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
/*
* Let's not notify content observers on deletes of less then 1 as each delete would cause a network call.
* user could delete multiple entries at once. if the deletes are greater then 1 then it's probably a
* request to remove the entire list, this we will allow
*/
//if(rowsDeleted>1)
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 TOTAL_ELEMENTS:
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO,
values,
selection,
selectionArgs);
break;
case ELEMENT_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO,
values,
Consts.COLUMN_ID + "=" + id,
null);
} else {
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO,
values,
Consts.COLUMN_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection) {
String[] available = {
Consts.COLUMN_USER,
Consts.COLUMN_ID
};
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
// Check if all columns which are requested are available
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}
}
}
UPDATE
ContentProviders are generic and I agree on this.
They can be adapted.
So I adapted mine to handle SearchView data content.
Below is the entire ContentProvider acting as both a SearchView content provider and a ListView cursor adapter for anyone interested:
package org.jefferyemanuel.database;
import java.util.Arrays;
import java.util.HashSet;
import org.jefferyemanuel.bulkwebsites.Consts;
import org.jefferyemanuel.bulkwebsites.Utils;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
public class WebSitesContentProvider extends ContentProvider {
/* define out search provider structures */
// UriMatcher constant for search suggestions
private static final int SEARCH_SUGGEST = 1;
private static final String[] SEARCH_SUGGEST_COLUMNS = {
BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID
};
// database
private UserDatabaseHelper database;
// Used for the UriMacher
private static final int TOTAL_ELEMENTS = 10;
private static final int ELEMENT_ID = 20;
private static final String BASE_PATH = "websites";
public static final Uri CONTENT_URI = Uri.parse("content://" + Consts.AUTHORITY + "/" + BASE_PATH);
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/website";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + Consts.TABLE_WEBSITES_INFO;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(Consts.AUTHORITY, BASE_PATH, TOTAL_ELEMENTS);
sURIMatcher.addURI(Consts.AUTHORITY, BASE_PATH + "/#", ELEMENT_ID);
sURIMatcher.addURI(Consts.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
sURIMatcher.addURI(Consts.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
}
#Override
public boolean onCreate() {
database = new UserDatabaseHelper(getContext());
return false;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// UsIng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// Check if the caller has requested a column which does not exists
checkColumns(projection);
// Set the table
queryBuilder.setTables(Consts.TABLE_WEBSITES_INFO);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case TOTAL_ELEMENTS:
break;
case SEARCH_SUGGEST:
queryBuilder.appendWhere(Consts.COLUMN_NAME + " LIKE '%" + uri.getLastPathSegment() + "%'");
break;
case ELEMENT_ID:
// Adding the ID to the original query
queryBuilder.appendWhere(Consts.COLUMN_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);
/*
* If this request is from a SearchView then convert cursor to search Matrix cursor.
*/
if (uriType == SEARCH_SUGGEST)
cursor = buildSearchMatrixCursorFromStandardCursor(cursor);
// Make sure that potential listeners are getting notified
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
int uriType = sURIMatcher.match(uri);
SQLiteDatabase sqlDB = database.getWritableDatabase();
int rowsDeleted = 0;
long id = 0;
switch (uriType) {
case TOTAL_ELEMENTS:
id = sqlDB.insert(Consts.TABLE_WEBSITES_INFO, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_PATH + "/" + id);
}
#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 TOTAL_ELEMENTS:
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO, selection, selectionArgs);
break;
case ELEMENT_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO,
Consts.COLUMN_ID + "=" + id,
null);
} else {
rowsDeleted = sqlDB.delete(Consts.TABLE_WEBSITES_INFO,
Consts.COLUMN_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 TOTAL_ELEMENTS:
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO, values,
selection, selectionArgs);
break;
case ELEMENT_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO, values,
Consts.COLUMN_ID + "=" + id,
null);
} else {
rowsUpdated = sqlDB.update(Consts.TABLE_WEBSITES_INFO, values,
Consts.COLUMN_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection) {
String[] available = { Consts.COLUMN_NAME, Consts.COLUMN_ID };
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
// Check if all columns which are requested are available
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}
}
/*
* KEY METHOD THAT USES THE DATA FROM DATABASE THAT LISTVIEW ALSO USES
* TO CREATE A MATRIX CURSOR TO SEND BACK TO SEARCHVIEW
*/
private Cursor buildSearchMatrixCursorFromStandardCursor(Cursor cursor) {
MatrixCursor cursorSearch = new MatrixCursor(SEARCH_SUGGEST_COLUMNS);
int id = 0;
int index = cursor.getColumnIndex(Consts.COLUMN_NAME);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String name = cursor.getString(index);
cursorSearch.addRow(new String[] {
String.valueOf(++id),
name,
name,
name,
SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT
});
cursor.moveToNext();
}
return cursorSearch;
}
}
The ContentProvider you're using is generic and can be used for a search feature.
You may think at first that maybe you should write a ContentProvider that specifically works for search but:
You will have to duplicate a lot of code.
ContentProviders are not designed to be specific, they just abstract the access to data.
Your "Search Content Provider" will be used to make queries and you already have that with the Content Provider you have.
The ContentProvider doesn't need to be specific to search.
On the other hand, your Adpater should be specific.

Categories

Resources