310 likes | 458 Views
Cosc 5/4730. Android Content Providers and Intents. Content Providers. Any Uri that begins with content:// is retrieved with a content provider. The data is encapsulated using a Uri instance You don't know where it is, nor care actually
E N D
Cosc5/4730 Android Content Providers and Intents
Content Providers • Any Uri that begins with content:// is retrieved with a content provider. • The data is encapsulated using a Uri instance • You don't know where it is, nor care actually • Could be a SQLite database, files, or retrieved from off the device • With a Uri, you perform basic CRUD operations using the content provider • CRUD = create, read, update, delete
URI • Examples: • content://media/internal/images • return the list of all internal images on the device. • content://contacts/people/ • return the list of all contact names on the device. • content://contacts/people/45 • return the single result row, the contact with ID=45. • content://mms • returns a list of the mms messages (content://mms/part/<partID> gets the message) • content://constants/5 • return the constant number 5 • content://edu.cs4730.provider.csteach/csteach • a custom content provider covered later in the lecture.
URI (2) • android.providers package provide some ready made URIs, example: • ContactsContract.Contacts.CONTENT_URI • MediaStore.Images.Media.INTERNAL_CONTENT_URI • Otherwise, use Uri.parse(String); Uri CONTENT_URI = Uri.parse("content://"+ PROVIDER_NAME + "/test");
Contacts Example • Simple code to read the contacts • Note, need at least these permissions • <uses-permission android:name="android.permission.GET_ACCOUNTS" /> • <uses-permission android:name="android.permission.READ_CONTACTS" /> Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI; String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; Cursor c = managedQuery(CONTENT_URI, projection, null, null, null); if (c.moveToFirst()) { do { String str = "Id: " + c.getString(0); str += "Name: " + c.getString(1); } while (c.moveToNext()); }
Custom Content Providers • SQLite databases are private only to the application that created it. • While custom content providers are way to make them accessible to other app's.
Custom Content Providers (2) • Extend the ContentProvider class and implement the required methods. • getType(): Returns the MIME type of the data at the given URI. • onCreate(): Called when the provider is being started. • query(): Receives a request from a client. The result is returned as a Cursor object. • insert(): Inserts a new record into the content provider. • delete(): Deletes an existing record from the content provider. • update(): Updates an existing record from the content provider.
Custom Content Providers (3) • With our content provider we can choose how to store the data: • file system, xml, database, or on the internet • For this lecture example, we use a database, based of the sqlite lecture previously. • The example code for sqlite is on the website as well as this example.
Creating a Custom Content Provider • create a class that extends ContentProvider • Using eclipse you can also have to create the necessary methods. • In our case: • public class cpProvider extends ContentProvider { • Most of the methods inside, we implement as a pass through allowing the user to access the database. • They maybe a security risk and for sensitive info, a really bad idea and probably not public anyway. • We also need the some constants the database and the DatabaseHelper (not shown in these slides) for database.
Constants we need public static final String PROVIDER_NAME = "edu.cs4730.provider.csteach"; public static final Uri CONTENT_URI = Uri.parse("content://"+ PROVIDER_NAME + "/csteach"); public static final String _ID = "_id"; public static final String NAME = "name"; public static final String IP = "ip"; public static final String OWNER = "owner";
UriMatcher private static final int CSTEACH = 1; private static final int CSTEACH_ID = 2; private static final UriMatcheruriMatcher; static{ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "csteach", CSTEACH); uriMatcher.addURI(PROVIDER_NAME, "csteach/#", CSTEACH_ID); } • Observe from the code above that you use a UriMatcher object to parse the content URI that is passed to the content provider through a Content Resolver. For example, the following content URI represents a request for all rows in the content provider: content://edu.cs4730.provider.csteach/csteach • In contrast, the following represents a request for a particular row with _id=5: content://edu.cs4730.provider.csteach/csteach/5
override getType() • You'll need to override the getType() method so it uniquely describes the data type for your content provider. Using the UriMatcher object, you will return "vnd.android.cursor.item/vnd.cs4730.csteach" for a single row, and "vnd.android.cursor.dir/vnd.cs4730.csteach" for multiple rows: @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { // get all rows case CSTEACH: return "vnd.android.cursor.dir/vnd.cs4730.csteach "; // get a particular row case CSTEACH_ID: return "vnd.android.cursor.item/vnd.csc4730.csteach "; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } }
The Rest @Override public booleanonCreate() { Context context = getContext(); dbHelper = new DatabaseHelper(context); db = dbHelper.getWritableDatabase(); return db != null; } @Override public Uri insert(Uri uri, ContentValues values) { //add a new entry long rowID = db.insert(DATABASE_TABLE, "", values); //check for success if (rowID>0){ Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); return _uri; } throw new SQLException("Failed to insert row into " + uri); }
The Rest (2) @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuildersqlBuilder = new SQLiteQueryBuilder(); sqlBuilder.setTables(DATABASE_TABLE); if (uriMatcher.match(uri) == CSTEACH_ID) //if a particular row sqlBuilder.appendWhere( _ID + " = " + uri.getPathSegments().get(1)); if (sortOrder==null || sortOrder=="") //set a default sort order if none is provided. sortOrder = NAME; //then pass everything to the query builder. Cursor c = sqlBuilder.query( db, projection, selection, selectionArgs, null, null, sortOrder); //---register to watch a content URI for changes--- c.setNotificationUri(getContext().getContentResolver(), uri); return c; }
The Rest (3) @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case CSTEACH: count = db.update(DATABASE_TABLE, values, selection, selectionArgs); break; case CSTEACH_ID: count = db.update(DATABASE_TABLE, values, _ID + " = " + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException( "Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; }
The Rest (4) @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case CSTEACH: count = db.delete(DATABASE_TABLE, selection, selectionArgs); break; case CSTEACH_ID: String id = uri.getPathSegments().get(1); count = db.delete(DATABASE_TABLE, _ID + " = " + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; }
Register the provider • In the AndroidManifest.xml file • add the class the is the provider and register the content string <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".cpDemo" android:label="@string/app_name"> … </activity> <provider android:name="cpProvider" android:authorities="edu.cs4730.provider.csteach" /> </application>
Activity class • Now in the activity class we can use it. • The example code has more examples, but two to give you a favor //add a value ContentValues values = new ContentValues(); values.put("name", "k2"); values.put("ip", "129.72.216.12"); values.put("owner", "cosc"); Uri uri = getContentResolver().insert(Uri.parse("content://edu.cs4730.provider.csteach/csteach"), values); // list all the entries Uri allrows = Uri.parse("content://edu.cs4730.provider.csteach/csteach"); Cursor c = managedQuery(allrows, null, null, null, null); if (c.moveToFirst()) { do { output.append("\n" + c.getString(0) + ", " + c.getString(1) + ", " + c.getString(2) + ", " + c.getString(3)); } while (c.moveToNext()); }
References • Android developer pages • http://developer.android.com/intl/zh-CN/reference/android/provider/package-summary.html • Using and Implementing Content Providers in Android • http://www.devx.com/wireless/Article/41133/1763/page/1
android Intents
Intents • As we saw in the Android GUI lecture intents can be used for message passing between activities. • Used to launch an Activity as well. • Also used to launch services and broadcast receives too. • An Intent is a message object holding passive data about an operation to be performed • Or with broadcast receives about something that has already happened.
Making a call example • The intent is pretty simple Intent dialIntent = new Intent( "android.intent.action.CALL", Uri.parse("tel:3075555555")); • Use “android.intent.action.DIAL” • To just bring up the dialer, but not call. startActivity(dialIntent); • Needs <uses-permission android:name= "android.permission.CALL_PHONE"> </uses-permission> • In the manifest.xml file. http://developer.android.com/reference/android/Manifest.permission.html
Web browser intent • To start up the browser with a page location you specify • Intent dialIntent = new Intent( "android.intent.action.VIEW", Uri.parse("http://www.cs.uwyo.edu")); • startActivity(dialIntent); <uses-permission android:name="android.permission.INTERNET"></uses-permission>
Other “standard” intents • You can start up the maps with an intent • intent = new Intent(Intent.ACTION_VIEW,Uri.parse("geo:41.312927,105.587251?z=19")); • Should show Laramie on the map, but force closes on the simulator. • Launch the camera • intent = new Intent("android.media.action.IMAGE_CAPTURE"); • <uses-permission android:name="android.permission.CAMERA"></uses-permission> • Show contacts • intent = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/")); • <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
Returning data. • Instead of startActivity, use • startActivityForResult(intent, resultCode); • resultCode is a number you pick, that you can identify the callback with. • Override onActivityResult(intrequestCode, intresultCode, Intent data) method.
Passing data to a new activity. • Create an intent • Intent i = new Intent(this, ActivityTwo.class); • i.putExtra(“key1”, “Some data”); • i.putExtra(“key2”, “more data”); • Where key1, key2 are names both activities know. • startActivityForResult(i, REQUEST_CODE); • Assumes we want return data, otherwise use startActivity.
Passing data to a new activity. (2) • ActivityTwo • In the onCreate method Bundle extras = getIntent().getExtras(); //Make sure the activity was called correctly. if (extras == null) {return;} String value1 = extras.getString(“key1"); String value2 = extras.getString(“key2"); • Like others, many getX methods, like getInt(String key)
Return data. • When activityTwo finishes, it can return an Intent with data. • In the finish() method @Override public void finish() { Intent data = new Intent(); data.putExtra("returnKey1", “some data "); data.putExtra("returnKey2", “more data"); setResult(RESULT_OK, data); super.finish(); } • Result: constants are RESULT_OK, RESULT_CANCELED, but you can also use any custom result with an int. • When an activity fails, crashes, the result will be RESULT_CANCELED.
Return data (2) • In the Calling activity @Override protected void onActivityResult(intrequestCode, intresultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) { //remember if RESULT_CANCELED, likely no data in the intent! if (data.hasExtra("returnKey1")) { Toast.makeText(this, data.getExtras().getString("returnKey1"), Toast.LENGTH_SHORT).show(); } } else if (resultCode == RESULT_CANCELED && requestCode == REQUEST_CODE} Toast.makeText(this, “ActivityTwo canceled!”, Toast.LENGTH_SHORT).show(); }
Finally. • You can see these intents working in the code provided with the lecture. • For more of google applications intents see • http://developer.android.com/guide/appendix/g-app-intents.html
Q A &