sqlite database is private to the application. So if you want to access your sqlite database in any other application, you can use content provider. Even there are some benifits of using content provider private to your app e.g. if you are using CursorLoader or sync adapter in your app.
First we need to declare content provider in AndroidMenifest.xml file
These are minimum required attributes to declare your content provider.
android:name – name of the subclass of ContentProvider
android:authorities – list of the authorities
other attributes that you can use
android:authorities="list"
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:grantUriPermissions=["true" | "false"]
android:icon="drawable resource"
android:initOrder="integer"
android:label="string resource"
android:multiprocess=["true" | "false"]
android:name="string"
android:permission="string"
android:process="string"
android:readPermission="string"
android:syncable=["true" | "false"]
android:writePermission="string"
http://developer.android.com/guide/topics/manifest/provider-element.html
android:enable – Whether or not the content provider can be instantiated by the system. You can also toggle this from code PackageManager and setComponentEnabledSetting()
android:exported- if you want to create content provider private to your app, just set adnroid:exported = false;
android:grantUriPermissions – the app with full access of content provider can grant temporary access to other app.
e.g.
Granting permission:
uri = "content://com.vimviv.mailprovider/attachments/42";
Context.grantUriPermission("com.example.test", uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
or
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
android:icon- icon for content provider.
android:label – label for content provider.
android:multiprocess – ‘true’ if content provider instance can run in multi process.
android:readPermission – Permission that client must have to read this content provider.
android:writePermission : Permission that client must have to change data of this content provider
android:permission : permission for read or write
e.g.
android:process - process in which content provider will run.
Content Uri:
We can identify data in content provider using content uri. It has following structure.
content://AUTHORITY/path
content://AUTHORITY/path/id
Authority: a symbolic unique name of content provider.
path: name of the table or file.
id: id of the single record
Uri Matcher:
Provides a mechanism to identify all the incoming uri patterns. e.g. content://AUTHORITY/table_name/21 matches a content uri of single record with id 21.
private static final UriMatcher sUriMatcher; private static final int INCOMING_CONTACT_COLLECTION_URI_INDICATOR = 1; private static final int INCOMING_SINGLE_CONTACT_URI_INDICATOR = 2; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(AUTHORITY, "contacts", INCOMING_CONTACT_COLLECTION_URI_INDICATOR); sUriMatcher.addURI(AUTHORITY, "contacts/#", INCOMING_SINGLE_CONTACT_URI_INDICATOR); } switch (sUriMatcher.match(uri)) { // If the incoming URI was for all of table contact case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: // do something break; // If the incoming URI was for a single row case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: id = uri.getLastPathSegment(); break; default: // URI is not recognized }
Mimetype:
getType method returns mimetype of data returned by the content URI argument.
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.vimviv.provider.contact"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.vimviv.provider.contact"; @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: return CONTENT_TYPE; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: return CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } }
Database columns:
public class ContactColumns implements BaseColumns { private ContactColumns() {} public static final String DEFAULT_SORT_ORDER = "name ASC"; //Additional Columns start here. public static final String NAME = "name"; public static final String PHONE_NUMBER = "phoneNumber"; public static final String EMAIL = "email"; public static final String ADDRESS = "address"; public static final String DOB = "dob"; //Integer from System.currentTimeMillis() public static final String CREATED_DATE = "created"; //Integer from System.currentTimeMillis() public static final String MODIFIED_DATE = "modified"; }
Database helper class:
create database helper class for create and upgrade database. /** * Setup/Create Database This class helps open, create, and upgrade the * database file. */ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { Log.d(TAG, "inner oncreate called"); db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + ContactColumns._ID + " INTEGER PRIMARY KEY," + ContactColumns.NAME + " TEXT," + ContactColumns.PHONE_NUMBER + " TEXT," + ContactColumns.EMAIL + " TEXT," + ContactColumns.ADDRESS + " TEXT," + ContactColumns.DOB + " INTEGER," + ContactColumns.CREATED_DATE + " INTEGER," + ContactColumns.MODIFIED_DATE + " INTEGER" + ");"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.d(TAG, "inner onupgrade called"); Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } }
projection map:
private static HashMapsContactsProjectionMap; static { sContactsProjectionMap = new HashMap (); sContactsProjectionMap.put(ContactColumns._ID, ContactColumns._ID); // name, phone number, email,address sContactsProjectionMap.put(ContactColumns.NAME, ContactColumns.NAME); sContactsProjectionMap.put(ContactColumns.PHONE_NUMBER, ContactColumns.PHONE_NUMBER); sContactsProjectionMap.put(ContactColumns.ADDRESS, ContactColumns.ADDRESS); sContactsProjectionMap.put(ContactColumns.EMAIL, ContactColumns.EMAIL); sContactsProjectionMap.put(ContactColumns.DOB, ContactColumns.DOB); // created date, modified date sContactsProjectionMap.put(ContactColumns.CREATED_DATE, ContactColumns.CREATED_DATE); sContactsProjectionMap.put(ContactColumns.MODIFIED_DATE, ContactColumns.MODIFIED_DATE); }
Now we will override methods
query:
In query method we will filter uri using UriMatcher, if query is for single row, we will first get the id from uri using getLastPathSegment() method, after that append a where clause in sql query.
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: qb.setTables(TABLE_NAME); qb.setProjectionMap(sContactsProjectionMap); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: qb.setTables(TABLE_NAME); qb.setProjectionMap(sContactsProjectionMap); qb.appendWhere(ContactColumns._ID + "=" + uri.getLastPathSegment()); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // If no sort order is specified use the default String orderBy; if (TextUtils.isEmpty(sortOrder)) { orderBy = ContactColumns.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } // Get the database and run the query SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); return c; }
delete:
delete data on the basis of where argument,
@Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: count = db.delete(TABLE_NAME, where, whereArgs); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: String rowId = uri.getPathSegments().get(1); count = db.delete(TABLE_NAME, ContactColumns._ID + "=" + rowId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; }
insert:
Insert data on the basis of contentvalues argument and return the uri of newly inserted row.
@Override public Uri insert(Uri uri, ContentValues initialValues) { // Validate the requested uri if (sUriMatcher.match(uri) != INCOMING_CONTACT_COLLECTION_URI_INDICATOR) { throw new IllegalArgumentException("Unknown URI " + uri); } ContentValues values; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } Long now = Long.valueOf(System.currentTimeMillis()); // Make sure that the fields are all set if (values.containsKey(ContactColumns.CREATED_DATE) == false) { values.put(ContactColumns.CREATED_DATE, now); } if (values.containsKey(ContactColumns.MODIFIED_DATE) == false) { values.put(ContactColumns.MODIFIED_DATE, now); } if (values.containsKey(ContactColumns.NAME) == false) { throw new SQLException( "Failed to insert row because Name is needed " + uri); } if (values.containsKey(ContactColumns.PHONE_NUMBER) == false) { values.put(ContactColumns.PHONE_NUMBER, "Unknown phone number"); } if (values.containsKey(ContactColumns.EMAIL) == false) { values.put(ContactColumns.EMAIL, "Unknown email"); } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId = db.insert(TABLE_NAME, ContactColumns.NAME, values); if (rowId > 0) { Uri insertedUri = ContentUris.withAppendedId( CONTENT_URI, rowId); return insertedUri; } throw new SQLException("Failed to insert row into " + uri); }
update:
update data using contentvalues and where arguments.
@Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: count = db.update(TABLE_NAME, values, where, whereArgs); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: String rowId = uri.getPathSegments().get(1); count = db.update(TABLE_NAME, values, ContactColumns._ID + "=" + rowId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; }
Now we will create ContectDataSouce class for contact operations like addContact, deleteContact.
ContactDataSource.java
public class ContactDataSource{ Context mContext; public ContactDataSource(Context context){ mContext = context; } private static final String TAG = "ContactDataSource"; // Adding new contact public void addContact(ContactsData contact){ Log.d(TAG,"Adding a book"); ContentValues values = new ContentValues(); values.put(ContactColumns.NAME, contact.mName); // Contact Name values.put(ContactColumns.PHONE_NUMBER, contact.mPhoneNumber); // Contact phone number values.put(ContactColumns.EMAIL, contact.mEmail); // Contact email values.put(ContactColumns.ADDRESS, contact.mAddress); // Contact address values.put(ContactColumns.DOB, contact.mDob); // Contact address // Inserting Row ContentResolver cr = mContext.getContentResolver(); Uri uri = ContactProvider.CONTENT_URI; Log.d(TAG,"book insert uri:" + uri); Uri insertedUri = cr.insert(uri, values); Log.d(TAG,"inserted uri:" + insertedUri); } // Adding new contact public ContactsData getContact(long contactId){ Log.d(TAG,"Adding a book"); ContentResolver cr = mContext.getContentResolver(); Uri uri = ContactProvider.CONTENT_URI; Uri selUri = Uri.withAppendedPath(uri, Long.toString(contactId)); Cursor c = cr.query(selUri, null, null, null, null); c.moveToFirst(); ContactsData contact = new ContactsData(); contact.mName = c.getString(c.getColumnIndex(ContactColumns.NAME)); contact.mPhoneNumber = c.getString(c.getColumnIndex(ContactColumns.PHONE_NUMBER)); contact.mEmail = c.getString(c.getColumnIndex(ContactColumns.EMAIL)); contact.mAddress = c.getString(c.getColumnIndex(ContactColumns.ADDRESS)); contact.mDob = c.getLong(c.getColumnIndex(ContactColumns.DOB)); return contact; } // Getting All Contacts public ListgetAllContacts() { List contactList = new ArrayList (); Uri uri = ContactProvider.CONTENT_URI; Activity a = (Activity)mContext; Cursor cursor = a.managedQuery(uri, null, //projection null, //selection string null, //selection args array of strings null); //sort order while (cursor.moveToNext()) { ContactsData contact = new ContactsData(Integer.parseInt( cursor.getString(cursor.getColumnIndex(ContactColumns._ID))), cursor.getString(cursor.getColumnIndex(ContactColumns.NAME)), cursor.getString(cursor.getColumnIndex(ContactColumns.PHONE_NUMBER)), cursor.getString(cursor.getColumnIndex(ContactColumns.EMAIL)), cursor.getString(cursor.getColumnIndex(ContactColumns.ADDRESS)), cursor.getLong(cursor.getColumnIndex(ContactColumns.DOB)) ); // Adding contact to list contactList.add(contact); } // return contact list return contactList; } // Getting contacts Count public int getContactsCount() { Uri uri = ContactProvider.CONTENT_URI; Activity a = (Activity)mContext; Cursor c = a.managedQuery(uri, null, //projection null, //selection string null, //selection args array of strings null); //sort order int numberOfRecords = c.getCount(); c.close(); return numberOfRecords; } // Deleting single contact public void deleteContact(long contactId) { ContentResolver cr = mContext.getContentResolver(); Uri uri = ContactProvider.CONTENT_URI; Uri delUri = Uri.withAppendedPath(uri, Long.toString(contactId)); Log.d(TAG, "Del Uri:" + delUri); cr.delete(delUri, null, null); } }
Complete code of ContactProvider.java
public class ContactProvider extends ContentProvider { private static final String TAG = "ContactProvider"; public static final String DATABASE_NAME = "contact.db"; public static final String TABLE_NAME = "contacts"; public static final String AUTHORITY = "com.vimviv.provider"; //uri and MIME type definitions public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/contacts"); public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.vimviv.provider.contact"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.vimviv.provider.contact"; //version 2: added dob column public static final int DATABASE_VERSION = 2; // Setup projection Map // Projection maps are similar to "as" (column alias) construct // in an sql statement where by you can rename the // columns. private static HashMapsContactsProjectionMap; static { sContactsProjectionMap = new HashMap (); sContactsProjectionMap.put(ContactColumns._ID, ContactColumns._ID); // name, phone number, email,address sContactsProjectionMap.put(ContactColumns.NAME, ContactColumns.NAME); sContactsProjectionMap.put(ContactColumns.PHONE_NUMBER, ContactColumns.PHONE_NUMBER); sContactsProjectionMap.put(ContactColumns.ADDRESS, ContactColumns.ADDRESS); sContactsProjectionMap.put(ContactColumns.EMAIL, ContactColumns.EMAIL); sContactsProjectionMap.put(ContactColumns.DOB, ContactColumns.DOB); // created date, modified date sContactsProjectionMap.put(ContactColumns.CREATED_DATE, ContactColumns.CREATED_DATE); sContactsProjectionMap.put(ContactColumns.MODIFIED_DATE, ContactColumns.MODIFIED_DATE); } // Setup URIs // Provide a mechanism to identify // all the incoming uri patterns. private static final UriMatcher sUriMatcher; private static final int INCOMING_CONTACT_COLLECTION_URI_INDICATOR = 1; private static final int INCOMING_SINGLE_CONTACT_URI_INDICATOR = 2; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(AUTHORITY, "contacts", INCOMING_CONTACT_COLLECTION_URI_INDICATOR); sUriMatcher.addURI(AUTHORITY, "contacts/#", INCOMING_SINGLE_CONTACT_URI_INDICATOR); } /** * Setup/Create Database This class helps open, create, and upgrade the * database file. */ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { Log.d(TAG, "inner oncreate called"); db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + ContactColumns._ID + " INTEGER PRIMARY KEY," + ContactColumns.NAME + " TEXT," + ContactColumns.PHONE_NUMBER + " TEXT," + ContactColumns.EMAIL + " TEXT," + ContactColumns.ADDRESS + " TEXT," + ContactColumns.DOB + " INTEGER," + ContactColumns.CREATED_DATE + " INTEGER," + ContactColumns.MODIFIED_DATE + " INTEGER" + ");"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.d(TAG, "inner onupgrade called"); Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } } private DatabaseHelper mOpenHelper; @Override public boolean onCreate() { Log.d(TAG, "main onCreate called"); mOpenHelper = new DatabaseHelper(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: qb.setTables(TABLE_NAME); qb.setProjectionMap(sContactsProjectionMap); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: qb.setTables(TABLE_NAME); qb.setProjectionMap(sContactsProjectionMap); qb.appendWhere(ContactColumns._ID + "=" + uri.getLastPathSegment()); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // If no sort order is specified use the default String orderBy; if (TextUtils.isEmpty(sortOrder)) { orderBy = ContactColumns.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } // Get the database and run the query SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); return c; } @Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: count = db.delete(TABLE_NAME, where, whereArgs); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: String rowId = uri.getPathSegments().get(1); count = db.delete(TABLE_NAME, ContactColumns._ID + "=" + rowId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: return CONTENT_TYPE; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: return CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public Uri insert(Uri uri, ContentValues initialValues) { // Validate the requested uri if (sUriMatcher.match(uri) != INCOMING_CONTACT_COLLECTION_URI_INDICATOR) { throw new IllegalArgumentException("Unknown URI " + uri); } ContentValues values; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } Long now = Long.valueOf(System.currentTimeMillis()); // Make sure that the fields are all set if (values.containsKey(ContactColumns.CREATED_DATE) == false) { values.put(ContactColumns.CREATED_DATE, now); } if (values.containsKey(ContactColumns.MODIFIED_DATE) == false) { values.put(ContactColumns.MODIFIED_DATE, now); } if (values.containsKey(ContactColumns.NAME) == false) { throw new SQLException( "Failed to insert row because Name is needed " + uri); } if (values.containsKey(ContactColumns.PHONE_NUMBER) == false) { values.put(ContactColumns.PHONE_NUMBER, "Unknown phone number"); } if (values.containsKey(ContactColumns.EMAIL) == false) { values.put(ContactColumns.EMAIL, "Unknown email"); } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId = db.insert(TABLE_NAME, ContactColumns.NAME, values); if (rowId > 0) { Uri insertedUri = ContentUris.withAppendedId( CONTENT_URI, rowId); return insertedUri; } throw new SQLException("Failed to insert row into " + uri); } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case INCOMING_CONTACT_COLLECTION_URI_INDICATOR: count = db.update(TABLE_NAME, values, where, whereArgs); break; case INCOMING_SINGLE_CONTACT_URI_INDICATOR: String rowId = uri.getPathSegments().get(1); count = db.update(TABLE_NAME, values, ContactColumns._ID + "=" + rowId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; } }