Search This Blog

Friday, November 27, 2009

First Application_Part 2 (Content Provider)

We will need to store and retrieve data in our application and what Android can do in this area is really impressive, at least for me ;). we can use both file or database to store and manipulate information... android has provided a mechanism called 'ContentProvider', it's actually kind of Facade which helps to keep the interface of data communication structure independent from whatever is happening behind the scene , whether you are using database or file or if you are using caching or data validation...the interface will be always the same....

I'm gonna use database facilities of Android for this application, speaking of database...the first question that might pop into your mind would be 'what about a mapping tool?' especially if your are an enterprise system developer i bet you cannot even imagine working with database without Hibernate or at least IBatis... but we should remember that we are dealing with mobile phones and not those incredibly powerful servers which are used in enterprise systems... i mean even the ability to work with database in these tiny devices really amazes me specially when you find out there is actually some kind of simple mapping structure to standardize and simplify working with database in android...

OK..let's see what i am talking about. we want to store the result of calculation and a title which user assigns to that using our 'Save Result Dialog'. So what will be storing in database are the result, title and some general information like , say , creation date and modification date.
We will need a class like this :



package com.amir.calculator;

import android.net.Uri;
import android.provider.BaseColumns;

public class CalcContent {

public static final String AUTHORITY = "com.amir.calculator.CalcContent";


private CalcContent() {}


public static final class Content implements BaseColumns {

private Content() {}

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


public static final String CONTENT_ITEM_TYPE = "vnd.amir.item/vnd.calc.content";


public static final String DEFAULT_SORT_ORDER = "modified DESC";


public static final String NOTE = "note";

public static final String VALUE = "value";

public static final String CREATED_DATE = "created";

public static final String MODIFIED_DATE = "modified";
}


}



CONTENT_URI represents the Identity of our Data Type and will be used
wherever we need to work with this type of data as we will see soon.

Next step would be Content Provider :


package com.amir.calculator;


import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;

public class CalcProvider extends ContentProvider {


private final static String DB_NAME = "Calc.db";
private final static String TABLE_NAME = "CalcNotes";
private DatabaseHelper helper;


private static class DatabaseHelper extends SQLiteOpenHelper {

DatabaseHelper(Context context) {
super(context, DB_NAME , null,1);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
+ CalcContent.Content._ID + " INTEGER PRIMARY KEY,"
+ CalcContent.Content.NOTE + " TEXT,"
+ CalcContent.Content.VALUE + " NUMBER,"
+ CalcContent.Content.CREATED_DATE + " INTEGER,"
+ CalcContent.Content.MODIFIED_DATE + " INTEGER"
+ ");");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("MyProvider", "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}


@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
return 0;
}

@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}

@Override
public Uri insert(Uri uri, ContentValues initialValues) {

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(CalcContent.Content.CREATED_DATE) == false) {
values.put(CalcContent.Content.CREATED_DATE, now);
}

if (values.containsKey(CalcContent.Content.MODIFIED_DATE) == false) {
values.put(CalcContent.Content.MODIFIED_DATE, now);
}

if (values.containsKey(CalcContent.Content.NOTE) == false) {
values.put(CalcContent.Content.NOTE, "");
}

if(values.containsKey(CalcContent.Content.VALUE) == false){
values.put(CalcContent.Content.VALUE,0.0);
}

SQLiteDatabase db = helper.getWritableDatabase();
long rowId = db.insert(TABLE_NAME, CalcContent.Content.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(CalcContent.Content.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}

throw new SQLException("Failed to insert row into " + uri);

}

@Override
public boolean onCreate() {
this.helper = new DatabaseHelper(getContext());
return true;
}

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

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
SQLiteDatabase db = helper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);

// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);

return c;

}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}

}



From now on any application which want to work with 'CalcContent' Data Type can simply use this Content Provider.the last thing we need to do is to define this Content Provider in Android Manifest file so that activities can use it. it would be pretty easy :


<provider android:name="CalcProvider"
android:authorities="com.amir.calculator.CalcContent"
/>

You can have most of your questions about 'ContentProvider' answered here. although i believe as long as you are not writing some real code you don't really know what's your question ;)

No comments: