Download using-content-providers-U08928-JAN

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Mobile business intelligence wikipedia , lookup

Data vault modeling wikipedia , lookup

PL/SQL wikipedia , lookup

Business intelligence wikipedia , lookup

Versant Object Database wikipedia , lookup

Clusterpoint wikipedia , lookup

Relational model wikipedia , lookup

Database model wikipedia , lookup

Transcript
USING CONTENT PROVIDERS
David Sutton
TOPICS COVERED THIS WEEK
 Persistence
 Introduction to databases
 Content providers
 Cursors
 Cursor adapters
 The Contacts content provider
 Extracting and displaying contact photos
PERSISTENCE
 We can use simple data structures such as arrays and ArrayLists to
store data within an application.
 The snag is that the data disappears when the application shuts
down
 We therefore need ways of making data persist.
 In general we can use
 Files (perhaps in XML format)
 Databases
 Android provides with various ways of presenting databases and files
which we will cover later.
DATABASE OPERATIONS: PROJECTION
 A projection extracts particular columns from a table
Staff
No.
First
Name
Last
Name
Staff
No.
P03489 Faye
Mitchell
P03489 Mitchell
P02345 David
Sutton
P02345 Sutton
P01234 Joe
Bloggs
P01234 Bloggs
Last
Name
DATABASE OPERATIONS: SELECTION
 A selection extracts particular rows from a table
Module Title
No.
Module
Leader
Module Title
No.
Module
Leader
U08928 Mobile
Software
P03489
U08928 Mobile
Software
P03489
U08223 Data
Structures
P02345
U08071 Networks
P03489
U08071 Networks
P03489
DATABASE OPERATIONS: JOIN
Module Title
No.
Module
Leader
Staff
No.
First
Name
U08928 Mobile
Software
P03489
P03489 Faye
Mitchell
U08223 Data
Structures
P02345
P02345 David
Sutton
U08071 Networks
P03489
P01234 Joe
Bloggs
Module Title
No.
Module
Leader
First
Name
Last Name
U08928 Mobile
Software
P03489
Faye
Mitchell
U08223 Data
Structures
P02345
David
Sutton
U08071 Networks
P03489
Faye
Mitchell
Last
Name
A join combines two
tables, usually by
taking rows from one
table and looking for
rows in the second
whose primary key
matches a foreign
key in the first
SQL
The Structured (English) Query Language, or SQL for
short. Is an almost universally used language for the
construction of relational database queries.
A projection query
SELECT ModuleNo, Title from Modules;
A selection query
SELECT * FROM Modules where Leader = "P03489";
Projection and selection in one query
SELECT ModuleNo, Title FROM Modules WHERE Leader ="P03489";
SQLITE
 SQLite is a highly popular “embedded” data base
management system.
 “Embedded” means that programs can access the
database without having to talk to some external server.
 Android is lightweight (an approx 350Kb library) and open
source.
 Included in Android and many other software systems.
PERSISTENCE IN ANDROID
 Android provides a number of ways of achieving
persistence, including:
 Shared Preferences (key-value pairs often used for
storing user preferences)
 Local files
 SQLite databases
 Content Providers (see next slide)
CONTENT PROVIDERS
 Content Providers allow applications to store, and potentially share,
data in a standard form.
 The data is wrapped up so that it looks as if it is in the form of tables
(often the data will be stored in an SQLite database, so it actually is in
that form, but it doesn’t have to be).
 Each content provider has a URI (Uniform Resource Identifier)
beginning with the scheme “content://”. For example
content://user_dictionary/words
is the URI for the Android content provider in which users may store the
spellings of any non-standard words that they might want to use.
PERMISSIONS AND
CONTENT PROVIDERS
 If an application wants to use a content provider then its manifest
must contain a <uses-permission> element that specifically says that
it needs permission to access the provider in a particular way. For
example an app that needs to read the user dictionary must request
android.permission.READ_USER_DICTIONARY
in its manifest.
 When a user installs an app they will be asked whether they want to
grant the app the permissions that its manifest says that it needs.
ANDROID NATIVE
CONTENT PROVIDERS
 Android contains “native” content providers that give access to
among, other things a user’s:
 Calendar
 Browser history
 Call log
 Contacts
 Media Store
 Device Settings
 In the rest of this week’s lecture we will create a simple application
that accesses the contacts provider.
 Next week, we will learn how to create our own content providers.
THIS WEEK’S TASK
 This week’s practical task
is to create an application
that shows a list of our
contacts and displays the
contact photo of a
selected contact.
A DIGRESSION ON TO
DEVELOPMENT METHODOLOGIES
 There are two different kinds of methodology that one can adopt when
developing a software system:
 “Waterfall” or “Big Design Up Front” methodologies require you to have a
complete design for your finished product before you begin implementing it.
 Iterative methodologies allow you to develop the project in stages. Each
stage can be a sort of mini-waterfall in which you design and implement a
system that has some, but not all of the features that you want.
The Waterfall approach does not work well in cases where unexpected
difficulties may crop up. This probably applies to anything you do as a
student, so an iterative approach is probably better for any project or
substantial coursework.
AN ITERATIVE PROTOTYPING
APPROACH TO THIS WEEKS TASK
 Rather than attempting to implement this week’s task in
