diff options
author | Fred Quintana <fredq@google.com> | 2009-12-09 16:00:31 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-12-10 10:37:52 -0800 |
commit | 2ec6c5699181316e5a5c2cd293c006ac4a8bb101 (patch) | |
tree | 5dffd05ec1b6cd3630bef911631ef2150904088a /core/java | |
parent | 8415afdb706c94cc297195a0dd5b5a62726d66e4 (diff) | |
download | frameworks_base-2ec6c5699181316e5a5c2cd293c006ac4a8bb101.zip frameworks_base-2ec6c5699181316e5a5c2cd293c006ac4a8bb101.tar.gz frameworks_base-2ec6c5699181316e5a5c2cd293c006ac4a8bb101.tar.bz2 |
am 328c0e79: - removed the concept of Entity from the ContentProvider APIs - removed the parcelling ability from Entity and EntityIterator and made them public - added an EntityIterator abstract implementation that allow easy wrapping of a Cursor - changed the VCard c
Merge commit '328c0e7986aa6bb7752ec6de3da9c999920bb55f' into eclair-mr2-plus-aosp
* commit '328c0e7986aa6bb7752ec6de3da9c999920bb55f':
- removed the concept of Entity from the ContentProvider APIs
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/content/AbstractCursorEntityIterator.java | 121 | ||||
-rw-r--r-- | core/java/android/content/AsyncQueryHandler.java | 68 | ||||
-rw-r--r-- | core/java/android/content/ContentProvider.java | 19 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderClient.java | 11 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderNative.java | 98 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 90 | ||||
-rw-r--r-- | core/java/android/content/CursorEntityIterator.java | 88 | ||||
-rw-r--r-- | core/java/android/content/Entity.aidl | 20 | ||||
-rw-r--r-- | core/java/android/content/Entity.java | 44 | ||||
-rw-r--r-- | core/java/android/content/EntityIterator.java | 3 | ||||
-rw-r--r-- | core/java/android/content/IContentProvider.java | 10 | ||||
-rw-r--r-- | core/java/android/content/IEntityIterator.java | 210 | ||||
-rw-r--r-- | core/java/android/database/DatabaseUtils.java | 96 | ||||
-rw-r--r-- | core/java/android/pim/vcard/VCardComposer.java | 15 | ||||
-rw-r--r-- | core/java/android/provider/Calendar.java | 234 | ||||
-rw-r--r-- | core/java/android/provider/ContactsContract.java | 143 |
16 files changed, 571 insertions, 699 deletions
diff --git a/core/java/android/content/AbstractCursorEntityIterator.java b/core/java/android/content/AbstractCursorEntityIterator.java deleted file mode 100644 index a804f3c..0000000 --- a/core/java/android/content/AbstractCursorEntityIterator.java +++ /dev/null @@ -1,121 +0,0 @@ -package android.content; - -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.os.RemoteException; - -/** - * An abstract class that makes it easy to implement an EntityIterator over a cursor. - * The user must implement {@link #newEntityFromCursorLocked}, which runs inside of a - * database transaction. - * @hide - */ -public abstract class AbstractCursorEntityIterator implements EntityIterator { - private final Cursor mEntityCursor; - private final SQLiteDatabase mDb; - private volatile Entity mNextEntity; - private volatile boolean mIsClosed; - - public AbstractCursorEntityIterator(SQLiteDatabase db, Cursor entityCursor) { - mEntityCursor = entityCursor; - mDb = db; - mNextEntity = null; - mIsClosed = false; - } - - /** - * If there are entries left in the cursor then advance the cursor and use the new row to - * populate mNextEntity. If the cursor is at the end or if advancing it causes the cursor - * to become at the end then set mEntityCursor to null. If newEntityFromCursor returns null - * then continue advancing until it either returns a non-null Entity or the cursor reaches - * the end. - */ - private void fillEntityIfAvailable() { - while (mNextEntity == null) { - if (!mEntityCursor.moveToNext()) { - // the cursor is at then end, bail out - return; - } - // This may return null if newEntityFromCursor is not able to create an entity - // from the current cursor position. In that case this method will loop and try - // the next cursor position - mNextEntity = newEntityFromCursorLocked(mEntityCursor); - } - mDb.beginTransaction(); - try { - int position = mEntityCursor.getPosition(); - mNextEntity = newEntityFromCursorLocked(mEntityCursor); - int newPosition = mEntityCursor.getPosition(); - if (newPosition != position) { - throw new IllegalStateException("the cursor position changed during the call to" - + "newEntityFromCursorLocked, from " + position + " to " + newPosition); - } - } finally { - mDb.endTransaction(); - } - } - - /** - * Checks if there are more Entities accessible via this iterator. This may not be called - * if the iterator is already closed. - * @return true if the call to next() will return an Entity. - */ - public boolean hasNext() { - if (mIsClosed) { - throw new IllegalStateException("calling hasNext() when the iterator is closed"); - } - fillEntityIfAvailable(); - return mNextEntity != null; - } - - /** - * Returns the next Entity that is accessible via this iterator. This may not be called - * if the iterator is already closed. - * @return the next Entity that is accessible via this iterator - */ - public Entity next() { - if (mIsClosed) { - throw new IllegalStateException("calling next() when the iterator is closed"); - } - if (!hasNext()) { - throw new IllegalStateException("you may only call next() if hasNext() is true"); - } - - try { - return mNextEntity; - } finally { - mNextEntity = null; - } - } - - public void reset() throws RemoteException { - if (mIsClosed) { - throw new IllegalStateException("calling reset() when the iterator is closed"); - } - mEntityCursor.moveToPosition(-1); - mNextEntity = null; - } - - /** - * Closes this iterator making it invalid. If is invalid for the user to call any public - * method on the iterator once it has been closed. - */ - public void close() { - if (mIsClosed) { - throw new IllegalStateException("closing when already closed"); - } - mIsClosed = true; - mEntityCursor.close(); - } - - /** - * Returns a new Entity from the current cursor position. This is called from within a - * database transaction. If a new entity cannot be created from this cursor position (e.g. - * if the row that is referred to no longer exists) then this may return null. The cursor - * is guaranteed to be pointing to a valid row when this call is made. The implementation - * of newEntityFromCursorLocked is not allowed to change the position of the cursor. - * @param cursor from where to read the data for the Entity - * @return an Entity that corresponds to the current cursor position or null - */ - public abstract Entity newEntityFromCursorLocked(Cursor cursor); -} diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java index 0a4a804..882879b 100644 --- a/core/java/android/content/AsyncQueryHandler.java +++ b/core/java/android/content/AsyncQueryHandler.java @@ -38,7 +38,6 @@ public abstract class AsyncQueryHandler extends Handler { private static final int EVENT_ARG_INSERT = 2; private static final int EVENT_ARG_UPDATE = 3; private static final int EVENT_ARG_DELETE = 4; - private static final int EVENT_ARG_QUERY_ENTITIES = 5; /* package */ final WeakReference<ContentResolver> mResolver; @@ -93,18 +92,6 @@ public abstract class AsyncQueryHandler extends Handler { args.result = cursor; break; - case EVENT_ARG_QUERY_ENTITIES: - EntityIterator iterator = null; - try { - iterator = resolver.queryEntities(args.uri, args.selection, - args.selectionArgs, args.orderBy); - } catch (Exception e) { - Log.w(TAG, e.toString()); - } - - args.result = iterator; - break; - case EVENT_ARG_INSERT: args.result = resolver.insert(args.uri, args.values); break; @@ -195,45 +182,6 @@ public abstract class AsyncQueryHandler extends Handler { } /** - * This method begins an asynchronous query for an {@link EntityIterator}. - * When the query is done {@link #onQueryEntitiesComplete} is called. - * - * @param token A token passed into {@link #onQueryComplete} to identify the - * query. - * @param cookie An object that gets passed into {@link #onQueryComplete} - * @param uri The URI, using the content:// scheme, for the content to - * retrieve. - * @param selection A filter declaring which rows to return, formatted as an - * SQL WHERE clause (excluding the WHERE itself). Passing null - * will return all rows for the given URI. - * @param selectionArgs You may include ?s in selection, which will be - * replaced by the values from selectionArgs, in the order that - * they appear in the selection. The values will be bound as - * Strings. - * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause - * (excluding the ORDER BY itself). Passing null will use the - * default sort order, which may be unordered. - * @hide - */ - public void startQueryEntities(int token, Object cookie, Uri uri, String selection, - String[] selectionArgs, String orderBy) { - // Use the token as what so cancelOperations works properly - Message msg = mWorkerThreadHandler.obtainMessage(token); - msg.arg1 = EVENT_ARG_QUERY_ENTITIES; - - WorkerArgs args = new WorkerArgs(); - args.handler = this; - args.uri = uri; - args.selection = selection; - args.selectionArgs = selectionArgs; - args.orderBy = orderBy; - args.cookie = cookie; - msg.obj = args; - - mWorkerThreadHandler.sendMessage(msg); - } - - /** * Attempts to cancel operation that has not already started. Note that * there is no guarantee that the operation will be canceled. They still may * result in a call to on[Query/Insert/Update/Delete]Complete after this @@ -340,18 +288,6 @@ public abstract class AsyncQueryHandler extends Handler { } /** - * Called when an asynchronous query is completed. - * - * @param token The token to identify the query. - * @param cookie The cookie object. - * @param iterator The iterator holding the query results. - * @hide - */ - protected void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) { - // Empty - } - - /** * Called when an asynchronous insert is completed. * * @param token the token to identify the query, passed in from @@ -408,10 +344,6 @@ public abstract class AsyncQueryHandler extends Handler { onQueryComplete(token, args.cookie, (Cursor) args.result); break; - case EVENT_ARG_QUERY_ENTITIES: - onQueryEntitiesComplete(token, args.cookie, (EntityIterator)args.result); - break; - case EVENT_ARG_INSERT: onInsertComplete(token, args.cookie, (Uri) args.result); break; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 8d90b83..91b1c4e 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -163,15 +163,6 @@ public abstract class ContentProvider implements ComponentCallbacks { selectionArgs, sortOrder); } - /** - * @hide - */ - public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, - String sortOrder) { - enforceReadPermission(uri); - return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder); - } - public String getType(Uri uri) { return ContentProvider.this.getType(uri); } @@ -477,14 +468,6 @@ public abstract class ContentProvider implements ComponentCallbacks { String selection, String[] selectionArgs, String sortOrder); /** - * @hide - */ - public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, - String sortOrder) { - throw new UnsupportedOperationException(); - } - - /** * Return the MIME type of the data at the given URI. This should start with * <code>vnd.android.cursor.item</code> for a single record, * or <code>vnd.android.cursor.dir/</code> for multiple items. @@ -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 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 403c4d8..0858ea5 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(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { return mContentProvider.applyBatch(operations); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index adc3f60..bacb684 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -106,20 +106,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } - case QUERY_ENTITIES_TRANSACTION: - { - data.enforceInterface(IContentProvider.descriptor); - Uri url = Uri.CREATOR.createFromParcel(data); - String selection = data.readString(); - String[] selectionArgs = data.readStringArray(); - String sortOrder = data.readString(); - EntityIterator entityIterator = queryEntities(url, selection, selectionArgs, - sortOrder); - reply.writeNoException(); - reply.writeStrongBinder(new IEntityIteratorImpl(entityIterator).asBinder()); - return true; - } - case GET_TYPE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); @@ -245,32 +231,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return super.onTransact(code, data, reply, flags); } - /** - * @hide - */ - private class IEntityIteratorImpl extends IEntityIterator.Stub { - private final EntityIterator mEntityIterator; - - IEntityIteratorImpl(EntityIterator iterator) { - mEntityIterator = iterator; - } - public boolean hasNext() throws RemoteException { - return mEntityIterator.hasNext(); - } - - public Entity next() throws RemoteException { - return mEntityIterator.next(); - } - - public void reset() throws RemoteException { - mEntityIterator.reset(); - } - - public void close() throws RemoteException { - mEntityIterator.close(); - } - } - public IBinder asBinder() { return this; @@ -352,64 +312,6 @@ final class ContentProviderProxy implements IContentProvider return adaptor; } - /** - * @hide - */ - public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs, - String sortOrder) - throws RemoteException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - - data.writeInterfaceToken(IContentProvider.descriptor); - - url.writeToParcel(data, 0); - data.writeString(selection); - data.writeStringArray(selectionArgs); - data.writeString(sortOrder); - - mRemote.transact(IContentProvider.QUERY_ENTITIES_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionFromParcel(reply); - - IBinder entityIteratorBinder = reply.readStrongBinder(); - - data.recycle(); - reply.recycle(); - - return new RemoteEntityIterator(IEntityIterator.Stub.asInterface(entityIteratorBinder)); - } - - /** - * @hide - */ - static class RemoteEntityIterator implements EntityIterator { - private final IEntityIterator mEntityIterator; - RemoteEntityIterator(IEntityIterator entityIterator) { - mEntityIterator = entityIterator; - } - - public boolean hasNext() throws RemoteException { - return mEntityIterator.hasNext(); - } - - public Entity next() throws RemoteException { - return mEntityIterator.next(); - } - - public void reset() throws RemoteException { - mEntityIterator.reset(); - } - - public void close() { - try { - mEntityIterator.close(); - } catch (RemoteException e) { - // doesn't matter - } - } - } - public String getType(Uri url) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index c4b0807..6e7e6d7 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -216,96 +216,6 @@ public abstract class ContentResolver { } /** - * EntityIterator wrapper that releases the associated ContentProviderClient when the - * iterator is closed. - * @hide - */ - private class EntityIteratorWrapper implements EntityIterator { - private final EntityIterator mInner; - private final ContentProviderClient mClient; - private volatile boolean mClientReleased; - - EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) { - mInner = inner; - mClient = client; - mClientReleased = false; - } - - public boolean hasNext() throws RemoteException { - if (mClientReleased) { - throw new IllegalStateException("this iterator is already closed"); - } - return mInner.hasNext(); - } - - public Entity next() throws RemoteException { - if (mClientReleased) { - throw new IllegalStateException("this iterator is already closed"); - } - return mInner.next(); - } - - public void reset() throws RemoteException { - if (mClientReleased) { - throw new IllegalStateException("this iterator is already closed"); - } - mInner.reset(); - } - - public void close() { - mClient.release(); - mInner.close(); - mClientReleased = true; - } - - protected void finalize() throws Throwable { - if (!mClientReleased) { - mClient.release(); - } - super.finalize(); - } - } - - /** - * Query the given URI, returning an {@link EntityIterator} over the result set. - * - * @param uri The URI, using the content:// scheme, for the content to - * retrieve. - * @param selection A filter declaring which rows to return, formatted as an - * SQL WHERE clause (excluding the WHERE itself). Passing null will - * return all rows for the given URI. - * @param selectionArgs You may include ?s in selection, which will be - * replaced by the values from selectionArgs, in the order that they - * appear in the selection. The values will be bound as Strings. - * @param sortOrder How to order the rows, formatted as an SQL ORDER BY - * clause (excluding the ORDER BY itself). Passing null will use the - * default sort order, which may be unordered. - * @return An EntityIterator object - * @throws RemoteException thrown if a RemoteException is encountered while attempting - * to communicate with a remote provider. - * @throws IllegalArgumentException thrown if there is no provider that matches the uri - * @hide - */ - public final EntityIterator queryEntities(Uri uri, - String selection, String[] selectionArgs, String sortOrder) throws RemoteException { - ContentProviderClient provider = acquireContentProviderClient(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URL " + uri); - } - try { - EntityIterator entityIterator = - provider.queryEntities(uri, selection, selectionArgs, sortOrder); - return new EntityIteratorWrapper(entityIterator, provider); - } catch(RuntimeException e) { - provider.release(); - throw e; - } catch(RemoteException e) { - provider.release(); - throw e; - } - } - - /** * Open a stream on to the content associated with a content URI. If there * is no data associated with the URI, FileNotFoundException is thrown. * diff --git a/core/java/android/content/CursorEntityIterator.java b/core/java/android/content/CursorEntityIterator.java new file mode 100644 index 0000000..0c66646 --- /dev/null +++ b/core/java/android/content/CursorEntityIterator.java @@ -0,0 +1,88 @@ +package android.content; + +import android.database.Cursor; +import android.os.RemoteException; + +/** + * Abstract implementation of EntityIterator that makes it easy to wrap a cursor + * that can contain several consecutive rows for an entity. + * @hide + */ +public abstract class CursorEntityIterator implements EntityIterator { + private final Cursor mCursor; + private boolean mIsClosed; + + /** + * Constructor that makes initializes the cursor such that the iterator points to the + * first Entity, if there are any. + * @param cursor the cursor that contains the rows that make up the entities + */ + public CursorEntityIterator(Cursor cursor) { + mIsClosed = false; + mCursor = cursor; + mCursor.moveToFirst(); + } + + /** + * Returns the entity that the cursor is currently pointing to. This must take care to advance + * the cursor past this entity. This will never be called if the cursor is at the end. + * @param cursor the cursor that contains the entity rows + * @return the entity that the cursor is currently pointing to + * @throws RemoteException if a RemoteException is caught while attempting to build the Entity + */ + public abstract Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException; + + /** + * Returns whether there are more elements to iterate, i.e. whether the + * iterator is positioned in front of an element. + * + * @return {@code true} if there are more elements, {@code false} otherwise. + * @see #next + */ + public final boolean hasNext() throws RemoteException { + if (mIsClosed) { + throw new IllegalStateException("calling hasNext() when the iterator is closed"); + } + + return !mCursor.isAfterLast(); + } + + /** + * Returns the next object in the iteration, i.e. returns the element in + * front of the iterator and advances the iterator by one position. + * + * @return the next object. + * @throws java.util.NoSuchElementException + * if there are no more elements. + * @see #hasNext + */ + public Entity next() throws RemoteException { + if (mIsClosed) { + throw new IllegalStateException("calling next() when the iterator is closed"); + } + if (!hasNext()) { + throw new IllegalStateException("you may only call next() if hasNext() is true"); + } + + return getEntityAndIncrementCursor(mCursor); + } + + public final void reset() throws RemoteException { + if (mIsClosed) { + throw new IllegalStateException("calling reset() when the iterator is closed"); + } + mCursor.moveToFirst(); + } + + /** + * Indicates that this iterator is no longer needed and that any associated resources + * may be released (such as a SQLite cursor). + */ + public final void close() { + if (mIsClosed) { + throw new IllegalStateException("closing when already closed"); + } + mIsClosed = true; + mCursor.close(); + } +} diff --git a/core/java/android/content/Entity.aidl b/core/java/android/content/Entity.aidl deleted file mode 100644 index fb201f3..0000000 --- a/core/java/android/content/Entity.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* //device/java/android/android/content/Entity.aidl -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package android.content; - -parcelable Entity; diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java index ee8112e..7842de0 100644 --- a/core/java/android/content/Entity.java +++ b/core/java/android/content/Entity.java @@ -24,11 +24,13 @@ import android.util.Log; import java.util.ArrayList; /** - * Objects that pass through the ContentProvider and ContentResolver's methods that deal with - * Entities must implement this abstract base class and thus themselves be Parcelable. - * @hide + * A representation of a item using ContentValues. It contains one top level ContentValue + * plus a collection of Uri, ContentValues tuples as subvalues. One example of its use + * is in Contacts, where the top level ContentValue contains the columns from the RawContacts + * table and the subvalues contain a ContentValues object for each row from the Data table that + * corresponds to that RawContact. The uri refers to the Data table uri for each row. */ -public final class Entity implements Parcelable { +public final class Entity { final private ContentValues mValues; final private ArrayList<NamedContentValues> mSubValues; @@ -49,40 +51,6 @@ public final class Entity implements Parcelable { mSubValues.add(new Entity.NamedContentValues(uri, values)); } - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - mValues.writeToParcel(dest, 0); - dest.writeInt(mSubValues.size()); - for (NamedContentValues value : mSubValues) { - value.uri.writeToParcel(dest, 0); - value.values.writeToParcel(dest, 0); - } - } - - private Entity(Parcel source) { - mValues = ContentValues.CREATOR.createFromParcel(source); - final int numValues = source.readInt(); - mSubValues = new ArrayList<NamedContentValues>(numValues); - for (int i = 0; i < numValues; i++) { - final Uri uri = Uri.CREATOR.createFromParcel(source); - final ContentValues values = ContentValues.CREATOR.createFromParcel(source); - mSubValues.add(new NamedContentValues(uri, values)); - } - } - - public static final Creator<Entity> CREATOR = new Creator<Entity>() { - public Entity createFromParcel(Parcel source) { - return new Entity(source); - } - - public Entity[] newArray(int size) { - return new Entity[size]; - } - }; - public static class NamedContentValues { public final Uri uri; public final ContentValues values; diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java index 1b73439..3cc1040 100644 --- a/core/java/android/content/EntityIterator.java +++ b/core/java/android/content/EntityIterator.java @@ -18,9 +18,6 @@ package android.content; import android.os.RemoteException; -/** - * @hide - */ public interface EntityIterator { /** * Returns whether there are more elements to iterate, i.e. whether the diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 0798adf..1b0ca58 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -44,12 +44,6 @@ public interface IContentProvider extends IInterface { CursorWindow window) throws RemoteException; public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException; - /** - * @hide - */ - public EntityIterator queryEntities(Uri url, String selection, - String[] selectionArgs, String sortOrder) - throws RemoteException; public String getType(Uri url) throws RemoteException; public Uri insert(Uri url, ContentValues initialValues) throws RemoteException; @@ -76,9 +70,5 @@ public interface IContentProvider extends IInterface { static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12; static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; - /** - * @hide - */ - static final int QUERY_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 18; static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19; } diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java deleted file mode 100644 index 068581e..0000000 --- a/core/java/android/content/IEntityIterator.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.os.Binder; -import android.os.IBinder; -import android.os.IInterface; -import android.os.Parcel; -import android.os.RemoteException; -import android.os.Parcelable; -import android.util.Log; - -/** - * ICPC interface methods for an iterator over Entity objects. - * @hide - */ -public interface IEntityIterator extends IInterface { - /** Local-side IPC implementation stub class. */ - public static abstract class Stub extends Binder implements IEntityIterator { - private static final String TAG = "IEntityIterator"; - private static final java.lang.String DESCRIPTOR = "android.content.IEntityIterator"; - - /** Construct the stub at attach it to the interface. */ - public Stub() { - this.attachInterface(this, DESCRIPTOR); - } - /** - * Cast an IBinder object into an IEntityIterator interface, - * generating a proxy if needed. - */ - public static IEntityIterator asInterface(IBinder obj) { - if ((obj==null)) { - return null; - } - IInterface iin = obj.queryLocalInterface(DESCRIPTOR); - if (((iin!=null)&&(iin instanceof IEntityIterator))) { - return ((IEntityIterator)iin); - } - return new IEntityIterator.Stub.Proxy(obj); - } - - public IBinder asBinder() { - return this; - } - - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - switch (code) { - case INTERFACE_TRANSACTION: - { - reply.writeString(DESCRIPTOR); - return true; - } - - case TRANSACTION_hasNext: - { - data.enforceInterface(DESCRIPTOR); - boolean _result; - try { - _result = this.hasNext(); - } catch (Exception e) { - Log.e(TAG, "caught exception in hasNext()", e); - reply.writeException(e); - return true; - } - reply.writeNoException(); - reply.writeInt(((_result)?(1):(0))); - return true; - } - - case TRANSACTION_next: - { - data.enforceInterface(DESCRIPTOR); - Entity entity; - try { - entity = this.next(); - } catch (RemoteException e) { - Log.e(TAG, "caught exception in next()", e); - reply.writeException(e); - return true; - } - reply.writeNoException(); - entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); - return true; - } - - case TRANSACTION_reset: - { - data.enforceInterface(DESCRIPTOR); - try { - this.reset(); - } catch (RemoteException e) { - Log.e(TAG, "caught exception in next()", e); - reply.writeException(e); - return true; - } - reply.writeNoException(); - return true; - } - - case TRANSACTION_close: - { - data.enforceInterface(DESCRIPTOR); - try { - this.close(); - } catch (RemoteException e) { - Log.e(TAG, "caught exception in close()", e); - reply.writeException(e); - return true; - } - reply.writeNoException(); - return true; - } - } - return super.onTransact(code, data, reply, flags); - } - - private static class Proxy implements IEntityIterator { - private IBinder mRemote; - Proxy(IBinder remote) { - mRemote = remote; - } - public IBinder asBinder() { - return mRemote; - } - public java.lang.String getInterfaceDescriptor() { - return DESCRIPTOR; - } - public boolean hasNext() throws RemoteException { - Parcel _data = Parcel.obtain(); - Parcel _reply = Parcel.obtain(); - boolean _result; - try { - _data.writeInterfaceToken(DESCRIPTOR); - mRemote.transact(Stub.TRANSACTION_hasNext, _data, _reply, 0); - _reply.readException(); - _result = (0!=_reply.readInt()); - } - finally { - _reply.recycle(); - _data.recycle(); - } - return _result; - } - - public Entity next() throws RemoteException { - Parcel _data = Parcel.obtain(); - Parcel _reply = Parcel.obtain(); - try { - _data.writeInterfaceToken(DESCRIPTOR); - mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0); - _reply.readException(); - return Entity.CREATOR.createFromParcel(_reply); - } finally { - _reply.recycle(); - _data.recycle(); - } - } - - public void reset() throws RemoteException { - Parcel _data = Parcel.obtain(); - Parcel _reply = Parcel.obtain(); - try { - _data.writeInterfaceToken(DESCRIPTOR); - mRemote.transact(Stub.TRANSACTION_reset, _data, _reply, 0); - _reply.readException(); - } finally { - _reply.recycle(); - _data.recycle(); - } - } - - public void close() throws RemoteException { - Parcel _data = Parcel.obtain(); - Parcel _reply = Parcel.obtain(); - try { - _data.writeInterfaceToken(DESCRIPTOR); - mRemote.transact(Stub.TRANSACTION_close, _data, _reply, 0); - _reply.readException(); - } - finally { - _reply.recycle(); - _data.recycle(); - } - } - } - static final int TRANSACTION_hasNext = (IBinder.FIRST_CALL_TRANSACTION + 0); - static final int TRANSACTION_next = (IBinder.FIRST_CALL_TRANSACTION + 1); - static final int TRANSACTION_close = (IBinder.FIRST_CALL_TRANSACTION + 2); - static final int TRANSACTION_reset = (IBinder.FIRST_CALL_TRANSACTION + 3); - } - public boolean hasNext() throws RemoteException; - public Entity next() throws RemoteException; - public void reset() throws RemoteException; - public void close() throws RemoteException; -} diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 4ca6601..9bfbb74 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -671,6 +671,102 @@ public class DatabaseUtils { } /** + * Reads a String out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getString(index)); + } + } + + /** + * Reads a Long out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getLong(index)); + } + } + + /** + * Reads a Short out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getShort(index)); + } + } + + /** + * Reads a Integer out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getInt(index)); + } + } + + /** + * Reads a Float out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getFloat(index)); + } + } + + /** + * Reads a Double out of a column in a Cursor and writes it to a ContentValues. + * Adds nothing to the ContentValues if the column isn't present or if its value is null. + * + * @param cursor The cursor to read from + * @param column The column to read + * @param values The {@link ContentValues} to put the value into + */ + public static void cursorDoubleToContentValuesIfPresent(Cursor cursor, ContentValues values, + String column) { + final int index = cursor.getColumnIndexOrThrow(column); + if (!cursor.isNull(index)) { + values.put(column, cursor.getDouble(index)); + } + } + + /** * This class allows users to do multiple inserts into a table but * compile the SQL insert statement only once, which may increase * performance. diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 033ef89..4eaea6a 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -139,13 +139,9 @@ public class VCardComposer { public static final Uri CONTACTS_TEST_CONTENT_URI = Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts"); - private static final Uri sDataRequestUri; private static final Map<Integer, String> sImMap; static { - Uri.Builder builder = RawContacts.CONTENT_URI.buildUpon(); - builder.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1"); - sDataRequestUri = builder.build(); sImMap = new HashMap<Integer, String>(); sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); @@ -482,16 +478,19 @@ public class VCardComposer { private String createOneEntryInternal(final String contactId) { final Map<String, List<ContentValues>> contentValuesListMap = new HashMap<String, List<ContentValues>>(); - final String selection = Data.CONTACT_ID + "=?"; - final String[] selectionArgs = new String[] {contactId}; // The resolver may return the entity iterator with no data. It is possiible. // e.g. If all the data in the contact of the given contact id are not exportable ones, // they are hidden from the view of this method, though contact id itself exists. boolean dataExists = false; EntityIterator entityIterator = null; try { - entityIterator = mContentResolver.queryEntities( - sDataRequestUri, selection, selectionArgs, null); + final Uri uri = RawContacts.CONTENT_URI.buildUpon() + .appendEncodedPath(contactId) + .appendEncodedPath(RawContacts.Entity.CONTENT_DIRECTORY) + .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") + .build(); + entityIterator = RawContacts.newEntityIterator(mContentResolver.query( + uri, null, null, null, null)); dataExists = entityIterator.hasNext(); while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 1652ebd..2286bb2 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -23,7 +23,12 @@ import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.EntityIterator; +import android.content.CursorEntityIterator; +import android.content.Entity; +import android.content.ContentProviderClient; import android.database.Cursor; +import android.database.DatabaseUtils; import android.net.Uri; import android.pim.ICalendar; import android.pim.RecurrenceSet; @@ -33,6 +38,7 @@ import android.text.format.Time; import android.util.Config; import android.util.Log; import android.accounts.Account; +import android.os.RemoteException; /** * The Calendar provider contains all calendar events. @@ -135,12 +141,60 @@ public final class Calendar { * <p>Type: String (blob)</p> */ public static final String SYNC_STATE = "sync_state"; + + /** + * The account that was used to sync the entry to the device. + * <P>Type: TEXT</P> + */ + public static final String _SYNC_ACCOUNT = "_sync_account"; + + /** + * The type of the account that was used to sync the entry to the device. + * <P>Type: TEXT</P> + */ + 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. + * <P>Type: TEXT</P> + */ + 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. + * <P>Type: INTEGER (long)</P> + */ + public static final String _SYNC_TIME = "_sync_time"; + + /** + * The version of the row, as assigned by the server. + * <P>Type: TEXT</P> + */ + public static final String _SYNC_VERSION = "_sync_version"; + + /** + * Used in temporary provider while syncing, always NULL for rows in persistent providers. + * <P>Type: INTEGER (long)</P> + */ + public static final String _SYNC_LOCAL_ID = "_sync_local_id"; + + /** + * Used only in persistent providers, and only during merging. + * <P>Type: INTEGER (long)</P> + */ + public static final String _SYNC_MARK = "_sync_mark"; + + /** + * Used to indicate that local, unsynced, changes are present. + * <P>Type: INTEGER (long)</P> + */ + 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 30c73d7..80d5d08 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.<p> + * again and passing the {@link ContactsContract#CALLER_IS_SYNCADAPTER} query parameter.<p> * <p>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); + } + } } /** |