Reimplement EAS contacts sync to work w/ new system facilities
* Modify to work with ContactsProvider2 * Modify to work with system AccountManager * Modify to work with system SyncManager (for triggering user-change syncs) * Sync server->client for adds/deletes implemented (CP2 doesn't handle delete yet) * Sync server->client changes handled efficiently (only write changes) * Some fields still not handled * Rewrote most of the CPO code to handle server->client changes * Sync client->server works for supported fields
This commit is contained in:
parent
9150b3005f
commit
948c36f47a
|
@ -24,6 +24,7 @@
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||||
|
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||||
|
|
||||||
<!-- For EAS purposes; could be removed when EAS has a permanent home -->
|
<!-- For EAS purposes; could be removed when EAS has a permanent home -->
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
|
@ -174,6 +175,17 @@
|
||||||
>
|
>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<!--Required stanza to register the ContactsSyncAdapterService with SyncManager -->
|
||||||
|
<service
|
||||||
|
android:name="com.android.exchange.ContactsSyncAdapterService"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.SyncAdapter" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.content.SyncAdapter"
|
||||||
|
android:resource="@xml/syncadapter_contacts" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<!-- Add android:process=":remote" below to enable SyncManager as a separate process -->
|
<!-- Add android:process=":remote" below to enable SyncManager as a separate process -->
|
||||||
<service
|
<service
|
||||||
android:name="com.android.exchange.SyncManager"
|
android:name="com.android.exchange.SyncManager"
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- The attributes in this XML file provide configuration information -->
|
||||||
|
<!-- for the SyncAdapter. -->
|
||||||
|
|
||||||
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:contentAuthority="com.android.contacts"
|
||||||
|
android:accountType="com.android.exchange"
|
||||||
|
/>
|
|
@ -19,6 +19,7 @@ package com.android.email.activity.setup;
|
||||||
import com.android.email.Email;
|
import com.android.email.Email;
|
||||||
import com.android.email.R;
|
import com.android.email.R;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
|
import com.android.email.mail.store.ExchangeStore;
|
||||||
import com.android.email.provider.EmailContent;
|
import com.android.email.provider.EmailContent;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
@ -120,6 +121,11 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
|
||||||
mAccount.setSyncLookback(window);
|
mAccount.setSyncLookback(window);
|
||||||
}
|
}
|
||||||
mAccount.setDefaultAccount(mDefaultView.isChecked());
|
mAccount.setDefaultAccount(mDefaultView.isChecked());
|
||||||
|
// EAS needs a hook to store account information for use by AccountManager
|
||||||
|
if (!mAccount.isSaved() && mAccount.mHostAuthRecv != null
|
||||||
|
&& mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
|
||||||
|
ExchangeStore.addSystemAccount(this, mAccount);
|
||||||
|
}
|
||||||
AccountSettingsUtils.commitSettings(this, mAccount);
|
AccountSettingsUtils.commitSettings(this, mAccount);
|
||||||
Email.setServicesEnabled(this);
|
Email.setServicesEnabled(this);
|
||||||
AccountSetupNames.actionSetNames(this, mAccount.mId);
|
AccountSetupNames.actionSetNames(this, mAccount.mId);
|
||||||
|
|
|
@ -26,6 +26,8 @@ import com.android.email.mail.MessageRetrievalListener;
|
||||||
import com.android.email.mail.MessagingException;
|
import com.android.email.mail.MessagingException;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
import com.android.email.mail.StoreSynchronizer;
|
import com.android.email.mail.StoreSynchronizer;
|
||||||
|
import com.android.email.provider.EmailContent.Account;
|
||||||
|
import com.android.email.service.EasAuthenticatorService;
|
||||||
import com.android.email.service.EmailServiceProxy;
|
import com.android.email.service.EmailServiceProxy;
|
||||||
import com.android.exchange.Eas;
|
import com.android.exchange.Eas;
|
||||||
import com.android.exchange.SyncManager;
|
import com.android.exchange.SyncManager;
|
||||||
|
@ -115,6 +117,34 @@ public class ExchangeStore extends Store {
|
||||||
mTransport.checkSettings(mUri);
|
mTransport.checkSettings(mUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public void addSystemAccount(Context context, Account acct) {
|
||||||
|
// This code was taken from sample code in AccountsTester
|
||||||
|
Bundle options = new Bundle();
|
||||||
|
options.putString(EasAuthenticatorService.OPTIONS_USERNAME, acct.mEmailAddress);
|
||||||
|
options.putString(EasAuthenticatorService.OPTIONS_PASSWORD, acct.mHostAuthRecv.mPassword);
|
||||||
|
Future2Callback callback = new Future2Callback() {
|
||||||
|
public void run(Future2 future) {
|
||||||
|
try {
|
||||||
|
Bundle bundle = future.getResult();
|
||||||
|
bundle.keySet();
|
||||||
|
Log.d(LOG_TAG, "account added: " + bundle);
|
||||||
|
} catch (OperationCanceledException e) {
|
||||||
|
Log.d(LOG_TAG, "addAccount was canceled");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.d(LOG_TAG, "addAccount failed: " + e);
|
||||||
|
} catch (AuthenticatorException e) {
|
||||||
|
Log.d(LOG_TAG, "addAccount failed: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Here's where we tell AccountManager about the new account. The addAccount
|
||||||
|
// method in AccountManager calls the addAccount method in our authenticator
|
||||||
|
// service (EasAuthenticatorService)
|
||||||
|
AccountManager.get(context).addAccount(Eas.ACCOUNT_MANAGER_TYPE, null, null,
|
||||||
|
options, null, callback, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Folder getFolder(String name) throws MessagingException {
|
public Folder getFolder(String name) throws MessagingException {
|
||||||
synchronized (mFolders) {
|
synchronized (mFolders) {
|
||||||
|
@ -287,32 +317,6 @@ public class ExchangeStore extends Store {
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException(result);
|
throw new MessagingException(result);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// This code was taken from sample code in AccountsTester
|
|
||||||
Bundle options = new Bundle();
|
|
||||||
options.putString("username", mUsername);
|
|
||||||
options.putString("password", mPassword);
|
|
||||||
Future2Callback callback = new Future2Callback() {
|
|
||||||
public void run(Future2 future) {
|
|
||||||
try {
|
|
||||||
Bundle bundle = future.getResult();
|
|
||||||
bundle.keySet();
|
|
||||||
Log.d(TAG, "account added: " + bundle);
|
|
||||||
} catch (OperationCanceledException e) {
|
|
||||||
Log.d(TAG, "addAccount was canceled");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.d(TAG, "addAccount failed: " + e);
|
|
||||||
} catch (AuthenticatorException e) {
|
|
||||||
Log.d(TAG, "addAccount failed: " + e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Here's where we tell AccountManager about the new account. The addAccount
|
|
||||||
// method in AccountManager calls the addAccount method in our authenticator
|
|
||||||
// service (EasAuthenticatorService)
|
|
||||||
AccountManager.get(mContext).addAccount(Eas.ACCOUNT_MANAGER_TYPE, null, null,
|
|
||||||
options, null, callback, null);
|
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw new MessagingException("Call to validate generated an exception", e);
|
throw new MessagingException("Call to validate generated an exception", e);
|
||||||
|
|
|
@ -25,10 +25,8 @@ import android.accounts.AccountManager;
|
||||||
import android.accounts.Constants;
|
import android.accounts.Constants;
|
||||||
import android.accounts.NetworkErrorException;
|
import android.accounts.NetworkErrorException;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PermissionInfo;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
@ -38,6 +36,8 @@ import android.os.IBinder;
|
||||||
* password. We will need to implement confirmPassword, confirmCredentials, and updateCredentials.
|
* password. We will need to implement confirmPassword, confirmCredentials, and updateCredentials.
|
||||||
*/
|
*/
|
||||||
public class EasAuthenticatorService extends Service {
|
public class EasAuthenticatorService extends Service {
|
||||||
|
public static final String OPTIONS_USERNAME = "username";
|
||||||
|
public static final String OPTIONS_PASSWORD = "password";
|
||||||
|
|
||||||
class EasAuthenticator extends AbstractAccountAuthenticator {
|
class EasAuthenticator extends AbstractAccountAuthenticator {
|
||||||
public EasAuthenticator(Context context) {
|
public EasAuthenticator(Context context) {
|
||||||
|
@ -50,8 +50,8 @@ public class EasAuthenticatorService extends Service {
|
||||||
throws NetworkErrorException {
|
throws NetworkErrorException {
|
||||||
// The Bundle we are passed has username and password set
|
// The Bundle we are passed has username and password set
|
||||||
AccountManager.get(EasAuthenticatorService.this).blockingAddAccountExplicitly(
|
AccountManager.get(EasAuthenticatorService.this).blockingAddAccountExplicitly(
|
||||||
new Account(options.getString("username"), Eas.ACCOUNT_MANAGER_TYPE),
|
new Account(options.getString(OPTIONS_USERNAME), Eas.ACCOUNT_MANAGER_TYPE),
|
||||||
options.getString("password"), null);
|
options.getString(OPTIONS_PASSWORD), null);
|
||||||
Bundle b = new Bundle();
|
Bundle b = new Bundle();
|
||||||
b.putString(Constants.ACCOUNT_NAME_KEY, options.getString("username"));
|
b.putString(Constants.ACCOUNT_NAME_KEY, options.getString("username"));
|
||||||
b.putString(Constants.ACCOUNT_TYPE_KEY, Eas.ACCOUNT_MANAGER_TYPE);
|
b.putString(Constants.ACCOUNT_TYPE_KEY, Eas.ACCOUNT_MANAGER_TYPE);
|
||||||
|
@ -84,7 +84,7 @@ public class EasAuthenticatorService extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAuthTokenLabel(String authTokenType) {
|
public String getAuthTokenLabel(String authTokenType) {
|
||||||
// null means we don't have compartmentalized authtoken types
|
// null means we don't have compartmentalized authtoken types
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class EasAuthenticatorService extends Service {
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
// TODO Replace this with an appropriate constant in AccountManager, when it's created
|
// TODO Replace this with an appropriate constant in AccountManager, when it's created
|
||||||
String authenticatorIntent = "android.accounts.AccountAuthenticator";
|
String authenticatorIntent = "android.accounts.AccountAuthenticator";
|
||||||
|
|
||||||
if (authenticatorIntent.equals(intent.getAction())) {
|
if (authenticatorIntent.equals(intent.getAction())) {
|
||||||
return new EasAuthenticator(this).getIAccountAuthenticator().asBinder();
|
return new EasAuthenticator(this).getIAccountAuthenticator().asBinder();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* 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 com.android.exchange;
|
||||||
|
|
||||||
|
import com.android.email.provider.EmailContent;
|
||||||
|
import com.android.email.provider.EmailContent.AccountColumns;
|
||||||
|
import com.android.email.provider.EmailContent.Mailbox;
|
||||||
|
import com.android.email.provider.EmailContent.MailboxColumns;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.OperationCanceledException;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class ContactsSyncAdapterService extends Service {
|
||||||
|
private final String TAG = "EAS ContactsSyncAdapterService";
|
||||||
|
private final SyncAdapterImpl mSyncAdapter;
|
||||||
|
|
||||||
|
private static final String[] ID_PROJECTION = new String[] {EmailContent.RECORD_ID};
|
||||||
|
private static final String ACCOUNT_AND_TYPE_CONTACTS =
|
||||||
|
MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS;
|
||||||
|
|
||||||
|
public ContactsSyncAdapterService() {
|
||||||
|
super();
|
||||||
|
mSyncAdapter = new SyncAdapterImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
|
||||||
|
public SyncAdapterImpl() {
|
||||||
|
super(ContactsSyncAdapterService.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performSync(Account account, Bundle extras,
|
||||||
|
String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||||
|
try {
|
||||||
|
ContactsSyncAdapterService.this.performSync(account, extras,
|
||||||
|
authority, provider, syncResult);
|
||||||
|
} catch (OperationCanceledException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return mSyncAdapter.getISyncAdapter().asBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial integration with system SyncManager; we tell our EAS SyncManager to start a contacts
|
||||||
|
* sync when we get the signal from the system SyncManager.
|
||||||
|
* The missing piece at this point is integration with the push/ping mechanism in EAS; this will
|
||||||
|
* be put in place at a later time.
|
||||||
|
*/
|
||||||
|
private void performSync(Account account, Bundle extras, String authority,
|
||||||
|
ContentProviderClient provider, SyncResult syncResult)
|
||||||
|
throws OperationCanceledException {
|
||||||
|
ContentResolver cr = getContentResolver();
|
||||||
|
// Find the (EmailProvider) account associated with this email address
|
||||||
|
Cursor accountCursor =
|
||||||
|
cr.query(com.android.email.provider.EmailContent.Account.CONTENT_URI, ID_PROJECTION,
|
||||||
|
AccountColumns.EMAIL_ADDRESS + "=?", new String[] {account.mName}, null);
|
||||||
|
try {
|
||||||
|
if (accountCursor.moveToFirst()) {
|
||||||
|
long accountId = accountCursor.getLong(0);
|
||||||
|
// Now, find the contacts mailbox associated with the account
|
||||||
|
Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_PROJECTION,
|
||||||
|
ACCOUNT_AND_TYPE_CONTACTS, new String[] {Long.toString(accountId)}, null);
|
||||||
|
try {
|
||||||
|
if (mailboxCursor.moveToFirst()) {
|
||||||
|
Log.i(TAG, "Contact sync requested for " + account.mName);
|
||||||
|
// Ask for a sync from our sync manager
|
||||||
|
SyncManager.serviceRequest(mailboxCursor.getLong(0));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mailboxCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
accountCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -347,6 +347,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||||
return setupEASCommand(method, cmd, null);
|
return setupEASCommand(method, cmd, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private String makeUriString(String cmd, String extra) {
|
private String makeUriString(String cmd, String extra) {
|
||||||
// Cache the authentication string and the command string
|
// Cache the authentication string and the command string
|
||||||
if (mDeviceId == null)
|
if (mDeviceId == null)
|
||||||
|
@ -691,7 +692,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||||
BufferedReader rdr = null;
|
BufferedReader rdr = null;
|
||||||
String id;
|
String id;
|
||||||
if (f.exists() && f.canRead()) {
|
if (f.exists() && f.canRead()) {
|
||||||
rdr = new BufferedReader(new FileReader(f));
|
rdr = new BufferedReader(new FileReader(f), 128);
|
||||||
id = rdr.readLine();
|
id = rdr.readLine();
|
||||||
rdr.close();
|
rdr.close();
|
||||||
return id;
|
return id;
|
||||||
|
@ -853,7 +854,9 @@ public class EasSyncService extends InteractiveSyncService {
|
||||||
mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
|
mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
|
||||||
mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
|
mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
|
||||||
try {
|
try {
|
||||||
if (mMailbox.mServerId.equals(Eas.ACCOUNT_MAILBOX)) {
|
if (mMailbox == null || mAccount == null) {
|
||||||
|
return;
|
||||||
|
} else if (mMailbox.mServerId.equals(Eas.ACCOUNT_MAILBOX)) {
|
||||||
runMain();
|
runMain();
|
||||||
} else {
|
} else {
|
||||||
EasSyncAdapter target;
|
EasSyncAdapter target;
|
||||||
|
@ -861,9 +864,9 @@ public class EasSyncService extends InteractiveSyncService {
|
||||||
mProtocolVersion = mAccount.mProtocolVersion;
|
mProtocolVersion = mAccount.mProtocolVersion;
|
||||||
mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
|
mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
|
||||||
if (mMailbox.mType == Mailbox.TYPE_CONTACTS)
|
if (mMailbox.mType == Mailbox.TYPE_CONTACTS)
|
||||||
target = new EasContactsSyncAdapter(mMailbox);
|
target = new EasContactsSyncAdapter(mMailbox, this);
|
||||||
else {
|
else {
|
||||||
target = new EasEmailSyncAdapter(mMailbox);
|
target = new EasEmailSyncAdapter(mMailbox, this);
|
||||||
}
|
}
|
||||||
// We loop here because someone might have put a request in while we were syncing
|
// We loop here because someone might have put a request in while we were syncing
|
||||||
// and we've missed that opportunity...
|
// and we've missed that opportunity...
|
||||||
|
|
|
@ -31,7 +31,7 @@ import android.util.Log;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data
|
* EmailSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data
|
||||||
* back to the Exchange server.
|
* back to the Exchange server.
|
||||||
*
|
*
|
||||||
* Here's how this works for Email, for example:
|
* Here's how this works for Email, for example:
|
||||||
|
@ -40,15 +40,15 @@ import java.util.ArrayList;
|
||||||
* 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change
|
* 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change
|
||||||
* 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the
|
* 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the
|
||||||
* future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism).
|
* future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism).
|
||||||
* 4) USAR Receiver's onReceive method is called
|
* 4) ESAR Receiver's onReceive method is called
|
||||||
* 5) USAR goes through all change and deletion records and compiles a list of mailboxes which have
|
* 5) ESAR goes through all change and deletion records and compiles a list of mailboxes which have
|
||||||
* changes to be uploaded.
|
* changes to be uploaded.
|
||||||
* 6) USAR calls SyncManager to start syncs of those mailboxes
|
* 6) ESAR calls SyncManager to start syncs of those mailboxes
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class UserSyncAlarmReceiver extends BroadcastReceiver {
|
public class EmailSyncAlarmReceiver extends BroadcastReceiver {
|
||||||
final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY, SyncColumns.DATA};
|
final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY, SyncColumns.DATA};
|
||||||
private static String TAG = "UserSyncAlarm";
|
private static String TAG = "EmailSyncAlarm";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
|
@ -59,7 +59,7 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SyncManager handles all aspects of starting, maintaining, and stopping the various sync
|
* The SyncManager handles all aspects of starting, maintaining, and stopping the various sync
|
||||||
* adapters used by Exchange. However, it is capable of handing any kind of email sync, and it
|
* adapters used by Exchange. However, it is capable of handing any kind of email sync, and it
|
||||||
* would be appropriate to use for IMAP push, when that functionality is added to the Email
|
* would be appropriate to use for IMAP push, when that functionality is added to the Email
|
||||||
* application.
|
* application.
|
||||||
*
|
*
|
||||||
|
@ -91,17 +91,17 @@ public class SyncManager extends Service implements Runnable {
|
||||||
MessageObserver mMessageObserver;
|
MessageObserver mMessageObserver;
|
||||||
String mNextWaitReason;
|
String mNextWaitReason;
|
||||||
IEmailServiceCallback mCallback;
|
IEmailServiceCallback mCallback;
|
||||||
|
|
||||||
RemoteCallbackList<IEmailServiceCallback> mCallbackList =
|
RemoteCallbackList<IEmailServiceCallback> mCallbackList =
|
||||||
new RemoteCallbackList<IEmailServiceCallback>();
|
new RemoteCallbackList<IEmailServiceCallback>();
|
||||||
|
|
||||||
static private HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>();
|
static private HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>();
|
||||||
static private HashMap<Long, PendingIntent> mPendingIntents =
|
static private HashMap<Long, PendingIntent> mPendingIntents =
|
||||||
new HashMap<Long, PendingIntent>();
|
new HashMap<Long, PendingIntent>();
|
||||||
static private WakeLock mWakeLock = null;
|
static private WakeLock mWakeLock = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the binder for EmailService implementation here. These are the calls that are
|
* Create the binder for EmailService implementation here. These are the calls that are
|
||||||
* defined in AbstractSyncService. Only validate is now implemented; loadAttachment currently
|
* defined in AbstractSyncService. Only validate is now implemented; loadAttachment currently
|
||||||
* spins its wheels counting up to 100%.
|
* spins its wheels counting up to 100%.
|
||||||
*/
|
*/
|
||||||
|
@ -183,24 +183,24 @@ public class SyncManager extends Service implements Runnable {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AccountList extends ArrayList<Account> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public boolean contains(long id) {
|
||||||
|
for (Account account: this) {
|
||||||
|
if (account.mId == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AccountObserver extends ContentObserver {
|
class AccountObserver extends ContentObserver {
|
||||||
|
|
||||||
// mAccounts keeps track of Accounts that we care about (EAS for now)
|
// mAccounts keeps track of Accounts that we care about (EAS for now)
|
||||||
AccountList mAccounts = new AccountList();
|
AccountList mAccounts = new AccountList();
|
||||||
|
|
||||||
class AccountList extends ArrayList<Account> {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public boolean contains(long id) {
|
|
||||||
for (Account account: this) {
|
|
||||||
if (account.mId == id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountObserver(Handler handler) {
|
public AccountObserver(Handler handler) {
|
||||||
super(handler);
|
super(handler);
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
|
@ -237,6 +237,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
// A change to the list requires us to scan for deletions (to stop running syncs)
|
// A change to the list requires us to scan for deletions (to stop running syncs)
|
||||||
// At startup, we want to see what accounts exist and cache them
|
// At startup, we want to see what accounts exist and cache them
|
||||||
|
@ -328,6 +329,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
super(handler);
|
super(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
// See if there's anything to do...
|
// See if there's anything to do...
|
||||||
kick();
|
kick();
|
||||||
|
@ -337,7 +339,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
class SyncedMessageObserver extends ContentObserver {
|
class SyncedMessageObserver extends ContentObserver {
|
||||||
long maxChangedId = 0;
|
long maxChangedId = 0;
|
||||||
long maxDeletedId = 0;
|
long maxDeletedId = 0;
|
||||||
Intent syncAlarmIntent = new Intent(INSTANCE, UserSyncAlarmReceiver.class);
|
Intent syncAlarmIntent = new Intent(INSTANCE, EmailSyncAlarmReceiver.class);
|
||||||
PendingIntent syncAlarmPendingIntent =
|
PendingIntent syncAlarmPendingIntent =
|
||||||
PendingIntent.getBroadcast(INSTANCE, 0, syncAlarmIntent, 0);
|
PendingIntent.getBroadcast(INSTANCE, 0, syncAlarmIntent, 0);
|
||||||
AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE);
|
AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
@ -347,6 +349,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
super(handler);
|
super(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
INSTANCE.log("SyncedMessage changed: (re)setting alarm for 10s");
|
INSTANCE.log("SyncedMessage changed: (re)setting alarm for 10s");
|
||||||
alarmManager.set(AlarmManager.RTC_WAKEUP,
|
alarmManager.set(AlarmManager.RTC_WAKEUP,
|
||||||
|
@ -360,6 +363,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
super(handler);
|
super(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
INSTANCE.log("MessageObserver");
|
INSTANCE.log("MessageObserver");
|
||||||
// A rather blunt instrument here. But we don't have information about the URI that
|
// A rather blunt instrument here. But we don't have information about the URI that
|
||||||
|
@ -374,7 +378,15 @@ public class SyncManager extends Service implements Runnable {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public AccountList getAccountList() {
|
||||||
|
if (INSTANCE != null) {
|
||||||
|
return INSTANCE.mAccountObserver.mAccounts;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class SyncStatus {
|
public class SyncStatus {
|
||||||
static public final int NOT_RUNNING = 0;
|
static public final int NOT_RUNNING = 0;
|
||||||
static public final int DIED = 1;
|
static public final int DIED = 1;
|
||||||
|
@ -651,13 +663,6 @@ public class SyncManager extends Service implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
mStop = false;
|
mStop = false;
|
||||||
|
|
||||||
// if (Debug.isDebuggerConnected()) {
|
|
||||||
// try {
|
|
||||||
// Thread.sleep(10000L);
|
|
||||||
// } catch (InterruptedException e) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
runAwake(-1);
|
runAwake(-1);
|
||||||
|
|
||||||
ContentResolver resolver = getContentResolver();
|
ContentResolver resolver = getContentResolver();
|
||||||
|
@ -668,7 +673,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
|
|
||||||
ConnectivityReceiver cr = new ConnectivityReceiver();
|
ConnectivityReceiver cr = new ConnectivityReceiver();
|
||||||
registerReceiver(cr, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
registerReceiver(cr, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -721,6 +726,22 @@ public class SyncManager extends Service implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
long checkMailboxes () {
|
long checkMailboxes () {
|
||||||
|
// First, see if any running mailboxes have been deleted
|
||||||
|
ArrayList<Long> deadMailboxes = new ArrayList<Long>();
|
||||||
|
synchronized (mSyncToken) {
|
||||||
|
for (long mailboxId: mServiceMap.keySet()) {
|
||||||
|
Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mailboxId);
|
||||||
|
if (m == null) {
|
||||||
|
deadMailboxes.add(mailboxId);
|
||||||
|
log("Stopping sync for mailbox " + mailboxId + "; record not found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If so, stop them
|
||||||
|
for (Long mailboxId: deadMailboxes) {
|
||||||
|
stopManualSync(mailboxId);
|
||||||
|
}
|
||||||
|
|
||||||
long nextWait = 10*MINS;
|
long nextWait = 10*MINS;
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
// Start up threads that need it...
|
// Start up threads that need it...
|
||||||
|
@ -800,7 +821,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
}
|
}
|
||||||
return nextWait;
|
return nextWait;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void serviceRequest(Mailbox m) {
|
static public void serviceRequest(Mailbox m) {
|
||||||
serviceRequest(m.mId, 5*SECS);
|
serviceRequest(m.mId, 5*SECS);
|
||||||
}
|
}
|
||||||
|
@ -881,7 +902,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
/**
|
/**
|
||||||
* Determine whether a given Mailbox can be synced, i.e. is not already syncing and is not in
|
* Determine whether a given Mailbox can be synced, i.e. is not already syncing and is not in
|
||||||
* an error state
|
* an error state
|
||||||
*
|
*
|
||||||
* @param mailboxId
|
* @param mailboxId
|
||||||
* @return whether or not the Mailbox is available for syncing (i.e. is a valid push target)
|
* @return whether or not the Mailbox is available for syncing (i.e. is a valid push target)
|
||||||
*/
|
*/
|
||||||
|
@ -896,7 +917,7 @@ public class SyncManager extends Service implements Runnable {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public int getSyncStatus(long mailboxId) {
|
static public int getSyncStatus(long mailboxId) {
|
||||||
synchronized (mSyncToken) {
|
synchronized (mSyncToken) {
|
||||||
if (INSTANCE == null || INSTANCE.mServiceMap == null) {
|
if (INSTANCE == null || INSTANCE.mServiceMap == null) {
|
||||||
|
@ -984,30 +1005,32 @@ public class SyncManager extends Service implements Runnable {
|
||||||
* @param svc the service that is finished
|
* @param svc the service that is finished
|
||||||
*/
|
*/
|
||||||
static public void done(AbstractSyncService svc) {
|
static public void done(AbstractSyncService svc) {
|
||||||
long mailboxId = svc.mMailboxId;
|
synchronized(mSyncToken) {
|
||||||
HashMap<Long, SyncError> errorMap = INSTANCE.mSyncErrorMap;
|
long mailboxId = svc.mMailboxId;
|
||||||
SyncError syncError = errorMap.get(mailboxId);
|
HashMap<Long, SyncError> errorMap = INSTANCE.mSyncErrorMap;
|
||||||
INSTANCE.mServiceMap.remove(mailboxId);
|
SyncError syncError = errorMap.get(mailboxId);
|
||||||
int exitStatus = svc.mExitStatus;
|
INSTANCE.mServiceMap.remove(mailboxId);
|
||||||
switch (exitStatus) {
|
int exitStatus = svc.mExitStatus;
|
||||||
case AbstractSyncService.EXIT_DONE:
|
switch (exitStatus) {
|
||||||
if (!svc.mPartRequests.isEmpty()) {
|
case AbstractSyncService.EXIT_DONE:
|
||||||
// TODO Handle this case
|
if (!svc.mPartRequests.isEmpty()) {
|
||||||
}
|
// TODO Handle this case
|
||||||
errorMap.remove(mailboxId);
|
}
|
||||||
break;
|
errorMap.remove(mailboxId);
|
||||||
case AbstractSyncService.EXIT_IO_ERROR:
|
break;
|
||||||
if (syncError != null) {
|
case AbstractSyncService.EXIT_IO_ERROR:
|
||||||
syncError.escalate();
|
if (syncError != null) {
|
||||||
} else {
|
syncError.escalate();
|
||||||
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, false));
|
} else {
|
||||||
}
|
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, false));
|
||||||
kick();
|
}
|
||||||
break;
|
kick();
|
||||||
case AbstractSyncService.EXIT_LOGIN_FAILURE:
|
break;
|
||||||
case AbstractSyncService.EXIT_EXCEPTION:
|
case AbstractSyncService.EXIT_LOGIN_FAILURE:
|
||||||
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, true));
|
case AbstractSyncService.EXIT_EXCEPTION:
|
||||||
break;
|
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,6 +1057,6 @@ public class SyncManager extends Service implements Runnable {
|
||||||
if (INSTANCE == null) {
|
if (INSTANCE == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (Context)INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class EasCalendarSyncAdapter extends EasSyncAdapter {
|
public class EasCalendarSyncAdapter extends EasSyncAdapter {
|
||||||
|
|
||||||
public EasCalendarSyncAdapter(Mailbox mailbox) {
|
public EasCalendarSyncAdapter(Mailbox mailbox, EasSyncService service) {
|
||||||
super(mailbox);
|
super(mailbox, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -63,8 +63,8 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
|
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
|
||||||
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
|
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
|
||||||
|
|
||||||
public EasEmailSyncAdapter(Mailbox mailbox) {
|
public EasEmailSyncAdapter(Mailbox mailbox, EasSyncService service) {
|
||||||
super(mailbox);
|
super(mailbox, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,10 +72,10 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
EasEmailSyncParser p = new EasEmailSyncParser(is, service);
|
EasEmailSyncParser p = new EasEmailSyncParser(is, service);
|
||||||
return p.parse();
|
return p.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EasEmailSyncParser extends EasContentParser {
|
public class EasEmailSyncParser extends EasContentParser {
|
||||||
|
|
||||||
private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY =
|
private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY =
|
||||||
SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?";
|
SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?";
|
||||||
|
|
||||||
private String mMailboxIdAsString;
|
private String mMailboxIdAsString;
|
||||||
|
@ -88,6 +88,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void wipe() {
|
public void wipe() {
|
||||||
mContentResolver.delete(Message.CONTENT_URI,
|
mContentResolver.delete(Message.CONTENT_URI,
|
||||||
Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
|
Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
|
||||||
|
@ -174,7 +175,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
|
|
||||||
while (nextTag(EasTags.SYNC_ADD) != END) {
|
while (nextTag(EasTags.SYNC_ADD) != END) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case EasTags.SYNC_SERVER_ID:
|
case EasTags.SYNC_SERVER_ID:
|
||||||
msg.mServerId = getValue();
|
msg.mServerId = getValue();
|
||||||
break;
|
break;
|
||||||
case EasTags.SYNC_APPLICATION_DATA:
|
case EasTags.SYNC_APPLICATION_DATA:
|
||||||
|
@ -389,6 +390,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see com.android.exchange.adapter.EasContentParser#commandsParser()
|
* @see com.android.exchange.adapter.EasContentParser#commandsParser()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void commandsParser() throws IOException {
|
public void commandsParser() throws IOException {
|
||||||
ArrayList<Message> newEmails = new ArrayList<Message>();
|
ArrayList<Message> newEmails = new ArrayList<Message>();
|
||||||
ArrayList<Long> deletedEmails = new ArrayList<Long>();
|
ArrayList<Long> deletedEmails = new ArrayList<Long>();
|
||||||
|
@ -436,7 +438,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||||
mMailbox.toContentValues()).build());
|
mMailbox.toContentValues()).build());
|
||||||
|
|
||||||
addCleanupOps(ops);
|
addCleanupOps(ops);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mService.mContext.getContentResolver()
|
mService.mContext.getContentResolver()
|
||||||
.applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
|
.applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
|
||||||
|
|
|
@ -51,7 +51,7 @@ import java.util.List;
|
||||||
|
|
||||||
public class EasFolderSyncParser extends EasParser {
|
public class EasFolderSyncParser extends EasParser {
|
||||||
|
|
||||||
private static boolean DEBUG_LOGGING = false;
|
private static boolean DEBUG_LOGGING = true;
|
||||||
|
|
||||||
public static final String TAG = "FolderSyncParser";
|
public static final String TAG = "FolderSyncParser";
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public abstract class EasSyncAdapter {
|
public abstract class EasSyncAdapter {
|
||||||
public Mailbox mMailbox;
|
public Mailbox mMailbox;
|
||||||
|
public EasSyncService mService;
|
||||||
|
|
||||||
// Create the data for local changes that need to be sent up to the server
|
// Create the data for local changes that need to be sent up to the server
|
||||||
public abstract boolean sendLocalChanges(EasSerializer s, EasSyncService service)
|
public abstract boolean sendLocalChanges(EasSerializer s, EasSyncService service)
|
||||||
|
@ -41,8 +42,9 @@ public abstract class EasSyncAdapter {
|
||||||
public abstract String getCollectionName();
|
public abstract String getCollectionName();
|
||||||
public abstract void cleanup(EasSyncService service);
|
public abstract void cleanup(EasSyncService service);
|
||||||
|
|
||||||
public EasSyncAdapter(Mailbox mailbox) {
|
public EasSyncAdapter(Mailbox mailbox, EasSyncService service) {
|
||||||
mMailbox = mailbox;
|
mMailbox = mailbox;
|
||||||
|
mService = service;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,8 +329,9 @@ public class EasTags {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x01 Contacts
|
// 0x01 Contacts
|
||||||
"Anniversary", "AssistantName", "AssistantTelephoneNumber", "Birthday", "Body",
|
"Anniversary", "AssistantName", "AssistantTelephoneNumber", "Birthday", "ContactsBody",
|
||||||
"BodySize", "BodyTruncated", "Business2TelephoneNumber", "BusinessAddressCity",
|
"ContactsBodySize", "ContactsBodyTruncated", "Business2TelephoneNumber",
|
||||||
|
"BusinessAddressCity",
|
||||||
"BusinessAddressCountry", "BusinessAddressPostalCode", "BusinessAddressState",
|
"BusinessAddressCountry", "BusinessAddressPostalCode", "BusinessAddressState",
|
||||||
"BusinessAddressStreet", "BusinessFaxNumber", "BusinessTelephoneNumber",
|
"BusinessAddressStreet", "BusinessFaxNumber", "BusinessTelephoneNumber",
|
||||||
"CarTelephoneNumber", "ContactsCategories", "ContactsCategory", "Children", "Child",
|
"CarTelephoneNumber", "ContactsCategories", "ContactsCategory", "Children", "Child",
|
||||||
|
@ -338,8 +339,8 @@ public class EasTags {
|
||||||
"FileAs", "FirstName", "Home2TelephoneNumber", "HomeAddressCity", "HomeAddressCountry",
|
"FileAs", "FirstName", "Home2TelephoneNumber", "HomeAddressCity", "HomeAddressCountry",
|
||||||
"HomeAddressPostalCode", "HomeAddressState", "HomeAddressStreet", "HomeFaxNumber",
|
"HomeAddressPostalCode", "HomeAddressState", "HomeAddressStreet", "HomeFaxNumber",
|
||||||
"HomeTelephoneNumber", "JobTitle", "LastName", "MiddleName", "MobileTelephoneNumber",
|
"HomeTelephoneNumber", "JobTitle", "LastName", "MiddleName", "MobileTelephoneNumber",
|
||||||
"OfficeLocation", "OfficeAddressCity", "OfficeAddressCountry",
|
"OfficeLocation", "OtherAddressCity", "OtherAddressCountry",
|
||||||
"OfficeAddressPostalCode", "OfficeAddressState", "OfficeAddressStreet", "PagerNumber",
|
"OtherAddressPostalCode", "OtherAddressState", "OtherAddressStreet", "PagerNumber",
|
||||||
"RadioTelephoneNumber", "Spouse", "Suffix", "Title", "Webpage", "YomiCompanyName",
|
"RadioTelephoneNumber", "Spouse", "Suffix", "Title", "Webpage", "YomiCompanyName",
|
||||||
"YomiFirstName", "YomiLastName", "CompressedRTF", "Picture"
|
"YomiFirstName", "YomiLastName", "CompressedRTF", "Picture"
|
||||||
},
|
},
|
||||||
|
@ -354,7 +355,7 @@ public class EasTags {
|
||||||
"Recurrence_Occurrences", "Recurrence_Interval", "Recurrence_DayOfWeek",
|
"Recurrence_Occurrences", "Recurrence_Interval", "Recurrence_DayOfWeek",
|
||||||
"Recurrence_DayOfMonth", "Recurrence_WeekOfMonth", "Recurrence_MonthOfYear",
|
"Recurrence_DayOfMonth", "Recurrence_WeekOfMonth", "Recurrence_MonthOfYear",
|
||||||
"StartTime", "Sensitivity", "TimeZone", "GlobalObjId", "ThreadTopic", "MIMEData",
|
"StartTime", "Sensitivity", "TimeZone", "GlobalObjId", "ThreadTopic", "MIMEData",
|
||||||
"MIMETruncated", "MIMESize", "InternetCPID", "Flag", "FlagStatus", "ContentClass",
|
"MIMETruncated", "MIMESize", "InternetCPID", "Flag", "FlagStatus", "EmailContentClass",
|
||||||
"FlagType", "CompleteTime"
|
"FlagType", "CompleteTime"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -375,7 +376,7 @@ public class EasTags {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x05 Move
|
// 0x05 Move
|
||||||
"MoveItems", "Move", "SrcMsgId", "SrcFldId", "DstFldId", "Response", "Status",
|
"MoveItems", "Move", "SrcMsgId", "SrcFldId", "DstFldId", "MoveResponse", "MoveStatus",
|
||||||
"DstMsgId"
|
"DstMsgId"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -384,9 +385,9 @@ public class EasTags {
|
||||||
{
|
{
|
||||||
// 0x07 FolderHierarchy
|
// 0x07 FolderHierarchy
|
||||||
"Folders", "Folder", "FolderDisplayName", "FolderServerId", "FolderParentId", "Type",
|
"Folders", "Folder", "FolderDisplayName", "FolderServerId", "FolderParentId", "Type",
|
||||||
"Response", "Status", "ContentClass", "Changes", "FolderAdd", "FolderDelete",
|
"FolderResponse", "FolderStatus", "FolderContentClass", "Changes", "FolderAdd",
|
||||||
"FolderUpdate", "FolderSyncKey", "FolderCreate", "FolderDelete", "FolderUpdate",
|
"FolderDelete", "FolderUpdate", "FolderSyncKey", "FolderFolderCreate",
|
||||||
"FolderSync", "Count", "Version"
|
"FolderFolderDelete", "FolderFolderUpdate", "FolderSync", "Count", "FolderVersion"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x08 MeetingResponse
|
// 0x08 MeetingResponse
|
||||||
|
@ -407,12 +408,12 @@ public class EasTags {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x0D Ping
|
// 0x0D Ping
|
||||||
"Ping", "AutdState", "Status", "HeartbeatInterval", "PingFolders", "PingFolder",
|
"Ping", "AutdState", "PingStatus", "HeartbeatInterval", "PingFolders", "PingFolder",
|
||||||
"PingId", "PingClass", "MaxFolders"
|
"PingId", "PingClass", "MaxFolders"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x0E Provision
|
// 0x0E Provision
|
||||||
"Provision", "Policies", "Policy", "PolicyType", "PolicyKey", "Data", "Status",
|
"Provision", "Policies", "Policy", "PolicyType", "PolicyKey", "Data", "ProvisionStatus",
|
||||||
"RemoteWipe", "EASProvidionDoc", "DevicePasswordEnabled",
|
"RemoteWipe", "EASProvidionDoc", "DevicePasswordEnabled",
|
||||||
"AlphanumericDevicePasswordRequired",
|
"AlphanumericDevicePasswordRequired",
|
||||||
"DeviceEncryptionEnabled", "-unused-", "AttachmentsEnabled", "MinDevicePasswordLength",
|
"DeviceEncryptionEnabled", "-unused-", "AttachmentsEnabled", "MinDevicePasswordLength",
|
||||||
|
@ -436,8 +437,8 @@ public class EasTags {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x10 Gal
|
// 0x10 Gal
|
||||||
"DisplayName", "Phone", "Office", "Title", "Company", "Alias", "FirstName", "LastName",
|
"GalDisplayName", "GalPhone", "GalOffice", "GalTitle", "GalCompany", "GalAlias",
|
||||||
"HomePhone", "MobilePhone", "EmailAddress"
|
"GalFirstName", "GalLastName", "GalHomePhone", "GalMobilePhone", "GalEmailAddress"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 0x11 AirSyncBase
|
// 0x11 AirSyncBase
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class EasEmailSyncAdapterTests extends AndroidTestCase {
|
||||||
service.mContext = getContext();
|
service.mContext = getContext();
|
||||||
service.mMailbox = mailbox;
|
service.mMailbox = mailbox;
|
||||||
service.mAccount = account;
|
service.mAccount = account;
|
||||||
EasEmailSyncAdapter adapter = new EasEmailSyncAdapter(mailbox);
|
EasEmailSyncAdapter adapter = new EasEmailSyncAdapter(mailbox, service);
|
||||||
EasEmailSyncParser p;
|
EasEmailSyncParser p;
|
||||||
p = adapter.new EasEmailSyncParser(getTestInputStream(), service);
|
p = adapter.new EasEmailSyncParser(getTestInputStream(), service);
|
||||||
// Test a few known types
|
// Test a few known types
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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 com.android.exchange;
|
||||||
|
|
||||||
|
import com.android.email.provider.EmailContent.Account;
|
||||||
|
import com.android.email.provider.EmailContent.Mailbox;
|
||||||
|
import com.android.exchange.adapter.EasEmailSyncAdapter;
|
||||||
|
import com.android.exchange.adapter.EasTags;
|
||||||
|
import com.android.exchange.adapter.EasEmailSyncAdapter.EasEmailSyncParser;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class EasTagsTests extends AndroidTestCase {
|
||||||
|
|
||||||
|
// Make sure there are no duplicates in the tags table
|
||||||
|
public void testNoDuplicates() {
|
||||||
|
String[][] allTags = EasTags.pages;
|
||||||
|
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
|
||||||
|
for (String[] page: allTags) {
|
||||||
|
for (String tag: page) {
|
||||||
|
assertTrue(!map.containsKey(tag));
|
||||||
|
map.put(tag, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue