Preliminary GAL/Contacts integration for EAS
Change-Id: I9997ac96f83f427c71caf12d591ba6069bedf935
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bdcd4c8..6d4ff98 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -339,12 +339,20 @@
<!--EXCHANGE-REMOVE-SECTION-START-->
<!-- In this release, GAL information is used locally only, so we used the same
strict permissions. -->
+ <!-- NOTE: ExchangeGalProvider will replace ExchangeProvider after integration with
+ the new GAL/contacts implementation -->
<provider
android:name="com.android.exchange.provider.ExchangeProvider"
android:authorities="com.android.exchange.provider"
android:multiprocess="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
/>
+ <provider
+ android:name="com.android.exchange.provider.ExchangeGalProvider"
+ android:authorities="com.android.exchange.gal.provider"
+ android:readPermission="android.permission.READ_CONTACTS"
+ android:multiprocess="false"
+ />
<!--EXCHANGE-REMOVE-SECTION-END-->
</application>
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index f58604c..aeeb90b 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -788,7 +788,7 @@
* @param context caller's context
* @param accountId the account Id to search
* @param filter the characters entered so far
- * @return a result record or null
+ * @return a result record or null for no data
*
* TODO: shorter timeout for interactive lookup
* TODO: make watchdog actually work (it doesn't understand our service w/Mailbox == 0)
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index f2d6e57..890a99a 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -420,6 +420,15 @@
}
return null;
}
+
+ public Account getByName(String accountName) {
+ for (Account account : this) {
+ if (account.mEmailAddress.equalsIgnoreCase(accountName)) {
+ return account;
+ }
+ }
+ return null;
+ }
}
class AccountObserver extends ContentObserver {
@@ -837,6 +846,17 @@
return null;
}
+ static public Account getAccountByName(String accountName) {
+ SyncManager syncManager = INSTANCE;
+ if (syncManager != null) {
+ AccountList accountList = syncManager.mAccountList;
+ synchronized (accountList) {
+ return accountList.getByName(accountName);
+ }
+ }
+ return null;
+ }
+
static public String getEasAccountSelector() {
SyncManager syncManager = INSTANCE;
if (syncManager != null && syncManager.mAccountObserver != null) {
diff --git a/src/com/android/exchange/provider/ExchangeDirectoryProvider.java b/src/com/android/exchange/provider/ExchangeDirectoryProvider.java
new file mode 100644
index 0000000..fde34df
--- /dev/null
+++ b/src/com/android/exchange/provider/ExchangeDirectoryProvider.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 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 com.android.exchange.provider;
+
+import com.android.email.provider.EmailContent.Account;
+import com.android.exchange.EasSyncService;
+import com.android.exchange.SyncManager;
+import com.android.exchange.provider.GalResult.GalData;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+
+/**
+ * ExchangeDirectoryProvider provides real-time data from the Exchange server; at the moment, it is
+ * used solely to provide GAL (Global Address Lookup) service to email address adapters
+ */
+public class ExchangeDirectoryProvider extends ContentProvider {
+ public static final String EXCHANGE_GAL_AUTHORITY = "com.android.exchange.gal.provider";
+
+ private static final int GAL_BASE = 0;
+ private static final int GAL_FILTER = GAL_BASE;
+
+ private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ static {
+ sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "contacts/filter/*", GAL_FILTER);
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
+ if (accountName == null) {
+ return null;
+ }
+
+ Account account = SyncManager.getAccountByName(accountName);
+ if (account == null) {
+ return null;
+ }
+
+ int match = sURIMatcher.match(uri);
+ switch (match) {
+ case GAL_FILTER:
+ String filter = uri.getLastPathSegment();
+ // We should have at least two characters before doing a GAL search
+ if (filter == null || filter.length() < 2) {
+ return null;
+ }
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ // Get results from the Exchange account
+ GalResult galResult = EasSyncService.searchGal(getContext(), account.mId,
+ filter);
+ if (galResult != null) {
+ return buildGalResultCursor(projection, galResult);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ break;
+ }
+
+ return null;
+ }
+
+ /*package*/ Cursor buildGalResultCursor(String[] projection, GalResult galResult) {
+ int displayNameIndex = -1;
+ int emailIndex = -1;
+ boolean alternateDisplayName = false;
+
+ for (int i = 0; i < projection.length; i++) {
+ String column = projection[i];
+ if (Contacts.DISPLAY_NAME.equals(column) ||
+ Contacts.DISPLAY_NAME_PRIMARY.equals(column)) {
+ displayNameIndex = i;
+ } else if (Contacts.DISPLAY_NAME_ALTERNATIVE.equals(column)) {
+ displayNameIndex = i;
+ alternateDisplayName = true;
+
+ } else if (CommonDataKinds.Email.ADDRESS.equals(column)) {
+ emailIndex = i;
+ }
+ // TODO other fields
+ }
+
+ Object[] row = new Object[projection.length];
+
+ /*
+ * ContactsProvider will ensure that every request has a non-null projection.
+ */
+ MatrixCursor cursor = new MatrixCursor(projection);
+ int count = galResult.galData.size();
+ for (int i = 0; i < count; i++) {
+ GalData galDataRow = galResult.galData.get(i);
+ if (displayNameIndex != -1) {
+ row[displayNameIndex] = galDataRow.displayName;
+ // TODO Handle alternate display name here
+ }
+ if (emailIndex != -1) {
+ row[emailIndex] = galDataRow.emailAddress;
+ }
+ cursor.addRow(row);
+ }
+ return cursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ int match = sURIMatcher.match(uri);
+ switch (match) {
+ case GAL_FILTER:
+ return Contacts.CONTENT_ITEM_TYPE;
+ }
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/src/com/android/exchange/provider/ExchangeDirectoryProviderTests.java b/tests/src/com/android/exchange/provider/ExchangeDirectoryProviderTests.java
new file mode 100644
index 0000000..a623aed
--- /dev/null
+++ b/tests/src/com/android/exchange/provider/ExchangeDirectoryProviderTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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 com.android.exchange.provider;
+
+import com.android.exchange.provider.GalResult.GalData;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.test.AndroidTestCase;
+
+/**
+ * You can run this entire test case with:
+ * runtest -c com.android.exchange.provider.ExchangeDirectoryProviderTests email
+ */
+public class ExchangeDirectoryProviderTests extends AndroidTestCase {
+
+ // Create a test projection; we should only get back values for display name and email address
+ private static final String[] GAL_RESULT_PROJECTION =
+ new String[] {Contacts.DISPLAY_NAME, CommonDataKinds.Email.ADDRESS, Contacts.CONTENT_TYPE};
+ private static final int GAL_RESULT_DISPLAY_NAME_COLUMN = 0;
+ private static final int GAL_RESULT_EMAIL_ADDRESS_COLUMN = 1;
+ private static final int GAL_RESULT_CONTENT_TYPE_COLUMN = 2;
+
+ public void testBuildGalResultCursor() {
+ GalResult result = new GalResult();
+ result.addGalData(1, "Alice Aardvark", "alice@aardvark.com");
+ result.addGalData(2, "Bob Badger", "bob@badger.com");
+ result.addGalData(3, "Clark Cougar", "clark@cougar.com");
+ result.addGalData(4, "Dan Dolphin", "dan@dolphin.com");
+
+ // Make sure our returned cursor has the expected contents
+ ExchangeDirectoryProvider provider = new ExchangeDirectoryProvider();
+ Cursor c = provider.buildGalResultCursor(GAL_RESULT_PROJECTION, result);
+ assertNotNull(c);
+ assertEquals(MatrixCursor.class, c.getClass());
+ assertEquals(4, c.getCount());
+ for (int i = 0; i < 4; i++) {
+ GalData data = result.galData.get(i);
+ assertTrue(c.moveToNext());
+ assertEquals(data.displayName, c.getString(GAL_RESULT_DISPLAY_NAME_COLUMN));
+ assertEquals(data.emailAddress, c.getString(GAL_RESULT_EMAIL_ADDRESS_COLUMN));
+ assertNull(c.getString(GAL_RESULT_CONTENT_TYPE_COLUMN));
+ }
+ }
+}