diff options
author | Fred Quintana <fredq@google.com> | 2009-05-22 14:23:31 -0700 |
---|---|---|
committer | Fred Quintana <fredq@google.com> | 2009-06-01 16:17:03 -0700 |
commit | 03d9490758c9318cee6d14d3cc5007556dce92d0 (patch) | |
tree | 58d56d848d0a1f0638157f0ba70afc9b75119601 /core | |
parent | b0d031ad132dac585d1f21d46ebebcc4d13f40c7 (diff) | |
download | frameworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.zip frameworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.tar.gz frameworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.tar.bz2 |
- create a new generic ISyncAdapter implementation, SyncAdapterNew
- change the applyBatch to take an ArrayList rather than an []
- change Entity to be a final flass that contains ContentValues
- remove the ability to update/insert Entities by a ContentProviderOperation
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/content/AbstractSyncableContentProvider.java | 4 | ||||
-rw-r--r-- | core/java/android/content/ContentProvider.java | 12 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderClient.java | 3 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderNative.java | 16 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderOperation.java | 82 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderResult.java | 7 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 47 | ||||
-rw-r--r-- | core/java/android/content/Entity.java | 80 | ||||
-rw-r--r-- | core/java/android/content/EntityIterator.java | 2 | ||||
-rw-r--r-- | core/java/android/content/IContentProvider.java | 3 | ||||
-rw-r--r-- | core/java/android/content/IEntityIterator.java | 5 | ||||
-rw-r--r-- | core/java/android/content/SyncAdapterNew.java | 135 | ||||
-rw-r--r-- | core/java/android/database/AbstractWindowedCursor.java | 42 | ||||
-rw-r--r-- | core/java/android/database/CursorWindow.java | 51 | ||||
-rw-r--r-- | core/java/android/provider/ContactsContract.java | 90 | ||||
-rw-r--r-- | core/jni/android_database_CursorWindow.cpp | 65 |
16 files changed, 525 insertions, 119 deletions
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java index 05781f4..e628dcd 100644 --- a/core/java/android/content/AbstractSyncableContentProvider.java +++ b/core/java/android/content/AbstractSyncableContentProvider.java @@ -406,7 +406,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro * } * </pre> * - * @hide This method should be used only when {@link #applyBatch} is not enough and must be + * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be * used with {@link #endBatch}. * e.g. If returned value has to be used during one transaction, this method might be useful. */ @@ -461,7 +461,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro } } - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { boolean successful = false; beginBatch(); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index bb25b68..4e631c4 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -32,6 +32,7 @@ import android.os.ParcelFileDescriptor; import java.io.File; import java.io.FileNotFoundException; +import java.util.ArrayList; /** * Content providers are one of the primary building blocks of Android applications, providing @@ -156,7 +157,7 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.insertEntity(uri, entities); } - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { for (ContentProviderOperation operation : operations) { if (operation.isReadOperation()) { @@ -641,11 +642,12 @@ public abstract class ContentProvider implements ComponentCallbacks { * @throws OperationApplicationException thrown if an application fails. * See {@link ContentProviderOperation#apply} for more information. */ - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { - ContentProviderResult[] results = new ContentProviderResult[operations.length]; - for (int i = 0; i < operations.length; i++) { - results[i] = operations[i].apply(this, results, i); + final int numOperations = operations.size(); + final ContentProviderResult[] results = new ContentProviderResult[numOperations]; + for (int i = 0; i < numOperations; i++) { + results[i] = operations.get(i).apply(this, results, i); } return results; } diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 08d4fca..452653e 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -23,6 +23,7 @@ import android.os.ParcelFileDescriptor; import android.content.res.AssetFileDescriptor; import java.io.FileNotFoundException; +import java.util.ArrayList; /** * The public interface object used to interact with a {@link ContentProvider}. This is obtained by @@ -105,7 +106,7 @@ public class ContentProviderClient { } /** see {@link ContentProvider#applyBatch} */ - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + 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 4747726..a4c217b 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -33,6 +33,7 @@ import android.os.ParcelFileDescriptor; import android.os.Parcelable; import java.io.FileNotFoundException; +import java.util.ArrayList; /** * {@hide} @@ -179,8 +180,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case APPLY_BATCH_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); - final ContentProviderOperation[] operations = - data.createTypedArray(ContentProviderOperation.CREATOR); + final int numOperations = data.readInt(); + final ArrayList<ContentProviderOperation> operations = + new ArrayList<ContentProviderOperation>(numOperations); + for (int i = 0; i < numOperations; i++) { + operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); + } final ContentProviderResult[] results = applyBatch(operations); reply.writeNoException(); reply.writeTypedArray(results, 0); @@ -471,13 +476,16 @@ final class ContentProviderProxy implements IContentProvider return count; } - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); - data.writeTypedArray(operations, 0); + data.writeInt(operations.size()); + for (ContentProviderOperation operation : operations) { + operation.writeToParcel(data, 0); + } mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index ce92198..07be017 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -24,8 +24,6 @@ import android.os.Parcel; import java.util.Map; import java.util.HashMap; -import dalvik.system.VMStack; - public class ContentProviderOperation implements Parcelable { private final static int TYPE_INSERT = 1; private final static int TYPE_UPDATE = 2; @@ -37,7 +35,6 @@ public class ContentProviderOperation implements Parcelable { private final String mSelection; private final String[] mSelectionArgs; private final ContentValues mValues; - private final Entity mEntity; private final Integer mExpectedCount; private final ContentValues mValuesBackReferences; private final Map<Integer, Integer> mSelectionArgsBackReferences; @@ -52,7 +49,6 @@ public class ContentProviderOperation implements Parcelable { mType = builder.mType; mUri = builder.mUri; mValues = builder.mValues; - mEntity = builder.mEntity; mSelection = builder.mSelection; mSelectionArgs = builder.mSelectionArgs; mExpectedCount = builder.mExpectedCount; @@ -60,11 +56,10 @@ public class ContentProviderOperation implements Parcelable { mValuesBackReferences = builder.mValuesBackReferences; } - private ContentProviderOperation(Parcel source, ClassLoader classLoader) { + private ContentProviderOperation(Parcel source) { mType = source.readInt(); mUri = Uri.CREATOR.createFromParcel(source); mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null; - mEntity = (Entity) source.readParcelable(classLoader); mSelection = source.readInt() != 0 ? source.readString() : null; mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null; mExpectedCount = source.readInt() != 0 ? source.readInt() : null; @@ -92,7 +87,6 @@ public class ContentProviderOperation implements Parcelable { } else { dest.writeInt(0); } - dest.writeParcelable(mEntity, 0); if (mSelection != null) { dest.writeInt(1); dest.writeString(mSelection); @@ -184,7 +178,7 @@ public class ContentProviderOperation implements Parcelable { * Applies this operation using the given provider. The backRefs array is used to resolve any * back references that were requested using * {@link Builder#withValueBackReferences(ContentValues)} and - * {@link Builder#withSelectionBackReferences(java.util.Map)}. + * {@link Builder#withSelectionBackReference}. * @param provider the {@link ContentProvider} on which this batch is applied * @param backRefs a {@link ContentProviderResult} array that will be consulted * to resolve any requested back references. @@ -201,12 +195,7 @@ public class ContentProviderOperation implements Parcelable { resolveSelectionArgsBackReferences(backRefs, numBackRefs); if (mType == TYPE_INSERT) { - Uri newUri; - if (mEntity != null) { - newUri = provider.insertEntity(mUri, mEntity); - } else { - newUri = provider.insert(mUri, values); - } + Uri newUri = provider.insert(mUri, values); if (newUri == null) { throw new OperationApplicationException("insert failed"); } @@ -217,11 +206,7 @@ public class ContentProviderOperation implements Parcelable { if (mType == TYPE_DELETE) { numRows = provider.delete(mUri, mSelection, selectionArgs); } else if (mType == TYPE_UPDATE) { - if (mEntity != null) { - numRows = provider.updateEntity(mUri, mEntity); - } else { - numRows = provider.update(mUri, values, mSelection, selectionArgs); - } + numRows = provider.update(mUri, values, mSelection, selectionArgs); } else if (mType == TYPE_COUNT) { Cursor cursor = provider.query(mUri, COUNT_COLUMNS, mSelection, selectionArgs, null); try { @@ -322,7 +307,7 @@ public class ContentProviderOperation implements Parcelable { */ private static String backRefToValue(ContentProviderResult[] backRefs, int numBackRefs, Integer backRefIndex) { - if (backRefIndex > numBackRefs) { + if (backRefIndex >= numBackRefs) { throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex + " but there are only " + numBackRefs + " back refs"); } @@ -343,7 +328,7 @@ public class ContentProviderOperation implements Parcelable { public static final Creator<ContentProviderOperation> CREATOR = new Creator<ContentProviderOperation>() { public ContentProviderOperation createFromParcel(Parcel source) { - return new ContentProviderOperation(source, VMStack.getCallingClassLoader2()); + return new ContentProviderOperation(source); } public ContentProviderOperation[] newArray(int size) { @@ -368,7 +353,6 @@ public class ContentProviderOperation implements Parcelable { private String mSelection; private String[] mSelectionArgs; private ContentValues mValues; - private Entity mEntity; private Integer mExpectedCount; private ContentValues mValuesBackReferences; private Map<Integer, Integer> mSelectionArgsBackReferences; @@ -384,14 +368,6 @@ public class ContentProviderOperation implements Parcelable { /** Create a ContentProviderOperation from this {@link Builder}. */ public ContentProviderOperation build() { - if (mValues != null && mEntity != null) { - throw new IllegalArgumentException("you are not allowed to specify both an entity " - + "and a values"); - } - if (mEntity != null && mValuesBackReferences != null) { - throw new IllegalArgumentException("you are not allowed to specify both an entity " - + "and a values backreference"); - } return new ContentProviderOperation(this); } @@ -414,33 +390,39 @@ public class ContentProviderOperation implements Parcelable { } /** - * Add a {@link Map} of back references. The integer key is the index of the selection arg - * to set and the integer value is the index of the previous result whose - * value should be used for the arg. If any value at that index of the selection arg - * that was specified by {@link #withSelection} will be overwritten. - * This can only be used with builders of type update, delete, or count query. + * Add a ContentValues back reference. + * A column value from the back references takes precedence over a value specified in + * {@link #withValues}. + * This can only be used with builders of type insert or update. * @return this builder, to allow for chaining. */ - public Builder withSelectionBackReferences(Map<Integer, Integer> backReferences) { - if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) { + public Builder withValueBackReference(String key, int previousResult) { + if (mType != TYPE_INSERT && mType != TYPE_UPDATE) { throw new IllegalArgumentException( - "only deletes, updates and counts can have selection back-references"); + "only inserts and updates can have value back-references"); } - mSelectionArgsBackReferences = backReferences; + if (mValuesBackReferences == null) { + mValuesBackReferences = new ContentValues(); + } + mValuesBackReferences.put(key, previousResult); return this; } /** - * The ContentValues to use. This may be null. These values may be overwritten by - * the corresponding value specified by {@link #withValueBackReferences(ContentValues)}. - * This can only be used with builders of type insert or update. + * Add a back references as a selection arg. Any value at that index of the selection arg + * that was specified by {@link #withSelection} will be overwritten. + * This can only be used with builders of type update, delete, or count query. * @return this builder, to allow for chaining. */ - public Builder withValues(ContentValues values) { - if (mType != TYPE_INSERT && mType != TYPE_UPDATE) { - throw new IllegalArgumentException("only inserts and updates can have values"); + public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) { + if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) { + throw new IllegalArgumentException( + "only deletes, updates and counts can have selection back-references"); } - mValues = values; + if (mSelectionArgsBackReferences == null) { + mSelectionArgsBackReferences = new HashMap<Integer, Integer>(); + } + mSelectionArgsBackReferences.put(selectionArgIndex, previousResult); return this; } @@ -450,11 +432,11 @@ public class ContentProviderOperation implements Parcelable { * This can only be used with builders of type insert or update. * @return this builder, to allow for chaining. */ - public Builder withEntity(Entity entity) { + public Builder withValues(ContentValues values) { if (mType != TYPE_INSERT && mType != TYPE_UPDATE) { - throw new IllegalArgumentException("only inserts and updates can have an entity"); + throw new IllegalArgumentException("only inserts and updates can have values"); } - mEntity = entity; + mValues = values; return this; } @@ -462,7 +444,7 @@ public class ContentProviderOperation implements Parcelable { * The selection and arguments to use. An occurrence of '?' in the selection will be * replaced with the corresponding occurence of the selection argument. Any of the * selection arguments may be overwritten by a selection argument back reference as - * specified by {@link #withSelectionBackReferences}. + * specified by {@link #withSelectionBackReference}. * This can only be used with builders of type update, delete, or count query. * @return this builder, to allow for chaining. */ diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java index 34aaa6d..5d188ef 100644 --- a/core/java/android/content/ContentProviderResult.java +++ b/core/java/android/content/ContentProviderResult.java @@ -74,4 +74,11 @@ public class ContentProviderResult implements Parcelable { return new ContentProviderResult[size]; } }; + + public String toString() { + if (uri != null) { + return "ContentProviderResult(uri=" + uri.toString() + ")"; + } + return "ContentProviderResult(count=" + count + ")"; + } }
\ No newline at end of file diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index f7b52fa..a01c5d1 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.ArrayList; /** @@ -567,50 +568,6 @@ public abstract class ContentResolver { } /** - * Inserts an Entity at the given URL. - * - * @param uri The URL of the table to insert into. - * @param entity an Entity to insert at uri. This must be the same Entity subtype that the - * matching content provider expects at that uri. - * @return the URL of the newly created row. - * @throws RemoteException thrown if a RemoteException is encountered while attempting - * to communicate with a remote provider. - */ - public final Uri insertEntity(Uri uri, Entity entity) throws RemoteException { - ContentProviderClient provider = acquireContentProviderClient(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URL " + uri); - } - try { - return provider.insertEntity(uri, entity); - } finally { - provider.release(); - } - } - - /** - * Replaces the Entity at the given URL with the provided entity. - * - * @param uri The URL of the entity to update. - * @param entity the new version of the entity - * @return the number of rows that are updated. Will be 0 if an entity no longer exists - * at uri otherwise it will be 1. - * @throws RemoteException thrown if a RemoteException is encountered while attempting - * to communicate with a remote provider. - */ - public final int updateEntity(Uri uri, Entity entity) throws RemoteException { - ContentProviderClient provider = acquireContentProviderClient(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URL " + uri); - } - try { - return provider.updateEntity(uri, entity); - } finally { - provider.release(); - } - } - - /** * Applies each of the {@link ContentProviderOperation} objects and returns an array * of their results. Passes through OperationApplicationException, which may be thrown * by the call to {@link ContentProviderOperation#apply}. @@ -627,7 +584,7 @@ public abstract class ContentResolver { * to communicate with a remote provider. */ public ContentProviderResult[] applyBatch(String authority, - ContentProviderOperation[] operations) + ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { ContentProviderClient provider = acquireContentProviderClient(authority); if (provider == null) { diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java index 31ea2cd..325dce5 100644 --- a/core/java/android/content/Entity.java +++ b/core/java/android/content/Entity.java @@ -17,10 +17,88 @@ package android.content; import android.os.Parcelable; +import android.os.Parcel; +import android.net.Uri; +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. */ -public abstract class Entity implements Parcelable { +public final class Entity implements Parcelable { + final private ContentValues mValues; + final private ArrayList<NamedContentValues> mSubValues; + + public Entity(ContentValues values) { + mValues = values; + mSubValues = new ArrayList<NamedContentValues>(); + } + + public ContentValues getEntityValues() { + return mValues; + } + + public ArrayList<NamedContentValues> getSubValues() { + return mSubValues; + } + + public void addSubValue(Uri uri, ContentValues values) { + 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; + + public NamedContentValues(Uri uri, ContentValues values) { + this.uri = uri; + this.values = values; + } + } + + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("Entity: ").append(getEntityValues()); + for (Entity.NamedContentValues namedValue : getSubValues()) { + sb.append("\n ").append(namedValue.uri); + sb.append("\n -> ").append(namedValue.values); + } + return sb.toString(); + } } diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java index 61914cf..5e5f14c 100644 --- a/core/java/android/content/EntityIterator.java +++ b/core/java/android/content/EntityIterator.java @@ -18,7 +18,7 @@ package android.content; import android.os.RemoteException; -public interface EntityIterator { +public interface EntityIterator { /** * Returns whether there are more elements to iterate, i.e. whether the * iterator is positioned in front of an element. diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index f82c982..7e5aba5 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -28,6 +28,7 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; import java.io.FileNotFoundException; +import java.util.ArrayList; /** * The ipc interface to talk to a content provider. @@ -60,7 +61,7 @@ public interface IContentProvider extends IInterface { throws RemoteException, FileNotFoundException; public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException, FileNotFoundException; - public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; /* IPC constants */ diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java index 1887df2..1c478b3 100644 --- a/core/java/android/content/IEntityIterator.java +++ b/core/java/android/content/IEntityIterator.java @@ -21,6 +21,7 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; import android.os.RemoteException; +import android.os.Parcelable; import android.util.Log; /** @@ -93,7 +94,7 @@ public interface IEntityIterator extends IInterface { return true; } reply.writeNoException(); - reply.writeParcelable(entity, 0); + entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return true; } @@ -149,7 +150,7 @@ public interface IEntityIterator extends IInterface { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0); _reply.readException(); - return (Entity) _reply.readParcelable(null /* classLoader */); + return Entity.CREATOR.createFromParcel(_reply); } finally { _reply.recycle(); _data.recycle(); diff --git a/core/java/android/content/SyncAdapterNew.java b/core/java/android/content/SyncAdapterNew.java new file mode 100644 index 0000000..5b23395 --- /dev/null +++ b/core/java/android/content/SyncAdapterNew.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2006 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.*; +import android.os.Process; +import android.accounts.Account; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @hide + */ +public abstract class SyncAdapterNew { + private static final String TAG = "SyncAdapter"; + private final Context mContext; + private final String mAuthority; + + /** Kernel event log tag. Also listed in data/etc/event-log-tags. */ + public static final int LOG_SYNC_DETAILS = 2743; + + public SyncAdapterNew(Context context, String authority) { + mContext = context; + mAuthority = authority; + } + + class Transport extends ISyncAdapter.Stub { + private final AtomicInteger mNumSyncStarts = new AtomicInteger(0); + private volatile Thread mSyncThread; + + public void startSync(ISyncContext syncContext, Account account, Bundle extras) { + boolean alreadyInProgress; + synchronized (this) { + if (mSyncThread == null) { + mSyncThread = new Thread( + new SyncRunnable(new SyncContext(syncContext), account, extras), + "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet()); + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSyncThread.start(); + alreadyInProgress = false; + } else { + alreadyInProgress = true; + } + } + + if (alreadyInProgress) { + try { + syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS); + } catch (RemoteException e) { + // don't care if the caller is no longer around + } + } + } + + public void cancelSync() { + synchronized (this) { + if (mSyncThread != null) { + mSyncThread.interrupt(); + } + } + } + + private class SyncRunnable implements Runnable { + private final SyncContext mSyncContext; + private final Account mAccount; + private final Bundle mExtras; + + private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) { + mSyncContext = syncContext; + mAccount = account; + mExtras = extras; + } + + public void run() { + if (isCanceled()) { + return; + } + + SyncResult syncResult = new SyncResult(); + ContentProviderClient provider = mAuthority != null + ? mContext.getContentResolver().acquireContentProviderClient(mAuthority) + : null; + try { + SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult); + } finally { + if (provider != null) { + provider.release(); + } + if (!isCanceled()) { + mSyncContext.onFinished(syncResult); + } + mSyncThread = null; + } + } + + private boolean isCanceled() { + return Thread.currentThread().isInterrupted(); + } + } + } + + Transport mTransport = new Transport(); + + /** + * Get the Transport object. + */ + public final ISyncAdapter getISyncAdapter() { + return mTransport; + } + + /** + * Perform a sync for this account. SyncAdapter-specific parameters may + * be specified in extras, which is guaranteed to not be null. Invocations + * of this method are guaranteed to be serialized. + * + * @param account the account that should be synced + * @param extras SyncAdapter-specific parameters + */ + public abstract void performSync(Account account, Bundle extras, + ContentProviderClient provider, SyncResult syncResult); +}
\ No newline at end of file diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index 4ac0aef..27a02e2 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -166,6 +166,48 @@ public abstract class AbstractWindowedCursor extends AbstractCursor return mWindow.isBlob(mPos, columnIndex); } + public boolean isString(int columnIndex) + { + checkPosition(); + + synchronized(mUpdatedRows) { + if (isFieldUpdated(columnIndex)) { + Object object = getUpdatedField(columnIndex); + return object == null || object instanceof String; + } + } + + return mWindow.isString(mPos, columnIndex); + } + + public boolean isLong(int columnIndex) + { + checkPosition(); + + synchronized(mUpdatedRows) { + if (isFieldUpdated(columnIndex)) { + Object object = getUpdatedField(columnIndex); + return object != null && (object instanceof Integer || object instanceof Long); + } + } + + return mWindow.isLong(mPos, columnIndex); + } + + public boolean isFloat(int columnIndex) + { + checkPosition(); + + synchronized(mUpdatedRows) { + if (isFieldUpdated(columnIndex)) { + Object object = getUpdatedField(columnIndex); + return object != null && (object instanceof Float || object instanceof Double); + } + } + + return mWindow.isFloat(mPos, columnIndex); + } + @Override protected void checkPosition() { diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 8e26730..99db81b 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -263,7 +263,58 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } } + /** + * Checks if a field contains a long + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a long + */ + public boolean isLong(int row, int col) { + acquireReference(); + try { + return isInteger_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Checks if a field contains a float. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a float + */ + public boolean isFloat(int row, int col) { + acquireReference(); + try { + return isFloat_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Checks if a field contains either a String or is null. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} or a String + */ + public boolean isString(int row, int col) { + acquireReference(); + try { + return isString_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + private native boolean isBlob_native(int row, int col); + private native boolean isString_native(int row, int col); + private native boolean isInteger_native(int row, int col); + private native boolean isFloat_native(int row, int col); /** * Returns a String for the given field. diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index cc71547..9d48bc78 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -31,6 +31,75 @@ public final class ContactsContract { /** A content:// style uri to the authority for the contacts provider */ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); + public interface AccountsColumns { + /** + * The name of this account data + * <P>Type: TEXT</P> + */ + public static final String NAME = "name"; + /** + * The name of this account data + * <P>Type: TEXT</P> + */ + public static final String TYPE = "type"; + /** + * The name of this account data + * <P>Type: TEXT</P> + */ + public static final String DATA1 = "data1"; + + /** + * The value for this account data + * <P>Type: INTEGER</P> + */ + public static final String DATA2 = "data2"; + + /** + * The value for this account data + * <P>Type: INTEGER</P> + */ + public static final String DATA3 = "data3"; + + /** + * The value for this account data + * <P>Type: INTEGER</P> + */ + public static final String DATA4 = "data4"; + + /** + * The value for this account data + * <P>Type: INTEGER</P> + */ + public static final String DATA5 = "data5"; + } + + /** + * Constants for the aggregates table, which contains a record per group + * of contact representing the same person. + */ + public static final class Accounts implements BaseColumns, AccountsColumns { + /** + * This utility class cannot be instantiated + */ + private Accounts() {} + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "accounts"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of + * account data. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contacts_account"; + + /** + * The MIME type of a {@link #CONTENT_URI} subdirectory of a account + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contacts_account"; + } + public interface AggregatesColumns { /** * The display name for the contact. @@ -136,6 +205,11 @@ public final class ContactsContract { private Contacts() {} /** + * A reference to the {@link Accounts#_ID} that this data belongs to. + */ + public static final String ACCOUNTS_ID = "accounts_id"; + + /** * A reference to the {@link Aggregates#_ID} that this data belongs to. */ public static final String AGGREGATE_ID = "aggregate_id"; @@ -167,6 +241,22 @@ public final class ContactsContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person"; /** + * A string that uniquely identifies this contact to its source, which is referred to + * by the {@link #ACCOUNTS_ID} + */ + public static final String SOURCE_ID = "sourceid"; + + /** + * An integer that is updated whenever this contact or its data changes. + */ + public static final String VERSION = "version"; + + /** + * Set to 1 whenever the version changes + */ + public static final String DIRTY = "dirty"; + + /** * A sub-directory of a single contact that contains all of their {@link Data} rows. * To access this directory append */ diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index f19fbbf..91449bc 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -229,7 +229,7 @@ static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column { int32_t err; CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window); +LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window); field_slot_t field; err = window->read_field_slot(row, column, &field); @@ -241,6 +241,54 @@ LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL; } +static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL; +} + +static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_INTEGER; +} + +static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_FLOAT; +} + static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) { int32_t err; @@ -326,11 +374,11 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); return NULL; } - + jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); if (buffer == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); - return NULL; + return NULL; } jchar* dst = env->GetCharArrayElements(buffer, NULL); uint8_t type = field.type; @@ -338,7 +386,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); jcharArray newArray = NULL; if (type == FIELD_TYPE_STRING) { uint32_t size = field.data.buffer.size; - if (size > 0) { + if (size > 0) { #if WINDOW_STORAGE_UTF8 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); @@ -346,7 +394,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); if (strSize > bufferSize || dst == NULL) { newArray = env->NewCharArray(strSize); env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); - } else { + } else { memcpy(dst, (jchar const *)utf16.string(), strSize * 2); } sizeCopied = strSize; @@ -359,7 +407,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); } #endif - } + } } else if (type == FIELD_TYPE_INTEGER) { int64_t value; if (window->getLong(row, column, &value)) { @@ -628,6 +676,9 @@ static JNINativeMethod sMethods[] = {"putDouble_native", "(DII)Z", (void *)putDouble_native}, {"freeLastRow_native", "()V", (void *)freeLastRow}, {"putNull_native", "(II)Z", (void *)putNull_native}, + {"isString_native", "(II)Z", (void *)isString_native}, + {"isFloat_native", "(II)Z", (void *)isFloat_native}, + {"isInteger_native", "(II)Z", (void *)isInteger_native}, }; int register_android_database_CursorWindow(JNIEnv * env) @@ -646,7 +697,7 @@ int register_android_database_CursorWindow(JNIEnv * env) LOGE("Error locating fields"); return -1; } - + clazz = env->FindClass("android/database/CharArrayBuffer"); if (clazz == NULL) { LOGE("Can't find android/database/CharArrayBuffer"); |