vnd.android.cursor.item for a single record,
@@ -581,7 +564,7 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
* Open a file blob associated with a content URI.
* This method can be called from multiple
- * threads, as described inentity
+ * threads, as described in
* Application Fundamentals:
* Processes and Threads.
*
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 403c4d87a4cd7..0858ea51c76cc 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -89,16 +89,7 @@ public class ContentProviderClient {
return mContentProvider.openAssetFile(url, mode);
}
- /**
- * see {@link ContentProvider#queryEntities}
- * @hide
- */
- public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
- String sortOrder) throws RemoteException {
- return mContentProvider.queryEntities(uri, selection, selectionArgs, sortOrder);
- }
-
- /** see {@link ContentProvider#applyBatch} */
+ /** see {@link ContentProvider#applyBatch} */
public ContentProviderResult[] applyBatch(ArrayListType: String (blob)
*/ public static final String SYNC_STATE = "sync_state"; + + /** + * The account that was used to sync the entry to the device. + *Type: TEXT
+ */ + public static final String _SYNC_ACCOUNT = "_sync_account"; + + /** + * The type of the account that was used to sync the entry to the device. + *Type: TEXT
+ */ + public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type"; + + /** + * The unique ID for a row assigned by the sync source. NULL if the row has never been synced. + *Type: TEXT
+ */ + public static final String _SYNC_ID = "_sync_id"; + + /** + * The last time, from the sync source's point of view, that this row has been synchronized. + *Type: INTEGER (long)
+ */ + public static final String _SYNC_TIME = "_sync_time"; + + /** + * The version of the row, as assigned by the server. + *Type: TEXT
+ */ + public static final String _SYNC_VERSION = "_sync_version"; + + /** + * Used in temporary provider while syncing, always NULL for rows in persistent providers. + *Type: INTEGER (long)
+ */ + public static final String _SYNC_LOCAL_ID = "_sync_local_id"; + + /** + * Used only in persistent providers, and only during merging. + *Type: INTEGER (long)
+ */ + public static final String _SYNC_MARK = "_sync_mark"; + + /** + * Used to indicate that local, unsynced, changes are present. + *Type: INTEGER (long)
+ */ + public static final String _SYNC_DIRTY = "_sync_dirty"; } /** * Contains a list of available calendars. */ - public static class Calendars implements BaseColumns, SyncConstValue, CalendarsColumns + public static class Calendars implements BaseColumns, CalendarsColumns { public static final Cursor query(ContentResolver cr, String[] projection, String where, String orderBy) @@ -523,8 +577,182 @@ public final class Calendar { /** * Contains one entry per calendar event. Recurring events show up as a single entry. */ - public static final class Events implements BaseColumns, SyncConstValue, - EventsColumns, CalendarsColumns { + public static final class EventsEntity implements BaseColumns, EventsColumns, CalendarsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = Uri.parse("content://calendar/event_entities"); + + public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) { + return new EntityIteratorImpl(cursor, resolver); + } + + public static EntityIterator newEntityIterator(Cursor cursor, + ContentProviderClient provider) { + return new EntityIteratorImpl(cursor, provider); + } + + private static class EntityIteratorImpl extends CursorEntityIterator { + private final ContentResolver mResolver; + private final ContentProviderClient mProvider; + + private static final String[] REMINDERS_PROJECTION = new String[] { + Reminders.MINUTES, + Reminders.METHOD, + }; + private static final int COLUMN_MINUTES = 0; + private static final int COLUMN_METHOD = 1; + + private static final String[] ATTENDEES_PROJECTION = new String[] { + Attendees.ATTENDEE_NAME, + Attendees.ATTENDEE_EMAIL, + Attendees.ATTENDEE_RELATIONSHIP, + Attendees.ATTENDEE_TYPE, + Attendees.ATTENDEE_STATUS, + }; + private static final int COLUMN_ATTENDEE_NAME = 0; + private static final int COLUMN_ATTENDEE_EMAIL = 1; + private static final int COLUMN_ATTENDEE_RELATIONSHIP = 2; + private static final int COLUMN_ATTENDEE_TYPE = 3; + private static final int COLUMN_ATTENDEE_STATUS = 4; + private static final String[] EXTENDED_PROJECTION = new String[] { + ExtendedProperties.NAME, + ExtendedProperties.VALUE, + }; + private static final int COLUMN_NAME = 0; + private static final int COLUMN_VALUE = 1; + + public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) { + super(cursor); + mResolver = resolver; + mProvider = null; + } + + public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) { + super(cursor); + mResolver = null; + mProvider = provider; + } + + public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException { + // we expect the cursor is already at the row we need to read from + final long eventId = cursor.getLong(cursor.getColumnIndexOrThrow(Events._ID)); + ContentValues cv = new ContentValues(); + cv.put(Events._ID, eventId); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HTML_URI); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TITLE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DESCRIPTION); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_LOCATION); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, STATUS); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SELF_ATTENDEE_STATUS); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, COMMENTS_URI); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTSTART); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBILITY); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, TRANSPARENCY); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, + HAS_EXTENDED_PROPERTIES); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RRULE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RDATE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_EVENT); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, + ORIGINAL_INSTANCE_TIME); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_DATE); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, HAS_ATTENDEE_DATA); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, + GUESTS_CAN_INVITE_OTHERS); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_MODIFY); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_SEE_GUESTS); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.URL); + + Entity entity = new Entity(cv); + Cursor subCursor; + if (mResolver != null) { + subCursor = mResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, + "event_id=" + eventId, null, null); + } else { + subCursor = mProvider.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, + "event_id=" + eventId, null, null); + } + try { + while (subCursor.moveToNext()) { + ContentValues reminderValues = new ContentValues(); + reminderValues.put(Reminders.MINUTES, subCursor.getInt(COLUMN_MINUTES)); + reminderValues.put(Reminders.METHOD, subCursor.getInt(COLUMN_METHOD)); + entity.addSubValue(Reminders.CONTENT_URI, reminderValues); + } + } finally { + subCursor.close(); + } + + if (mResolver != null) { + subCursor = mResolver.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, + "event_id=" + eventId, null /* selectionArgs */, null /* sortOrder */); + } else { + subCursor = mProvider.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, + "event_id=" + eventId, null /* selectionArgs */, null /* sortOrder */); + } + try { + while (subCursor.moveToNext()) { + ContentValues attendeeValues = new ContentValues(); + attendeeValues.put(Attendees.ATTENDEE_NAME, + subCursor.getString(COLUMN_ATTENDEE_NAME)); + attendeeValues.put(Attendees.ATTENDEE_EMAIL, + subCursor.getString(COLUMN_ATTENDEE_EMAIL)); + attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP, + subCursor.getInt(COLUMN_ATTENDEE_RELATIONSHIP)); + attendeeValues.put(Attendees.ATTENDEE_TYPE, + subCursor.getInt(COLUMN_ATTENDEE_TYPE)); + attendeeValues.put(Attendees.ATTENDEE_STATUS, + subCursor.getInt(COLUMN_ATTENDEE_STATUS)); + entity.addSubValue(Attendees.CONTENT_URI, attendeeValues); + } + } finally { + subCursor.close(); + } + + if (mResolver != null) { + subCursor = mResolver.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION, + "event_id=" + eventId, null /* selectionArgs */, null /* sortOrder */); + } else { + subCursor = mProvider.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION, + "event_id=" + eventId, null /* selectionArgs */, null /* sortOrder */); + } + try { + while (subCursor.moveToNext()) { + ContentValues extendedValues = new ContentValues(); + extendedValues.put(ExtendedProperties.NAME, cursor.getString(COLUMN_NAME)); + extendedValues.put(ExtendedProperties.VALUE, + cursor.getString(COLUMN_VALUE)); + entity.addSubValue(ExtendedProperties.CONTENT_URI, extendedValues); + } + } finally { + subCursor.close(); + } + + cursor.moveToNext(); + return entity; + } + } + } + + /** + * Contains one entry per calendar event. Recurring events show up as a single entry. + */ + public static final class Events implements BaseColumns, EventsColumns, CalendarsColumns { private static final String[] FETCH_ENTRY_COLUMNS = new String[] { Events._SYNC_ACCOUNT, Events._SYNC_ID }; diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 30c73d79f489d..80d5d08be8ec3 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -24,12 +24,15 @@ import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.CursorEntityIterator; +import android.content.EntityIterator; +import android.content.Entity; import android.content.res.Resources; import android.database.Cursor; +import android.database.DatabaseUtils; import android.graphics.Rect; import android.net.Uri; import android.os.RemoteException; -import android.provider.ContactsContract.CommonDataKinds.Email; import android.text.TextUtils; import android.util.Pair; import android.view.View; @@ -984,7 +987,7 @@ public final class ContactsContract { * removes the raw contact from its aggregate contact. * The sync adapter then deletes the raw contact from the server and * finalizes phone-side deletion by calling {@code resolver.delete(...)} - * again and passing the {@link #CALLER_IS_SYNCADAPTER} query parameter.+ * again and passing the {@link ContactsContract#CALLER_IS_SYNCADAPTER} query parameter.
*
Some sync adapters are read-only, meaning that they only sync server-side * changes to the phone, but not the reverse. If one of those raw contacts * is marked for deletion, it will remain on the phone. However it will be @@ -1366,6 +1369,107 @@ public final class ContactsContract { */ public static final String DATA_ID = "data_id"; } + + public static EntityIterator newEntityIterator(Cursor cursor) { + return new EntityIteratorImpl(cursor); + } + + private static class EntityIteratorImpl extends CursorEntityIterator { + private static final String[] DATA_KEYS = new String[]{ + Data.DATA1, + Data.DATA2, + Data.DATA3, + Data.DATA4, + Data.DATA5, + Data.DATA6, + Data.DATA7, + Data.DATA8, + Data.DATA9, + Data.DATA10, + Data.DATA11, + Data.DATA12, + Data.DATA13, + Data.DATA14, + Data.DATA15, + Data.SYNC1, + Data.SYNC2, + Data.SYNC3, + Data.SYNC4}; + + public EntityIteratorImpl(Cursor cursor) { + super(cursor); + } + + public android.content.Entity getEntityAndIncrementCursor(Cursor cursor) + throws RemoteException { + final int columnRawContactId = cursor.getColumnIndexOrThrow(RawContacts._ID); + final long rawContactId = cursor.getLong(columnRawContactId); + + // we expect the cursor is already at the row we need to read from + ContentValues cv = new ContentValues(); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _ID); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, VERSION); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SOURCE_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC1); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC2); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC3); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC4); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED); + android.content.Entity contact = new android.content.Entity(cv); + + // read data rows until the contact id changes + do { + if (rawContactId != cursor.getLong(columnRawContactId)) { + break; + } + // add the data to to the contact + cv = new ContentValues(); + cv.put(Data._ID, cursor.getLong(cursor.getColumnIndexOrThrow(Entity.DATA_ID))); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, + Data.RES_PACKAGE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Data.MIMETYPE); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, Data.IS_PRIMARY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, + Data.IS_SUPER_PRIMARY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, Data.DATA_VERSION); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, + CommonDataKinds.GroupMembership.GROUP_SOURCE_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, + Data.DATA_VERSION); + for (String key : DATA_KEYS) { + final int columnIndex = cursor.getColumnIndexOrThrow(key); + if (cursor.isNull(columnIndex)) { + // don't put anything + } else { + cv.put(key, cursor.getString(columnIndex)); + } + // TODO: go back to this version of the code when bug + // http://b/issue?id=2306370 is fixed. +// if (cursor.isNull(columnIndex)) { +// // don't put anything +// } else if (cursor.isLong(columnIndex)) { +// values.put(key, cursor.getLong(columnIndex)); +// } else if (cursor.isFloat(columnIndex)) { +// values.put(key, cursor.getFloat(columnIndex)); +// } else if (cursor.isString(columnIndex)) { +// values.put(key, cursor.getString(columnIndex)); +// } else if (cursor.isBlob(columnIndex)) { +// values.put(key, cursor.getBlob(columnIndex)); +// } + } + contact.addSubValue(ContactsContract.Data.CONTENT_URI, cv); + } while (cursor.moveToNext()); + + return contact; + } + + } } /** @@ -4326,6 +4430,41 @@ public final class ContactsContract { * The MIME type of a single group. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/group"; + + public static EntityIterator newEntityIterator(Cursor cursor) { + return new EntityIteratorImpl(cursor); + } + + private static class EntityIteratorImpl extends CursorEntityIterator { + public EntityIteratorImpl(Cursor cursor) { + super(cursor); + } + + public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException { + // we expect the cursor is already at the row we need to read from + final ContentValues values = new ContentValues(); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, _ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, ACCOUNT_NAME); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, ACCOUNT_TYPE); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, DIRTY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, VERSION); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SOURCE_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, RES_PACKAGE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, TITLE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, TITLE_RES); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, GROUP_VISIBLE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SYNC1); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SYNC2); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SYNC3); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SYNC4); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SYSTEM_ID); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, DELETED); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, NOTES); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SHOULD_SYNC); + cursor.moveToNext(); + return new Entity(values); + } + } } /** diff --git a/preloaded-classes b/preloaded-classes index f86555cf0230f..c6d0bf9c1d624 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -136,7 +136,6 @@ android.content.ContentProvider android.content.ContentProvider$Transport android.content.ContentProviderClient android.content.ContentProviderNative -android.content.ContentProviderNative$IEntityIteratorImpl android.content.ContentProviderOperation android.content.ContentProviderProxy android.content.ContentProviderResult @@ -151,7 +150,6 @@ android.content.Entity android.content.IContentProvider android.content.IContentService$Stub android.content.IContentService$Stub$Proxy -android.content.IEntityIterator$Stub android.content.IIntentReceiver$Stub android.content.IIntentSender$Stub android.content.ISyncAdapter$Stub diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/ExportTestResolver.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/ExportTestResolver.java index 481c10f429944..38aed6f5cdd8d 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/vcard/ExportTestResolver.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/ExportTestResolver.java @@ -110,21 +110,6 @@ import java.util.List; return contactEntry; } - @Override - public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, - String sortOrder) { - mTestCase.assertTrue(uri != null); - mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())); - final String authority = uri.getAuthority(); - mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority)); - mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection)); - mTestCase.assertEquals(1, selectionArgs.length); - int id = Integer.parseInt(selectionArgs[0]); - mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size()); - - return new MockEntityIterator(mContactEntryList.get(id).getList()); - } - @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java index c13d060bdd681..2382b42df7090 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java @@ -96,15 +96,6 @@ class MockContentProvider extends ContentProvider { throw new UnsupportedOperationException("unimplemented mock method"); } - /** - * @hide - */ - @Override - public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs, - String sortOrder) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - @Override public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("unimplemented mock method");