Content Providers are again one of the fundamental building blocks of the Android Platform. So far, we have looked at Activities, Services, which are basic building blocks. We have also looked at intents, notificationsand variations of the same. None of these have dealt with stored data. Content Provider brings us to the concept of how to deal with data stored in the Android mobile esp. if the data has to be shared across applications.
Before we move on to shared data, let us understand the basics of data storage support provided by Android.
There are 4 ways we can store data:
- 1. Preferences
- 2. Files
- 3. RDBMS (SQLite)
- 4. Network
Preferences Preferences are used to store user preferences for a single application or across applications in a mobile. They are typically name-value pairs accessible to the context.
Files Files can be stored directly on to the mobile or to extended storage mediums. They are by default not accessible to applications other than the one that created it.
Database(RDBMS) Android support creation of databases based on SQLite. These are again private to the application that created it.
Network Android provides API support to store data over the network on to a server, probably
Note that all these are various forms of storing data and most often for a single application. Irrespective of how data is stored, if it has to be shared across applications, Here comes the concept of Content Providers.
Content Providers are the only way to share data across Android applications. They store and retrieve data thus making it accessible to all. Android platform provides default implementations of content providers for data types like audio, video, images, contact information etc.
In the sample I would like to first show how to work with existing content providers like the contact information. We will first view the existing contacts on the phone. We will insert a new contact (hardcoded name and phone number), update the same contact and delete the same contact, in this example. Note that the update and delete will not work till we create a contact through this example.
Introduction to Content Providers:
Irrespective of how the data is stored, Content Providers give a uniform interface to access the data. Data is exposed as a simple table with rows and columns where row is a record and column is a particular data type with a specific meaning. Like a row could be about a single person and the columns could be the persons first name, number, address, email id etc.
Each record is identified by a unique _ID field which is the key to the record. Each content provider exposes a unique URI that identifies its data set uniquely. This URI is equivalent to a table name in a database. The URI consists of various parts: eg: content://com.colllabera.labs.sai/tasks/123is a unique URI. content:// is a standard prefix. com.collabera.labs.saiis the authority, tasks is the table name, 123 is the unique _ID.
For the native content providers, these unique URIs are declared as constants in an interface. So, in our program we will be using constants like People.CONTENT_URI which internally translates to content://contacts/people
Let us now look at the code to view all the existing contacts:
//Here is the button to click for viewing the contacts
Button view = (Button)findViewById(R.id.viewButton);
//The method / class that gets invoked when the View button is clicked
view.setOnClickListener(new OnClickListener() {
public void onClick(View v){
displayContacts();
Log.i("NativeContentProvider", "Completed Displaying Contact list");
}
});
//Here is the displayContacts() method
private void displayContacts() {
String[] columns = new String[] {People.NAME,People.NUMBER};
Uri mContacts = People.CONTENT_URI;
Cursor mCur = managedQuery(mContacts, // Contact URI
columns, // Which columns to return
null, // Which rows to return
null, // Where clause parameters
null // Order by clause
);
if (mCur.moveToFirst()) {
String name = null;
String phoneNo = null;
do {
name = mCur.getString(mCur.getColumnIndex(People.NAME));
phoneNo = mCur.getString(mCur.getColumnIndex(People.NUMBER));
Toast.makeText(NativeContentProvider.this, name + " " + phoneNo, Toast.LENGTH_SHORT).show();
} while (mCur.moveToNext());
}
}
Here we are using the Activity.managedQuery(..)to create and execute a query against the provided URI. The comments against the parameters in the code is self-explanatory. This returns a cursorobject that can be iterated using the two methods moveToFirst()and moveToNext(). For simplicity sake, I have just toasted the contact name and phone number retrieved. An advanced tutorial can start a new activity that can display this in a ListView.
Now, we can move on to creating a new contact. While the button related code will be very similar to the above, let us look at the actual createContact() method.
private voidcreateContact(String name, String phone) {
ContentValues contact = new ContentValues();
contact.put(People.NAME, name);
insertUri = getContentResolver().insert(People.CONTENT_URI, contact);
Log.d(getClass().getSimpleName(),insertUri.toString());
Uri phoneUri = Uri.withAppendedPath(insertUri, People.Phones.CONTENT_DIRECTORY);
contact.clear();
contact.put(People.Phones.TYPE, People.TYPE_MOBILE);
Continuing my tutorials on some UI related stuff
In Part 16 & 17, I spoke about Simple ListView and Custom ListView. In Part 18, though it seems like a tutorial on Threads and Handlers, I have touched upon ProgressDialog, another view.
Here I would like to talk on the DatePicker and TimePicker that are bundled with the SDK. Both are very similar. So, I will talk only about DatePicker, but the sample code will have both.
Typically, we would want an end user to be able to set a date though a DatePickerDialog that pops-up on some user action. So, I have a button Set Date on the click of which a DatePickerDialog pops-up. Once the user selects a date and sets it, it is displayed back in a TextView field.
So, the code associated with the button is:
pickDate.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(DATE_DIALOG_ID);
}
});
showDialog(
) is a method available on an Activity Itself. It just takes an integer to help decide which dialog should actually be shown. This is decided in the onCreateDialog(
) method that gets automatically invoked when showDialog(
) is called.
Here is the code for the same:
protected Dialog onCreateDialog(int id){
switch(id) {
case DATE_DIALOG_ID:
return new DatePickerDialog(this, dateListener, year, month, day);
case
.
}
return null;
}
In this method, when the int passed is DATE_DIALOG_ID, a new DatePickerDialog is passed along with the values for year, month and day. Where did I set values for these? In the onCreate(
) method itself, I have done this:
final Calendar cal = Calendar.getInstance();
year = cal.get(Calendar.YEAR);
month = cal.get(Calendar.MONTH);
day = cal.get(Calendar.DAY_OF_MONTH);
Also note that a handle to a listener is expected to be given to the DatePickerDialog. I will come to this listener a little later.
I have also updated the Date - TextView with these values in the updateDate() method which is invoked in the onCreate(
) method itself.
private void updateTime() {
timeDisplay.setText(new StringBuilder().append(hours).append(:)
.append(min));
}
So far, what I have done is shown how to create a Dialog and set the date.
Once the dialog shows up, when a user sets the date and clicks set, the listener that is invoked in on the DatePickerDialog. The method is onDateSetListener whose handle is passed during the creation of the DatePickerDialog itself.
private DatePickerDialog.OnDateSetListener dateListener =
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int yr, int monthOfYear, int dayOfMonth) {
year = yr;
month = monthOfYear;
day = dayOfMonth;
updateDate();
}
};
In this method, on the click on the dialog, we are setting the day, month, year values to the new values that the user selected and also calling the updateDate() method to refresh the TextView with what the user selected.
It is this simple.
Same thing can be done with the TimePicker as well. The only difference is that you can pass a Boolean variable to say whether you want the time in 24 hour or 12 hour pattern.
Here is the complete code for the same.
Read More..