Let's start with a multi-module project:
F:\work> mkdir ProviderTest
F:\work> cd ProviderTest
F:\work\ProviderTest> mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
2: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> galatea-archetype (null)
Confirm properties configuration:
groupId: org.eoti.android.providertest.provider
artifactId: TestProvider
version: 1.0-SNAPSHOT
package: org.eoti.android.providertest.provider
F:\work\ProviderTest> mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
2: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> galatea-archetype (null)
Confirm properties configuration:
groupId: org.eoti.android.providertest.consumer
artifactId: TestConsumer
version: 1.0-SNAPSHOT
package: org.eoti.android.providertest.consumer
F:\work\ProviderTest> mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
2: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> galatea-archetype (null)
Confirm properties configuration:
groupId: org.eoti.android.providertest.generator
artifactId: TestGenerator
version: 1.0-SNAPSHOT
package: org.eoti.android.providertest.generator
Create a top-level pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.eoti.android.providertest</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>ProviderTest</artifactId>
<packaging>pom</packaging>
<name>ProviderTest</name>
<description>Testing a simple Content Provider and consumer</description>
<modules>
<module>TestProvider</module>
<module>TestConsumer</module>
<module>TestGenerator</module>
</modules>
</project>
Open the TestProvider\pom.xml and TestConsumer\pom.xml and add a parent to the pom.xml:
<parent>
<groupId>org.eoti.android.providertest</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>ProviderTest</artifactId>
</parent>
F:\work\ProviderTest> mvn clean install
Replace TestProviderActivity.java with TestProvider.java:
package org.eoti.android.providertest.provider;
import android.app.Activity;
import android.content.*;
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.os.Bundle;
import android.provider.BaseColumns;
import android.provider.LiveFolders;
import android.text.TextUtils;
import android.util.Log;
import java.util.HashMap;
public class TestProvider
extends ContentProvider
{
// Derived from http://developer.android.com/resources/samples/NotePad/src/com/example/android/notepad/NotePadProvider.html
public static final String AUTHORITY = "org.eoti.android.providertest";
private static class Logs implements BaseColumns
{
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/logs");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.eoti.logs";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.eoti.log";
public static final String DEFAULT_SORT_ORDER = "modified DESC";
public static final String LOG = "log";
public static final String TIMESTAMP = "timestamp";
}
private static String TAG = "TestProvider";
private static final String DATABASE_NAME = "providertest.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "logs";
private static final int LOGS = 1;
private static final int LOG_ID = 2;
private static final int LIVE_FOLDER_LOGS = 3;
private static HashMap<String, String> logsProjectionMap;
private static HashMap<String, String> folderProjectionMap;
private static final UriMatcher uriMatcher;
private static class DatabaseHelper extends SQLiteOpenHelper
{
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
+ Logs._ID + " INTEGER PRIMARY KEY,"
+ Logs.LOG + " TEXT,"
+ Logs.TIMESTAMP + " INTEGER"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
private DatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = 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);
switch (uriMatcher.match(uri)) {
case LOGS:
qb.setProjectionMap(logsProjectionMap);
break;
case LOG_ID:
qb.setProjectionMap(logsProjectionMap);
qb.appendWhere(Logs._ID + "=" + uri.getPathSegments().get(1));
break;
case LIVE_FOLDER_LOGS:
qb.setProjectionMap(folderProjectionMap);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
String orderBy = (TextUtils.isEmpty(sortOrder) ? Logs.DEFAULT_SORT_ORDER : sortOrder);
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case LOGS:
case LIVE_FOLDER_LOGS:
return Logs.CONTENT_TYPE;
case LOG_ID:
return Logs.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
if (uriMatcher.match(uri) != LOGS)
throw new IllegalArgumentException("Unknown URI " + uri);
ContentValues values = (initialValues == null) ? new ContentValues() : new ContentValues(initialValues);
if(!values.containsKey(Logs.TIMESTAMP))
values.put(Logs.TIMESTAMP, System.currentTimeMillis());
if(!values.containsKey(Logs.LOG))
values.put(Logs.LOG, "");
SQLiteDatabase db = dbHelper.getWritableDatabase();
long rowId = db.insert(TABLE_NAME, Logs.LOG, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(Logs.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (uriMatcher.match(uri)) {
case LOGS:
count = db.delete(TABLE_NAME, where, whereArgs);
break;
case LOG_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(TABLE_NAME, Logs._ID + "=" + noteId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (uriMatcher.match(uri)) {
case LOGS:
count = db.update(TABLE_NAME, values, where, whereArgs);
break;
case LOG_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(TABLE_NAME, values, Logs._ID + "=" + noteId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "logs", LOGS);
uriMatcher.addURI(AUTHORITY, "logs/#", LOG_ID);
uriMatcher.addURI(AUTHORITY, "livefolders/logs", LIVE_FOLDER_LOGS);
logsProjectionMap = new HashMap<String,String>();
logsProjectionMap.put(Logs._ID, Logs._ID);
logsProjectionMap.put(Logs.LOG, Logs.LOG);
logsProjectionMap.put(Logs.TIMESTAMP, Logs.TIMESTAMP);
folderProjectionMap = new HashMap<String, String>();
folderProjectionMap.put(LiveFolders._ID, Logs._ID + " AS " + LiveFolders._ID);
folderProjectionMap.put(LiveFolders.NAME, Logs.LOG + " AS " + LiveFolders.NAME);
}
}
Change your TestProvider\AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eoti.android.providertest.provider">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name="TestProvider" android:authorities="org.eoti.android.providertest"/>
</application>
</manifest>
Replace TestConsumerActivity.java with TestConsumer.java:
package org.eoti.android.providertest.consumer;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.LiveFolders;
public class TestConsumer extends Activity
{
public static final String AUTHORITY = "org.eoti.android.providertest";
private static String TAG = "TestConsumer";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/livefolders/logs");
public static final Uri LOG_URI = Uri.parse("content://" + AUTHORITY + "/logs/#");
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
final String action = intent.getAction();
if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action))
{
final Intent liveFolderIntent = new Intent();
liveFolderIntent.setData(CONTENT_URI);
liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, getString(R.string.app_name));
liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon));
liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);
liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, new Intent(Intent.ACTION_EDIT, LOG_URI));
setResult(RESULT_OK, liveFolderIntent);
} else {
setResult(RESULT_CANCELED);
}
finish();
}
}
Change your TestConsumer\AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eoti.android.providertest.consumer">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name="TestConsumer" android:label="@string/app_name" android:icon="@drawable/icon">
<intent-filter>
<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
Update your TestGeneratorActivity:
package org.eoti.android.providertest.generator;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestGeneratorActivity extends Activity {
public static final String AUTHORITY = "org.eoti.android.providertest";
private static String TAG = "TestGenerator";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/logs");
private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ContentValues values = new ContentValues();
long time = System.currentTimeMillis();
values.put("timestamp", time);
values.put("log", sdf.format(new Date(time)));
Uri uri = getContentResolver().insert(CONTENT_URI, values);
Toast.makeText(getApplicationContext(), (uri == null) ? " null " : uri.toString(), Toast.LENGTH_SHORT).show();
finish();
}
}
Redeploy (mvn clean install). Launch the generator app. Add the live folder to your desktop. Launch it. Run the generator a few more times. Launch the folder again.
Oops, db.execSQL("DROP TABLE IF EXISTS notes"); could have been db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
ReplyDelete