Various EAS related changes related to accounts and services.
* Renamed ISyncManager/ISyncManagerCallback to IEmailService/IEmailServiceCallback * Restored ExchangeTransportExample to its original state; created ExchangeStore to handle validation functionality instead; updated stores.xml to reflect these changes. * Add support for AccountManager in EAS code (this is necessary for the contacts and calendar providers to work with syncable data); created EasAuthenticatorService to as our authenticator, which required adding authenticator.xml and modifying the manifest to register our service with AccountManager metadata * Created EmailServiceProxy as a convenience for the UI in calling into the EAS service; created EmailServiceStatus class for status codes in callbacks.
This commit is contained in:
parent
ded3c915d8
commit
17250429db
|
@ -17,8 +17,9 @@ include $(CLEAR_VARS)
|
|||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_SRC_FILES += \
|
||||
src/com/android/exchange/ISyncManager.aidl \
|
||||
src/com/android/exchange/ISyncManagerCallback.aidl
|
||||
src/com/android/exchange/IEmailService.aidl \
|
||||
src/com/android/exchange/IEmailServiceCallback.aidl
|
||||
|
||||
|
||||
LOCAL_PACKAGE_NAME := Email
|
||||
|
||||
|
|
|
@ -178,6 +178,16 @@
|
|||
android:enabled="true"
|
||||
>
|
||||
</service>
|
||||
|
||||
<!--Required stanza to register the EasAuthenticatorService with AccountManager -->
|
||||
<service android:name=".service.EasAuthenticatorService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name=".provider.AttachmentProvider"
|
||||
android:authorities="com.android.email.attachmentprovider"
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?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 Account Manager. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.exchange"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
/>
|
|
@ -35,6 +35,6 @@
|
|||
<store scheme="imap" class="com.android.email.mail.store.ImapStore" />
|
||||
|
||||
<!-- This is here for temporary demo purposes only. Do not ship with this. -->
|
||||
<store scheme="eas" class="com.android.email.mail.exchange.ExchangeStoreExample"
|
||||
push="true" visibleLimitDefault="-1" visibleLimitIncrement="-1" accountInstanceLimit="1" />
|
||||
<store scheme="eas" class="com.android.email.mail.store.ExchangeStore"
|
||||
push="true" visibleLimitDefault="-1" visibleLimitIncrement="-1" />
|
||||
</stores>
|
||||
|
|
|
@ -33,6 +33,8 @@ import com.android.email.mail.Message.RecipientType;
|
|||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.mail.store.LocalStore.LocalMessage;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
import com.android.email.provider.EmailContent.MailboxColumns;
|
||||
|
||||
import android.app.ExpandableListActivity;
|
||||
import android.app.NotificationManager;
|
||||
|
@ -996,12 +998,14 @@ public class FolderMessageList extends ExpandableListActivity {
|
|||
// to match desired UI presentation (e.g. INBOX at the top)
|
||||
@Override
|
||||
protected Cursor doInBackground(Void... params) {
|
||||
// Only show boxes with mail, and sort secondarily by name
|
||||
return FolderMessageList.this.managedQuery(
|
||||
EmailContent.Mailbox.CONTENT_URI,
|
||||
EmailContent.Mailbox.CONTENT_PROJECTION,
|
||||
EmailContent.MailboxColumns.ACCOUNT_KEY + "=?",
|
||||
Mailbox.CONTENT_URI,
|
||||
Mailbox.CONTENT_PROJECTION,
|
||||
MailboxColumns.ACCOUNT_KEY + "=? and "
|
||||
+ MailboxColumns.TYPE + '<' + Mailbox.TYPE_NOT_EMAIL,
|
||||
new String[] { String.valueOf(FolderMessageList.this.mAccountId) },
|
||||
EmailContent.MailboxColumns.TYPE);
|
||||
MailboxColumns.TYPE + ',' + MailboxColumns.DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,7 +82,7 @@ public class MessageListItem extends RelativeLayout {
|
|||
int touchX = (int) event.getX();
|
||||
|
||||
if (!mCachedViewPositions) {
|
||||
float paddingScale = mContext.getResources().getDisplayMetrics().density;
|
||||
float paddingScale = getContext().getResources().getDisplayMetrics().density;
|
||||
int checkPadding = (int) ((CHECKMARK_PAD * paddingScale) + 0.5);
|
||||
int starPadding = (int) ((STAR_PAD * paddingScale) + 0.5);
|
||||
mCheckRight = findViewById(R.id.selected).getRight() + checkPadding;
|
||||
|
|
|
@ -17,18 +17,9 @@
|
|||
package com.android.email.mail.exchange;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.mail.AuthenticationFailedException;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.exchange.ISyncManager;
|
||||
import com.android.exchange.SyncManager;
|
||||
//import com.android.exchange.EasService;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -44,6 +35,7 @@ public class ExchangeTransportExample {
|
|||
|
||||
public static final String FOLDER_INBOX = Email.INBOX;
|
||||
|
||||
private static final String TAG = "ExchangeTransportExample";
|
||||
private final Context mContext;
|
||||
|
||||
private String mHost;
|
||||
|
@ -115,52 +107,10 @@ public class ExchangeTransportExample {
|
|||
* @param uri the server/account to try and connect to
|
||||
* @throws MessagingException thrown if the connection, server, account are not useable
|
||||
*/
|
||||
ISyncManager mSyncManagerService = null;
|
||||
|
||||
private ServiceConnection mSyncManagerConnection = new ServiceConnection () {
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
mSyncManagerService = ISyncManager.Stub.asInterface(binder);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mSyncManagerService = null;
|
||||
}
|
||||
};
|
||||
|
||||
public void checkSettings(URI uri) throws MessagingException {
|
||||
setUri(uri);
|
||||
boolean ssl = uri.getScheme().contains("ssl+");
|
||||
try {
|
||||
mContext.bindService(new Intent(mContext, SyncManager.class), mSyncManagerConnection, Context.BIND_AUTO_CREATE);
|
||||
// TODO Is this the right way of waiting for a connection??
|
||||
int count = 0;
|
||||
while ((count++ < 10) && (mSyncManagerService == null)) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
if (mSyncManagerService == null) {
|
||||
throw new MessagingException(MessagingException.UNSPECIFIED_EXCEPTION);
|
||||
}
|
||||
// The result of validate is a MessagingException error code
|
||||
// We use NO_ERROR to indicate a validated account
|
||||
int result = mSyncManagerService.validate("eas", mHost, mUsername, mPassword, ssl ? 443 : 80, ssl);
|
||||
if (result != MessagingException.NO_ERROR) {
|
||||
if (result == MessagingException.AUTHENTICATION_FAILED) {
|
||||
throw new AuthenticationFailedException("Authentication failed.");
|
||||
} else {
|
||||
throw new MessagingException(result);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new MessagingException("Call to validate generated an exception", e);
|
||||
} catch (InterruptedException e) {
|
||||
} finally {
|
||||
if (mSyncManagerService != null) {
|
||||
mContext.unbindService(mSyncManagerConnection);
|
||||
} else
|
||||
throw new MessagingException(MessagingException.UNSPECIFIED_EXCEPTION);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typical helper function: Return existence of a given folder
|
||||
|
|
|
@ -0,0 +1,497 @@
|
|||
/*
|
||||
* 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.email.mail.store;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.mail.AuthenticationFailedException;
|
||||
import com.android.email.mail.FetchProfile;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessageRetrievalListener;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.mail.StoreSynchronizer;
|
||||
import com.android.email.service.EmailServiceProxy;
|
||||
import com.android.exchange.Eas;
|
||||
import com.android.exchange.IEmailService;
|
||||
import com.android.exchange.IEmailServiceCallback;
|
||||
import com.android.exchange.SyncManager;
|
||||
import com.android.exchange.EmailContent.Attachment;
|
||||
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.Future2;
|
||||
import android.accounts.Future2Callback;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This is a placeholder for use in Exchange implementations. It is based on the notion of
|
||||
* lightweight adapter classes for Store, Folder, and Sender, and a common facade for the common
|
||||
* Transport code.
|
||||
*/
|
||||
public class ExchangeStore extends Store {
|
||||
public static final String LOG_TAG = "ExchangeStore";
|
||||
|
||||
private final Context mContext;
|
||||
private URI mUri;
|
||||
private PersistentDataCallbacks mCallbacks;
|
||||
|
||||
private final ExchangeTransport mTransport;
|
||||
private final HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
|
||||
|
||||
private boolean mPushModeRunning = false;
|
||||
|
||||
/**
|
||||
* Factory method.
|
||||
*/
|
||||
public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
|
||||
throws MessagingException {
|
||||
return new ExchangeStore(uri, context, callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* eas://user:password@server/domain
|
||||
*
|
||||
* @param _uri
|
||||
* @param application
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private ExchangeStore(String _uri, Context context, PersistentDataCallbacks callbacks)
|
||||
throws MessagingException {
|
||||
mContext = context;
|
||||
try {
|
||||
mUri = new URI(_uri);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new MessagingException("Invalid uri for ExchangeStore");
|
||||
}
|
||||
mCallbacks = callbacks;
|
||||
|
||||
String scheme = mUri.getScheme();
|
||||
int connectionSecurity;
|
||||
if (scheme.equals("eas")) {
|
||||
connectionSecurity = ExchangeTransport.CONNECTION_SECURITY_NONE;
|
||||
} else if (scheme.equals("eas+ssl+")) {
|
||||
connectionSecurity = ExchangeTransport.CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
} else {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
|
||||
mTransport = ExchangeTransport.getInstance(mUri, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying transport. Used primarily for testing.
|
||||
* @return
|
||||
*/
|
||||
/* package */ ExchangeTransport getTransport() {
|
||||
return mTransport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSettings() throws MessagingException {
|
||||
mTransport.checkSettings(mUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder getFolder(String name) throws MessagingException {
|
||||
synchronized (mFolders) {
|
||||
Folder folder = mFolders.get(name);
|
||||
if (folder == null) {
|
||||
folder = new ExchangeFolder(this, name);
|
||||
mFolders.put(folder.getName(), folder);
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder[] getPersonalNamespaces() throws MessagingException {
|
||||
return new Folder[] {
|
||||
getFolder(ExchangeTransport.FOLDER_INBOX),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* For a store that supports push mode, this is the API that enables it or disables it.
|
||||
* The store should use this API to start or stop its persistent connection service or thread.
|
||||
*
|
||||
* <p>Note, may be called multiple times, even after push mode has been started or stopped.
|
||||
*
|
||||
* @param enablePushMode start or stop push mode delivery
|
||||
*/
|
||||
@Override
|
||||
public void enablePushModeDelivery(boolean enablePushMode) {
|
||||
if (Config.LOGD && Email.DEBUG) {
|
||||
if (enablePushMode && !mPushModeRunning) {
|
||||
Log.d(Email.LOG_TAG, "start push mode");
|
||||
} else if (!enablePushMode && mPushModeRunning) {
|
||||
Log.d(Email.LOG_TAG, "stop push mode");
|
||||
} else {
|
||||
Log.d(Email.LOG_TAG, enablePushMode ?
|
||||
"push mode already started" : "push mode already stopped");
|
||||
}
|
||||
}
|
||||
mPushModeRunning = enablePushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class of SettingActivity for this Store class.
|
||||
* @return Activity class that has class method actionEditIncomingSettings()
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends android.app.Activity> getSettingActivityClass() {
|
||||
return com.android.email.activity.setup.AccountSetupExchange.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class of sync'er for this Store class. Because exchange Sync rules are so different
|
||||
* than IMAP or POP3, it's likely that an Exchange implementation will need its own sync
|
||||
* controller. If so, this function must return a non-null value.
|
||||
*
|
||||
* @return Message Sync controller, or null to use default
|
||||
*/
|
||||
@Override
|
||||
public StoreSynchronizer getMessageSynchronizer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform MessagingController that this store requires message structures to be prefetched
|
||||
* before it can fetch message bodies (this is due to EAS protocol restrictions.)
|
||||
* @return always true for EAS
|
||||
*/
|
||||
@Override
|
||||
public boolean requireStructurePrefetch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform MessagingController that messages sent via EAS will be placed in the Sent folder
|
||||
* automatically (server-side) and don't need to be uploaded.
|
||||
* @return always false for EAS (assuming server-side copy is supported)
|
||||
*/
|
||||
@Override
|
||||
public boolean requireCopyMessageToSentFolder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class ExchangeTransport {
|
||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 1;
|
||||
|
||||
public static final String FOLDER_INBOX = Email.INBOX;
|
||||
|
||||
private static final String TAG = "ExchangeTransport";
|
||||
private final Context mContext;
|
||||
|
||||
private String mHost;
|
||||
private String mDomain;
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
|
||||
private static HashMap<String, ExchangeTransport> sUriToInstanceMap =
|
||||
new HashMap<String, ExchangeTransport>();
|
||||
private static final HashMap<String, Integer> sFolderMap = new HashMap<String, Integer>();
|
||||
|
||||
/**
|
||||
* Public factory. The transport should be a singleton (per Uri)
|
||||
*/
|
||||
public synchronized static ExchangeTransport getInstance(URI uri, Context context)
|
||||
throws MessagingException {
|
||||
if (!uri.getScheme().equals("eas") && !uri.getScheme().equals("eas+ssl+")) {
|
||||
throw new MessagingException("Invalid scheme");
|
||||
}
|
||||
|
||||
final String key = uri.toString();
|
||||
ExchangeTransport transport = sUriToInstanceMap.get(key);
|
||||
if (transport == null) {
|
||||
transport = new ExchangeTransport(uri, context);
|
||||
sUriToInstanceMap.put(key, transport);
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor - use public factory.
|
||||
*/
|
||||
private ExchangeTransport(URI uri, Context context) throws MessagingException {
|
||||
mContext = context;
|
||||
setUri(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Uri to set up a newly-constructed transport
|
||||
* @param uri
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void setUri(final URI uri) throws MessagingException {
|
||||
mHost = uri.getHost();
|
||||
if (mHost == null) {
|
||||
throw new MessagingException("host not specified");
|
||||
}
|
||||
|
||||
mDomain = uri.getPath();
|
||||
if (!TextUtils.isEmpty(mDomain)) {
|
||||
mDomain = mDomain.substring(1);
|
||||
}
|
||||
|
||||
final String userInfo = uri.getUserInfo();
|
||||
if (userInfo == null) {
|
||||
throw new MessagingException("user information not specifed");
|
||||
}
|
||||
final String[] uinfo = userInfo.split(":", 2);
|
||||
if (uinfo.length != 2) {
|
||||
throw new MessagingException("user name and password not specified");
|
||||
}
|
||||
mUsername = uinfo[0];
|
||||
mPassword = uinfo[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking call that checks for a useable server connection, credentials, etc.
|
||||
* @param uri the server/account to try and connect to
|
||||
* @throws MessagingException thrown if the connection, server, account are not useable
|
||||
*/
|
||||
|
||||
IEmailServiceCallback mCallback = new IEmailServiceCallback () {
|
||||
|
||||
public void status(int statusCode, int progress) throws RemoteException {
|
||||
Log.d("Status: ", "Code = " + statusCode + ", progress = " + progress);
|
||||
}
|
||||
|
||||
public IBinder asBinder() { return null; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Here's where we check the settings for EAS.
|
||||
* @param uri the URI of the account to create
|
||||
* @throws MessagingException if we can't authenticate the account
|
||||
*/
|
||||
public void checkSettings(URI uri) throws MessagingException {
|
||||
setUri(uri);
|
||||
boolean ssl = uri.getScheme().contains("ssl+");
|
||||
try {
|
||||
IEmailService svc = EmailServiceProxy.getService(mContext, SyncManager.class);
|
||||
int result = svc.validate("eas", mHost, mUsername, mPassword, ssl ? 443 : 80, ssl);
|
||||
if (result != MessagingException.NO_ERROR) {
|
||||
if (result == MessagingException.AUTHENTICATION_FAILED) {
|
||||
throw new AuthenticationFailedException("Authentication failed.");
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
svc.loadAttachment(0, new Attachment(), mCallback);
|
||||
} catch (RemoteException e) {
|
||||
throw new MessagingException("Call to validate generated an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typical helper function: Return existence of a given folder
|
||||
*/
|
||||
public boolean isFolderAvailable(final String folder) {
|
||||
return sFolderMap.containsKey(folder);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExchangeFolder extends Folder {
|
||||
|
||||
private final ExchangeTransport mTransport;
|
||||
@SuppressWarnings("unused")
|
||||
private final ExchangeStore mStore;
|
||||
@SuppressWarnings("unused")
|
||||
private final String mName;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private PersistentDataCallbacks mPersistenceCallbacks;
|
||||
|
||||
public ExchangeFolder(ExchangeStore store, String name)
|
||||
throws MessagingException {
|
||||
mStore = store;
|
||||
mTransport = store.getTransport();
|
||||
mName = name;
|
||||
if (!mTransport.isFolderAvailable(name)) {
|
||||
throw new MessagingException("folder not supported: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendMessages(Message[] messages) throws MessagingException {
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean expunge) throws MessagingException {
|
||||
mPersistenceCallbacks = null;
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks)
|
||||
throws MessagingException {
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean create(FolderType type) throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(boolean recurse) throws MessagingException {
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] expunge() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getMessage(String uid) throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessageCount() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(int start, int end, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenMode getMode() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flag[] getPermanentFlags() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnreadMessageCount() throws MessagingException {
|
||||
// TODO Implement this function
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
// TODO Implement this function
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(OpenMode mode, PersistentDataCallbacks callbacks)
|
||||
throws MessagingException {
|
||||
mPersistenceCallbacks = callbacks;
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
||||
throws MessagingException {
|
||||
// TODO Implement this function
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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.email.service;
|
||||
|
||||
import com.android.exchange.Eas;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.Constants;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* A very basic authenticator service for EAS. At the moment, it has no UI hooks. When called
|
||||
* with addAccount, it simply adds the account to AccountManager directly with a username and
|
||||
* password. We will need to implement confirmPassword, confirmCredentials, and updateCredentials.
|
||||
*/
|
||||
public class EasAuthenticatorService extends Service {
|
||||
|
||||
class EasAuthenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
@Override
|
||||
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
|
||||
String authTokenType, String[] requiredFeatures, Bundle options)
|
||||
throws NetworkErrorException {
|
||||
// The Bundle we are passed has username and password set
|
||||
AccountManager.get(EasAuthenticatorService.this).blockingAddAccountExplicitly(
|
||||
new Account(options.getString("username"), Eas.ACCOUNT_MANAGER_TYPE),
|
||||
options.getString("password"), null);
|
||||
Bundle b = new Bundle();
|
||||
b.putString(Constants.ACCOUNT_NAME_KEY, options.getString("username"));
|
||||
b.putString(Constants.ACCOUNT_TYPE_KEY, Eas.ACCOUNT_MANAGER_TYPE);
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirmPassword(AccountAuthenticatorResponse response, Account account,
|
||||
String password) throws NetworkErrorException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
|
||||
String authTokenType, Bundle loginOptions) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
|
||||
String[] features) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
|
||||
String authTokenType, Bundle loginOptions) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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())) {
|
||||
return new EasAuthenticator().getIAccountAuthenticator().asBinder();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2008-2009 Marc Blank
|
||||
* Licensed to 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.email.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.exchange.IEmailService;
|
||||
|
||||
/**
|
||||
* Proxy for an IEmailService (remote email service); handles all connections to the service.
|
||||
* All calls via the proxy are synchronous; the UI must ensure that these calls are running
|
||||
* on appropriate background threads.
|
||||
*
|
||||
* A call to loadAttachment, for example, would look like this (assuming MyService is the service)
|
||||
* EmailProxyService.getService(context, MyService.class).loadAttachment(..args..);
|
||||
*/
|
||||
|
||||
public class EmailServiceProxy {
|
||||
|
||||
// Map associating a context and a proxy
|
||||
static HashMap<Context, EmailServiceProxy> sProxyMap =
|
||||
new HashMap<Context, EmailServiceProxy>();
|
||||
|
||||
// Map associating, for a given proxy, a class name (String) and a connected service
|
||||
public HashMap<String, IEmailService> serviceMap =
|
||||
new HashMap<String, IEmailService>();
|
||||
|
||||
public EmailServiceProxy () {
|
||||
}
|
||||
|
||||
class EmailServiceConnection implements ServiceConnection {
|
||||
EmailServiceProxy mProxy;
|
||||
|
||||
EmailServiceConnection (EmailServiceProxy proxy) {
|
||||
mProxy = proxy;
|
||||
}
|
||||
|
||||
void setProxy (EmailServiceProxy proxy) {
|
||||
mProxy = proxy;
|
||||
}
|
||||
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
synchronized (mProxy) {
|
||||
IEmailService service = IEmailService.Stub.asInterface(binder);
|
||||
mProxy.serviceMap.put(name.getClassName(), service);
|
||||
}
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
synchronized (mProxy) {
|
||||
mProxy.serviceMap.remove(name.getClassName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceConnection mSyncManagerConnection = new EmailServiceConnection (this);
|
||||
|
||||
static public IEmailService getService(Context context, Class<? extends Service> klass)
|
||||
throws RemoteException {
|
||||
String className = klass.getName();
|
||||
|
||||
// First, lets get the proxy for this context
|
||||
// Make sure we're synchronized on the map
|
||||
EmailServiceProxy proxy;
|
||||
synchronized (sProxyMap) {
|
||||
proxy = sProxyMap.get(context);
|
||||
if (proxy == null) {
|
||||
proxy = new EmailServiceProxy();
|
||||
sProxyMap.put(context, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
// Once we have the proxy, we need to synchronize working with its map, connect to the
|
||||
// appropriate service (if not already connected) and return that service
|
||||
synchronized (proxy) {
|
||||
if (proxy.serviceMap.get(klass) == null) {
|
||||
context.bindService(new Intent(context, klass), proxy.mSyncManagerConnection,
|
||||
Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait up to 5 seconds for the connection
|
||||
int count = 0;
|
||||
IEmailService service = null;
|
||||
while (count++ < 10) {
|
||||
synchronized (proxy) {
|
||||
service = proxy.serviceMap.get(className);
|
||||
if (service != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (service == null) {
|
||||
throw new RemoteException();
|
||||
}
|
||||
return service;
|
||||
}
|
||||
}
|
|
@ -27,7 +27,9 @@ public class Eas {
|
|||
// For temporary use while debugging
|
||||
public static boolean TEST_DEBUG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
|
||||
|
||||
public static String VERSION = "0.1";
|
||||
public static final String VERSION = "0.1";
|
||||
|
||||
public static final String ACCOUNT_MANAGER_TYPE = "com.android.exchange";
|
||||
|
||||
// From EAS spec
|
||||
// Mail Cal
|
||||
|
|
|
@ -176,7 +176,7 @@ public class EasSyncService extends InteractiveSyncService {
|
|||
|
||||
|
||||
@Override
|
||||
public void loadAttachment(Attachment att, ISyncManagerCallback cb) {
|
||||
public void loadAttachment(Attachment att, IEmailServiceCallback cb) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2008-2009 Marc Blank
|
||||
* Licensed to 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;
|
||||
|
||||
/**
|
||||
* Definitions of service status codes returned to IEmailServiceCallback's status method
|
||||
*/
|
||||
public interface EmailServiceStatus {
|
||||
public static final int SUCCESS = 0;
|
||||
public static final int IN_PROGRESS = 1;
|
||||
|
||||
public static final int MESSAGE_NOT_FOUND = 0x10;
|
||||
public static final int ATTACHMENT_NOT_FOUND = 0x11;
|
||||
public static final int FOLDER_NOT_DELETED = 0x12;
|
||||
public static final int FOLDER_NOT_RENAMED = 0x13;
|
||||
public static final int FOLDER_NOT_CREATED = 0x14;
|
||||
|
||||
// Maybe we should automatically retry these?
|
||||
public static final int CONNECTION_ERROR = 0x20;
|
||||
}
|
|
@ -14,28 +14,25 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.exchange;
|
||||
|
||||
import com.android.exchange.ISyncManagerCallback;
|
||||
import com.android.exchange.IEmailServiceCallback;
|
||||
import com.android.exchange.EmailContent;
|
||||
|
||||
interface ISyncManager {
|
||||
int validate(in String protocol, in String host, in String userName, in String password, int port, boolean ssl) ;
|
||||
|
||||
void registerCallback(ISyncManagerCallback cb);
|
||||
void unregisterCallback(ISyncManagerCallback cb);
|
||||
interface IEmailService {
|
||||
int validate(in String protocol, in String host, in String userName, in String password,
|
||||
int port, boolean ssl) ;
|
||||
|
||||
boolean startSync(long mailboxId);
|
||||
boolean stopSync(long mailboxId);
|
||||
|
||||
boolean updateFolderList(long accountId);
|
||||
boolean loadMore(long messageId, IEmailServiceCallback cb);
|
||||
boolean loadAttachment(long messageId, in EmailContent.Attachment att,
|
||||
IEmailServiceCallback cb);
|
||||
|
||||
boolean loadMore(long messageId, ISyncManagerCallback cb);
|
||||
boolean loadAttachment(long messageId, in EmailContent.Attachment att, ISyncManagerCallback cb);
|
||||
boolean updateFolderList(long accountId);
|
||||
|
||||
boolean createFolder(long accountId, String name);
|
||||
boolean deleteFolder(long accountId, String name);
|
||||
boolean renameFolder(long accountId, String oldName, String newName);
|
||||
//AddressLookup - real-time address lookup (EAS)
|
||||
}
|
|
@ -17,6 +17,6 @@
|
|||
|
||||
package com.android.exchange;
|
||||
|
||||
oneway interface ISyncManagerCallback {
|
||||
void progress(int value);
|
||||
oneway interface IEmailServiceCallback {
|
||||
void status(int statusCode, int progress);
|
||||
}
|
|
@ -46,5 +46,5 @@ public abstract class InteractiveSyncService extends AbstractSyncService {
|
|||
|
||||
public abstract void reloadFolderList();
|
||||
|
||||
public abstract void loadAttachment(Attachment att, ISyncManagerCallback cb);
|
||||
public abstract void loadAttachment(Attachment att, IEmailServiceCallback cb);
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ import android.net.NetworkInfo;
|
|||
import android.net.Uri;
|
||||
import android.net.NetworkInfo.State;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.database.ContentObserver;
|
||||
|
@ -94,33 +94,23 @@ public class SyncManager extends Service implements Runnable {
|
|||
new HashMap<Long, PendingIntent>();
|
||||
static private WakeLock mWakeLock = null;
|
||||
|
||||
final RemoteCallbackList<ISyncManagerCallback> mCallbacks =
|
||||
new RemoteCallbackList<ISyncManagerCallback>();
|
||||
|
||||
private final ISyncManager.Stub mBinder = new ISyncManager.Stub() {
|
||||
/**
|
||||
* Create the binder for EmailService implementation here. These are the calls that are
|
||||
* defined in AbstractSyncService. Only validate is now implemented; loadAttachment currently
|
||||
* spins its wheels counting up to 100%.
|
||||
*/
|
||||
private final IEmailService.Stub mBinder = new IEmailService.Stub() {
|
||||
public int validate(String protocol, String host, String userName, String password,
|
||||
int port, boolean ssl) throws RemoteException {
|
||||
try {
|
||||
AbstractSyncService.validate(EasSyncService.class, host, userName, password, port, ssl,
|
||||
SyncManager.this);
|
||||
AbstractSyncService.validate(EasSyncService.class, host, userName, password, port,
|
||||
ssl, SyncManager.this);
|
||||
return MessagingException.NO_ERROR;
|
||||
} catch (MessagingException e) {
|
||||
return e.getExceptionType();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerCallback(ISyncManagerCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.register(cb);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterCallback(ISyncManagerCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.unregister(cb);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startSync(long mailboxId) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
|
@ -136,7 +126,7 @@ public class SyncManager extends Service implements Runnable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean loadMore(long messageId, ISyncManagerCallback cb) throws RemoteException {
|
||||
public boolean loadMore(long messageId, IEmailServiceCallback cb) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
@ -157,9 +147,17 @@ public class SyncManager extends Service implements Runnable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean loadAttachment(long messageId, Attachment att, ISyncManagerCallback cb)
|
||||
public boolean loadAttachment(long messageId, Attachment att, IEmailServiceCallback cb)
|
||||
throws RemoteException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
cb.status(EmailServiceStatus.IN_PROGRESS, i * 10);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
// TODO Auto-generated method stub
|
||||
cb.status(EmailServiceStatus.SUCCESS, 0);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -237,7 +235,7 @@ public class SyncManager extends Service implements Runnable {
|
|||
main.mDisplayName = "_main";
|
||||
main.mServerId = "_main";
|
||||
main.mAccountKey = acct.mId;
|
||||
main.mType = Mailbox.TYPE_MAIL;
|
||||
main.mType = Mailbox.TYPE_NOT_EMAIL;
|
||||
main.mSyncFrequency = Account.CHECK_INTERVAL_PUSH;
|
||||
main.mFlagVisible = false;
|
||||
main.save(getContext());
|
||||
|
|
|
@ -212,6 +212,7 @@ public class EasFolderSyncParser extends EasParser {
|
|||
m.mDisplayName = name;
|
||||
m.mServerId = serverId;
|
||||
m.mAccountKey = mAccountId;
|
||||
m.mType = Mailbox.TYPE_MAIL;
|
||||
m.mSyncFrequency = Account.CHECK_INTERVAL_NEVER;
|
||||
switch (type) {
|
||||
case INBOX_TYPE:
|
||||
|
|
Loading…
Reference in New Issue