ZOFTINO.COM android and web dev tutorials

Android Content Provider

If your application has data that is useful to other apps on a device, your app can share the data with other apps using content provider. Android provides a mechanism or framework to allow apps share data with other apps. The components involved in enabling content sharing in android are ContentProvider, ContentResolver and android system.

The app that wants to share data with other apps needs to provide ContentProvider and register it with android system by adding it to manifest file.The apps that want to access the data provided by other apps need to send request to content providers using content resolvers.

Android system keeps tracks of all content providers from all apps installed on a device by reading manifest files. Android system maps data access request from content resolver to the right content provider using content URI.

Content provider can restrict access to it by listing required permission in the manifest file. Content resolver that has access permissions for a particular content provider can perform read and/or write operations on it.

Content provider like activity and service components, needs to be added to manifest file so that android system can keep track of content providers.

Why Do You Need Content Provider

If your app has data that need to be shared with other apps, then only you need to provide content provider. Data to be shared can be in any format, file or structured data in tables. For own use within app, you don’t need to provide content provider.

You also need to provider content provider, if you need some features in your app like widgets, CursorLoader, Synchronizing app data with server and custom search suggestions.

How to Create Content Provider

Once you decided that your app needs content provider in order to share data with other apps or provide features, you need to analyze data, decide on data storage, design data and create content provider.

To create content provider, first you need to decide on data storage to be used to store your app data. The decision depends on type of data that your app needs to store. Data stores can be relational, key-value, file based or net work based. Android system provides API to access relational database SQLite and file data.

Then you need to implement ContentProvider interface. ContentProvider is the interface other application interact with to read and write data. Your ContentProvider should implement insert, update, delete, query and getType methods. You also need to implement a call back method onCreate() which android system calls when content provider is created.

You need to come up with authority name and content Uri. Authority name identifies content provider and content Uri (authority + path) identifies data in a provider.

Content Uri example: “content://com.zoftino.coupon.provider/coupon/1”, here authority is com.zoftino.coupon.provider, table name is coupon and 1 points to first record.

You need to create contract class that contains content Uri, column name and MIME types. Contract class is used by consumer apps. It helps them know about details of content provider in your apps. See below example contract class CouponsContract.java.

Finally you need to add content provider to manifest file using provider element. Using provider element attributes, you can set permissions and other configuration.

How to Get Access to Content Providers

Client app which uses a particular content provider needs to request for required permission in order for the app to be able to access the content provider data. At the time of installation of your app, user allows or denies requested content provider permissions mentioned in your app manifest file.

Content provider client apps need information about content provider like content Uri, columns and MIME types. The best way to get this information is to use contract class provided by content provider app.

You need to use ContentResolver to interact with content provider passing content Uri. Android system identifies right content provider from available content providers using content Uri.

How to Read, Write and Delete Content Provider Data

To get data from content provider, first you need to construct query which is similar to SQL query. You can see in the example. You need to call query() method of content resolver passing content Uri and query to get data from content provider. Query() method return cursor that can be used either to display result or retrieve data for processing.

You can display results in UI using ListView, cursor, simple cursor adaptor. To display cursor results in listview using simple cursor adaptor, content should hava _id primary key. Or you can retrieve the data from cursor by calling appropriate get methods of cursor object.

In the same way, you can insert, update and delete data by calling insert(), update() and delete() methods, see below example.

How to do Batch Operations

You can perform batch operation to insert large number of rows or to group operation as transaction. To perform batch operation, you need to create array of ContentProviderOperation each object representing an operation and then call applyBatch method of content resolver.

Data Access Using Intents

An app can access data from content provider using intent. The app that matches to the requested intent opens its flow allowing user to select content of interest. Once selection of data is complete, the app that processed the intent sends result intent back to your app with content Uri granting temporary read or write permissions to the selected content Uri. Your app receives the result intent and retrieves content Uri. Using the content Uri, you can get data from content provider. This way instead of getting permanent permissions to access complete data, you get temporary permission to access select data.

Content Provider Example

I am going to show how to create a content provider using coupons app. Other apps can display latest coupons provided by coupons app using coupons content provider.

Content Provider

 package com.zoftino.content;


import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.Nullable;

public class CouponsContentProvider extends ContentProvider {

    private CouponSQLiteOpenHelper sqLiteOpenHelper;

    private static final String COUPONS_DBNAME = "zoftino";

    private static final String COUPON_TABLE = "coupon";

    private SQLiteDatabase cpnDB;

    private static final String SQL_CREATE_COUPON = "CREATE TABLE " +
            COUPON_TABLE +
            "(" +
            "_id INTEGER PRIMARY KEY, " +
            "STORE TEXT, " +
            "COUPON TEXT, " +
            "EXPIRES TEXT)";

    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        uriMatcher.addURI("com.zoftino.coupon.provider", COUPON_TABLE, 1);
    }
    @Override
    public boolean onCreate() {
        //this way db create or open is delayed till getWritableDatabase() is called frist time
        sqLiteOpenHelper = new CouponSQLiteOpenHelper( getContext(), COUPONS_DBNAME, SQL_CREATE_COUPON );
        return true;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,
                                String sortOrder) {

        String tableNme = "";
        switch(uriMatcher.match(uri)){
            case 1 :
                tableNme = COUPON_TABLE;
                break;
            default:
                return null;
        }

        cpnDB = sqLiteOpenHelper.getWritableDatabase();

        Cursor cursor = (SQLiteCursor)cpnDB.query(tableNme, projection, selection, selectionArgs,
                null, null, sortOrder);
        return cursor;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {

        String tableNme = "";
        switch(uriMatcher.match(uri)){
            case 1 :
                tableNme = COUPON_TABLE;
                break;
            default:
                return null;
        }

        cpnDB = sqLiteOpenHelper.getWritableDatabase();
        long rowid = cpnDB.insert(tableNme, null, contentValues);
        return getContentUriRow(rowid);
    }

    @Override
    public int delete(Uri uri, String where, String[] selectionArgs) {
        String tableNme = "";
        switch(uriMatcher.match(uri)){
            case 1 :
                tableNme = COUPON_TABLE;
                break;
            default:
                return 0;
        }

        cpnDB = sqLiteOpenHelper.getWritableDatabase();

        return cpnDB.delete(tableNme, where, selectionArgs);
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String where, String[] selectionArgs) {
        String tableNme = "";
        switch(uriMatcher.match(uri)){
            case 1 :
                tableNme = COUPON_TABLE;
                break;
            default:
                return 0;
        }
        cpnDB = sqLiteOpenHelper.getWritableDatabase();
        return cpnDB.update(tableNme,contentValues,where,selectionArgs );
    }
    private Uri getContentUriRow(long rowid){
     return Uri.fromParts("com.zoftino.coupon.provider", COUPON_TABLE, Long.toString(rowid));
    }
}
 

SQLiteOpenHelper

 package com.zoftino.content;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


  public class CouponSQLiteOpenHelper extends SQLiteOpenHelper {

    private String sql;
    CouponSQLiteOpenHelper(Context context, String dbName, String msql) {
        super(context, dbName, null, 1);
        sql = msql;
    }

    public void onCreate(SQLiteDatabase db) {
        db.execSQL(sql);
    }

      @Override
      public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

      }
  }
 

Contract Class

 package com.zoftino.content;

import android.net.Uri;

public class CouponsContract {

    public static final String Table_COUPON = "coupon";

    public static final String Column_ID = "_id";
    public static final String Column_STORE = "STORE";
    public static final String Column_COUPON = "COUPON";
    public static final String Column_EXPIRY = "EXPIRES";


    public static final String AUTHORITY = "com.zoftino.coupon.provider";

    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);

    public static final Uri CONTENT_URI =
            Uri.withAppendedPath(AUTHORITY_URI, Table_COUPON);
}
 

Manifest.xml

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zoftino.content">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".CouponsContentResolverActivity"></activity>
        <provider
            android:authorities="com.zoftino.coupon.provider"
            android:name=".CouponsContentProvider"></provider>
    </application>   

</manifest>
 

Content Resolver Activity

 package com.zoftino.content;

import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ListView;

public class CouponsContentResolverActivity extends AppCompatActivity {

    ListView cpnLst;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_resolver);

        cpnLst = (ListView) findViewById(R.id.couponsList);
    }

    public void viewCouponsFromCouponsContentProvider(View view){

        Cursor cursor = getCouponsFromProvider();

        String[] cursorColumns =
                {
                        CouponsContract.Column_STORE,
                        CouponsContract.Column_COUPON,
                        CouponsContract.Column_EXPIRY
                };

        int[] viewIds = {R.id.storeName, R.id.coupon, R.id.expirationDt};


        SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(
                getApplicationContext(),
                R.layout.coupon_row,
                cursor,
                cursorColumns,
                viewIds,
                0);

        cpnLst.setAdapter(simpleCursorAdapter);
    }
    private Cursor getCouponsFromProvider(){
        String[] mProjection =
                {
                        CouponsContract.Column_ID,
                        CouponsContract.Column_STORE,
                        CouponsContract.Column_COUPON,
                        CouponsContract.Column_EXPIRY
                };

        String mSelectionClause = CouponsContract.Column_STORE+ " = ?";;


        String[] mSelectionArgs = {"amazon"};

        String orderBy =  CouponsContract.Column_EXPIRY+" ASC";

        return getContentResolver().query(CouponsContract.CONTENT_URI,mProjection,mSelectionClause,mSelectionArgs,orderBy );
    }
    public void addCouponsToCouponsContentProvider(View view){

        ContentValues contentValues = new ContentValues();
        contentValues.put(CouponsContract.Column_ID , 2);
        contentValues.put(CouponsContract.Column_STORE , "amazon");
        contentValues.put(CouponsContract.Column_COUPON , "Get Upto 40% Off on Shoes");
        contentValues.put(CouponsContract.Column_EXPIRY , "2017/02/21");

        getContentResolver().insert(CouponsContract.CONTENT_URI, contentValues);
    }
}
 

Content Resolver Activity Layout (activity_resolver.xml)

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.zoftino.content.MainActivity">
    <Button
        android:id="@+id/button2"
        android:text="Add Coupon To Provider"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="addCouponsToCouponsContentProvider"></Button>

    <Button
        android:id="@+id/button1"
        android:text="View Coupons From Provider"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="viewCouponsFromCouponsContentProvider"></Button>
    <ListView android:id="@+id/couponsList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>
</LinearLayout>
 

coupon_row.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/coupon_row"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="@dimen/activity_vertical_margin">
    <TextView android:id="@+id/storeName" android:layout_width="100dp" android:textSize="20dp"
        android:textColor="@color/colorPrimary" android:textAlignment="center"
        android:layout_height="match_parent" android:text=""></TextView>

    <LinearLayout
        android:orientation="vertical"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_height="match_parent">
        <TextView android:id="@+id/coupon" android:layout_width="match_parent" android:textSize="15dp"
            android:layout_height="wrap_content" android:text=""></TextView>
        <TextView android:id="@+id/expirationDt" android:layout_width="match_parent"
            android:layout_height="wrap_content" android:text=""></TextView>
    </LinearLayout>
</LinearLayout>