one go, we will take an iterative approach and build three
versions of the project.
 Version 1: Simply lists our contacts in a text view.
 Version 2: Creates a ListView displaying our contacts.
Allows us to select and display details of any contact in
the list, but does not display the contact photo.
 Version 3: Finished system with display of contact photos.
A NOTE ON AVDS
 To test the final version of the application, you will need to
create an AVD with an SD card large enough to hold
some contact photos, and you will need to actually get
photos to put on that SD card.
 This can be a bit fiddly, so it might be better to use a
simpler AVD for the first two versions.
APPLICATION VERSION 1
Our first version of the
application will just read
the contacts database
and display the name
and id of each contact in
a TextView
This is a TextView, not a
ListView!
PROBLEMS TO SOLVE
 Our application needs to request permission access to
the Contacts Content Provider.
 It needs to find out the URI of the Contacts Content
Provider.
 It needs to work through the Contacts database and read
appropriate data from each entry.
PERMISSIONS
 Permissions for an application need to be added to the manifest. It
is probably easiest to use graphical editor for this file (you need to
select the “Permissions” tab).
 Click on “Add…” and then select “Uses Permission” from the dialog
that appears.
PERMISSIONS
 Select ‘Uses Permission” and then
pick the “READ_CONTACTS”
permission
ANDROID MANIFEST
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="brookes.ac.uk.contactlistv1"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="true”
…
FINDING THE CONTACTS PROVIDER
 The Contacts Provider exposes a number of tables to the user. The table we
will be interested in for this week’s task is the “Contacts” table.
 The ContractsContact class contains details of the “contract” between the
Contacts Content Provider and applications that use it. That is to say that it
provides details of the URIs, column names, etc. that will be user.
 The class ContactsContract.Contacts class contains details specific to the
Contacts table. It defines a number of constants including:
 CONTENT_URI the URI of the contacts table.
 DISPLAY_NAME the name of the column which contains names of
contacts
 _ID the name of a column containing a unique ID for each row
READING FROM A CONTENT
PROVIDER
 To read from a content provider we make use of a Cursor.
 A Cursor is associated with a particular query over a content provider, and at
any point in time is pointing at a particular row in that query.
Get the number of rows in the query
Move cursor to first row in query
Move cursor to next row in query
Move cursor to specified row in query
Get value at a particular column in
query (throws an exception if type is
not what was expected).
Get the index of a particular column
(e.g. column “Name” might be at index
2). Throws an exception if there is no
column with the specified name.
Cursor
getCount()
moveToFirst()
moveToNext()
moveToPosition(int position)
getString(int columnIndex)
getInt(int columnIndex)
getColumnIndexOrThrow(
String columnName)
N.B We are only showing a selection
of the methods in the Cursor class
QUERYING A CONTENT PROVIDER
AND OBTAINING A CURSOR
The Activity class contains a method managedQuery that allows us to pass a
query to a content provider and obtain Cursor over that query.
N.B. This method is deprecated in recent versions of Android. If you want to be
up to date you can use the CursorLoader class instead. We will stick with the
old fashioned version because there are more tutorial examples of its use.
public final Cursor managedQuery (Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
Parameters:
uri – the URI of the content provider
projection, selection, selectionArgs – allow us to specify a projection and/or
selection query. If they are null then the cursor is for an entire table.
sortOrder – allows us to specify the way rows in the query are to be ordered.
Can be null if we are not bothered what order rows are in.
VERSION 1 CODE
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_list_v1);
TextView contactText = (TextView) findViewById(R.id.contact_list_text);
Cursor cursor = managedQuery(
ContactsContract.Contacts.CONTENT_URI, null,null,null,null);
int idIdx = cursor.getColumnIndexOrThrow(
ContactsContract.Contacts._ID);
int nameIdx= cursor.getColumnIndexOrThrow (
ContactsContract.Contacts.DISPLAY_NAME);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(nameIdx);
int id = cursor.getInt(idIdx);
contactList += id + ". " + name + "\n”;
} while (cursor.moveToNext());
}
contactText.setText(contactList);
}
WHAT YOU SHOULD SEE WHEN
YOU RUN THE APP
VERSION 2
Version 2 of our
application will display the
contact list in a ListView.
When we click on an item
in the ListView details of
that item will be displayed
in a TextView beneath it.
This is a TextView. The information it
contains is not very useful, but it allows us to
check that our application works
This is a ListView
PROBLEMS TO BE SOLVED
 We need to bind a ListView to data returned by a Cursor.
 We need to implement an OnClickListener to display
values at a given row in the ListView.
CURSORADAPTERS
 A CursorAdapter works in a similar way as the
ArrayAdapters we have used in previous weeks. It takes
data from a Cursor and uses it to populate rows in a
ListView
ListView
CursorAdapter
Cursor
Content Provider
1 Ratty
2 Mole
3 Badger
4 Toad
5 Otter
The CursorAdapter examines each
row returned by the Cursor and
generates a View which is added
to the ListView
The Cursor allows us to
iterate through the rows
produced by a query over
the content provider.
CREATING A CURSOR ADAPTER
 The Android API includes a SimpleCursorAdapter class.
 This populates the ListView by inflating a layout file and
populating individual views in that layout with the data
from specified columns in the query.
LAYOUT FILE FOR LISTVIEW ROWS
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
This LinearLayout describes
android:layout_height="match_parent"
the arrangement of a row in
android:orientation="horizontal" >
the ListView
<TextView
This TextView will be
android:id="@+id/id_text"
populated with values from
android:layout_width="wrap_content"
the _ID column of a query.
android:layout_height="wrap_content" />
<TextView
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="20sp" />
</LinearLayout>
This TextView will be
populated with values from
the DISPLAY_NAME column
of a query.
The paddingLeft creates a
gap between it an the other
TextView
MAIN ACTIVITY (SLIDE 1)
public class ContactListV2 extends Activity {
private Cursor cursor;
private int nameIdx;
private int idIdx;
private TextView detailsText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
These are
setContentView(R.layout.activity_contact_list_v2);
the rows we
want to read
cursor = managedQuery(ContactsContract.Contacts.CONTENT_URI,
from the
null, null, null, null);
query.
These are the
TextViews that
we want to
populate
String [] from = new String[] {ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME};
int[] to = new int[] {R.id.id_text, R.id.name_text};
MAIN ACTIVITY (SLIDE 2)
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.contact_details_layout,
This is the layout file that
cursor, from, to);
describes rows in the ListView
ListView listView = (ListView) findViewById(R.id.contact_list);
listView.setAdapter(adapter);
N.B The SimpleCursorAdapter used here
is deprecated in recent versions of Android
MAIN ACTIVITY (SLIDE 3)
nameIdx = cursor.getColumnIndexOrThrow (
ContactsContract.Contacts.DISPLAY_NAME);
idIdx = cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID);
detailsText = (TextView) findViewById(R.id.details_text);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
cursor.moveToPosition(position);
String name = cursor.getString(nameIdx);
String personId = cursor.getString(idIdx);
detailsText.setText("Id: " + personId + " Name: " + name);
}
});
WHAT WE SHOULD SEE NOW
VERSION 3
Version 3 of our application will display the
contact photo of the selected icon.
PROBLEMS TO SOLVE
 Add photos to contacts (if you don’t already have them).
 Retrieve photos and display them.
ADDING PHOTOS TO THE
CONTACTS
 If you are working with the emulator, this is a bit fiddly.
 You need to make sure that your emulator has a big enough SD card
to store contact images (probably at least 500MB).
 If you want to capture photos using the camera then you also need to
set the camera on the emulator to either use your web cam, or to be
emulated.
 You can also capture images by using the emulator’s browser,
searching for images and long clicking on them. The long click will
give you the option of saving them.
After this you can find the image by going to the downloads app on
your emulator. Select the image and then click on the “Menu” button.
This should give you the option of setting the image as a contact
photo (this may not work with all image types, but JPEGs should be
fine).
READING PHOTO DATA
To read photo data we need to
 Get a URI for a particular contact. This is done by taking the URI for
the Contacts Content Provider, and adding an extra item to the path,
to indicate an individual entry.
 For example to get the first element in the contacts table
content://com.android.contacts/contacts/1
 Set up an input stream to read the photo data.
 Use that data to create a bitmap image, which we then associate with
an ImageView which we will to our layout.
CODE TO ADD TO THE MAIN
LAYOUT FILE
<LinearLayout …
<ListView
…
</ListView>
<ImageView
android:id="@+id/contact_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/sym_def_app_icon" />
<TextView
… />
</LinearLayout>
CODE TO ADD TO THE
ONITEMCLICK LISTENER
String personId = cursor.getString(idIdx);
Uri person = ContentUris.withAppendedId(
ContactsContract.Contacts.CONTENT_URI,
Long.parseLong(personId));
detailsText.setText("Id: " + personId + " Name: " + name + " URI: " + person);
InputStream photoStream =
ContactsContract.Contacts.openContactPhotoInputStream
(getContentResolver(), person);
CODE TO ADD TO THE
ONITEMCLICK LISTENER
if (photoStream != null) {
Bitmap photo = BitmapFactory.decodeStream(photoStream);
contactImage.setImageBitmap(photo);
try {
photoStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
else {
contactImage.setImageResource
(android.R.drawable.sym_def_app_icon);
}
}
});
WHAT WE SHOULD NOW SEE
When you click on an row in the list, you should now see its photo,
or a default image if there is no photo.
SUMMARY
 Persistence in Android can be achieved using files,
databases, shared preferences and/or content providers.
 A content provider is identified with a URI beginning with
the scheme content://
 Cursors allow us to examine the results of queries over
content providers.
 CursorAdapters can be used to map cursor rows on to
ListView rows.
 Contact details, including photos, can be obtained from
the Contacts content provider.