Preliminary GAL/Contacts integration for EAS
Change-Id: I9997ac96f83f427c71caf12d591ba6069bedf935
This commit is contained in:
parent
1a3a3a5aec
commit
5bd2faee5e
|
@ -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>
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
package com.android.email.service;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
import com.android.email.VendorPolicyLoader;
|
||||
import com.android.email.activity.setup.AccountSetupBasics;
|
||||
import com.android.exchange.provider.ExchangeDirectoryProvider;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
|
@ -26,12 +29,14 @@ import android.accounts.AccountManager;
|
|||
import android.accounts.NetworkErrorException;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Calendar;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.Directory;
|
||||
|
||||
/**
|
||||
* A very basic authenticator service for EAS. At the moment, it has no UI hooks. When called
|
||||
|
@ -45,8 +50,11 @@ public class EasAuthenticatorService extends Service {
|
|||
public static final String OPTIONS_CALENDAR_SYNC_ENABLED = "calendar";
|
||||
|
||||
class EasAuthenticator extends AbstractAccountAuthenticator {
|
||||
private Context mContext;
|
||||
|
||||
public EasAuthenticator(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +91,21 @@ public class EasAuthenticatorService extends Service {
|
|||
ContentResolver.setIsSyncable(account, Calendar.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, Calendar.AUTHORITY, syncCalendar);
|
||||
|
||||
// Register our GAL provider
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Directory.DIRECTORY_AUTHORITY,
|
||||
ExchangeDirectoryProvider.EXCHANGE_GAL_AUTHORITY);
|
||||
values.put(Directory.ACCOUNT_NAME, account.name);
|
||||
values.put(Directory.ACCOUNT_TYPE, account.type);
|
||||
values.put(Directory.PACKAGE_NAME, mContext.getPackageName());
|
||||
if (VendorPolicyLoader.getInstance(EasAuthenticatorService.this)
|
||||
.useAlternateExchangeStrings()) {
|
||||
values.put(Directory.TYPE_RESOURCE_ID, R.string.exchange_name_alternate);
|
||||
} else {
|
||||
values.put(Directory.TYPE_RESOURCE_ID, R.string.exchange_name);
|
||||
}
|
||||
values.put(Directory.DISPLAY_NAME, account.name);
|
||||
values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY);
|
||||
Bundle b = new Bundle();
|
||||
b.putString(AccountManager.KEY_ACCOUNT_NAME, options.getString(OPTIONS_USERNAME));
|
||||
b.putString(AccountManager.KEY_ACCOUNT_TYPE, Email.EXCHANGE_ACCOUNT_MANAGER_TYPE);
|
||||
|
@ -144,10 +167,7 @@ public class EasAuthenticatorService extends Service {
|
|||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// TODO Replace this with an appropriate constant in AccountManager, when it's created
|
||||
String authenticatorIntent = "android.accounts.AccountAuthenticator";
|
||||
|
||||
if (authenticatorIntent.equals(intent.getAction())) {
|
||||
if (AccountManager.ACTION_AUTHENTICATOR_INTENT.equals(intent.getAction())) {
|
||||
return new EasAuthenticator(this).getIBinder();
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
@ -788,7 +788,7 @@ public class EasSyncService extends AbstractSyncService {
|
|||
* @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)
|
||||
|
|
|
@ -420,6 +420,15 @@ public class SyncManager extends Service implements Runnable {
|
|||
}
|
||||
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 @@ public class SyncManager extends Service implements Runnable {
|
|||
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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue