diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 025d49501..b1fc4fdf1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -58,6 +58,11 @@
android:label="@string/account_setup_outgoing_title"
>
+
+
+
diff --git a/res/layout/account_setup_exchange.xml b/res/layout/account_setup_exchange.xml
new file mode 100644
index 000000000..2a6c65124
--- /dev/null
+++ b/res/layout/account_setup_exchange.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 509fb6d1d..47dffdc6e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -238,6 +238,8 @@
POP3 account
IMAP account
+
+ Exchange/ActiveSync
Incoming server settings
@@ -290,6 +292,17 @@
Username
Password
+
+
+ Exchange server settings
+
+ Exchange Server
+
+ Domain:
+
+ Enter domain here
+
+ Use secure connection (SSL)
Account options
diff --git a/res/xml/senders.xml b/res/xml/senders.xml
index 138bcd8a6..3a1aff8b9 100644
--- a/res/xml/senders.xml
+++ b/res/xml/senders.xml
@@ -31,4 +31,7 @@
+
+
+
diff --git a/res/xml/stores.xml b/res/xml/stores.xml
index d0f2d8ce1..efe4ac227 100644
--- a/res/xml/stores.xml
+++ b/res/xml/stores.xml
@@ -33,4 +33,7 @@
+
+
+
diff --git a/src/com/android/email/activity/setup/AccountSetupAccountType.java b/src/com/android/email/activity/setup/AccountSetupAccountType.java
index f62e42061..021175463 100644
--- a/src/com/android/email/activity/setup/AccountSetupAccountType.java
+++ b/src/com/android/email/activity/setup/AccountSetupAccountType.java
@@ -18,6 +18,8 @@ package com.android.email.activity.setup;
import com.android.email.Account;
import com.android.email.R;
+import com.android.email.mail.MessagingException;
+import com.android.email.mail.Store;
import android.app.Activity;
import android.content.Intent;
@@ -57,9 +59,15 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
setContentView(R.layout.account_setup_account_type);
((Button)findViewById(R.id.pop)).setOnClickListener(this);
((Button)findViewById(R.id.imap)).setOnClickListener(this);
-
+ ((Button)findViewById(R.id.exchange)).setOnClickListener(this);
+
mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
mMakeDefault = (boolean)getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
+
+ if (isExchangeAvailable()) {
+ findViewById(R.id.exchange).setVisibility(View.VISIBLE);
+ }
+ // TODO: Dynamic creation of buttons, instead of just hiding things we don't need
}
private void onPop() {
@@ -98,6 +106,47 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
AccountSetupIncoming.actionIncomingSettings(this, mAccount, mMakeDefault);
finish();
}
+
+ /**
+ * The user has selected an exchange account type. Try to put together a URI using the entered
+ * email address. Also set the mail delete policy here, because there is no UI (for exchange).
+ */
+ private void onExchange() {
+ try {
+ URI uri = new URI(mAccount.getStoreUri());
+ uri = new URI("eas", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
+ mAccount.setStoreUri(uri.toString());
+ mAccount.setSenderUri(uri.toString());
+ } catch (URISyntaxException use) {
+ /*
+ * This should not happen.
+ */
+ throw new Error(use);
+ }
+ // TODO: Confirm correct delete policy for exchange
+ mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
+ AccountSetupExchange.actionIncomingSettings(this, mAccount, mMakeDefault);
+ finish();
+ }
+
+ /**
+ * Determine if we can show the "exchange" option
+ *
+ * TODO: This should be dynamic and data-driven for all account types, not just hardcoded
+ * like this.
+ */
+ private boolean isExchangeAvailable() {
+ try {
+ URI uri = new URI(mAccount.getStoreUri());
+ uri = new URI("eas", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
+ Store store = Store.getInstance(uri.toString(), this);
+ return (store != null);
+ } catch (URISyntaxException e) {
+ return false;
+ } catch (MessagingException e) {
+ return false;
+ }
+ }
public void onClick(View v) {
switch (v.getId()) {
@@ -107,6 +156,9 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
case R.id.imap:
onImap();
break;
+ case R.id.exchange:
+ onExchange();
+ break;
}
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupExchange.java b/src/com/android/email/activity/setup/AccountSetupExchange.java
new file mode 100644
index 000000000..13671215d
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupExchange.java
@@ -0,0 +1,268 @@
+/*
+ * 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.activity.setup;
+
+import com.android.email.Account;
+import com.android.email.Preferences;
+import com.android.email.R;
+import com.android.email.Utility;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Provides generic setup for Exchange accounts. The following fields are supported:
+ *
+ * Email Address (from previous setup screen)
+ * Server
+ * Domain
+ * Requires SSL?
+ * User (login)
+ * Password
+ */
+public class AccountSetupExchange extends Activity implements OnClickListener {
+ private static final String EXTRA_ACCOUNT = "account";
+ private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
+
+ private EditText mUsernameView;
+ private EditText mPasswordView;
+ private EditText mServerView;
+ private EditText mDomainView;
+ private CheckBox mSslSecurityView;
+
+ private Button mNextButton;
+ private Account mAccount;
+ private boolean mMakeDefault;
+
+ public static void actionIncomingSettings(Activity fromActivity, Account account,
+ boolean makeDefault) {
+ Intent i = new Intent(fromActivity, AccountSetupExchange.class);
+ i.putExtra(EXTRA_ACCOUNT, account);
+ i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault);
+ fromActivity.startActivity(i);
+ }
+
+ public static void actionEditIncomingSettings(Activity fromActivity, Account account) {
+ Intent i = new Intent(fromActivity, AccountSetupExchange.class);
+ i.setAction(Intent.ACTION_EDIT);
+ i.putExtra(EXTRA_ACCOUNT, account);
+ fromActivity.startActivity(i);
+ }
+
+ /**
+ * For now, we'll simply replicate outgoing, for the purpose of satisfying the
+ * account settings flow.
+ */
+ public static void actionEditOutgoingSettings(Activity fromActivity, Account account) {
+ Intent i = new Intent(fromActivity, AccountSetupExchange.class);
+ i.setAction(Intent.ACTION_EDIT);
+ i.putExtra(EXTRA_ACCOUNT, account);
+ fromActivity.startActivity(i);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.account_setup_exchange);
+
+ mUsernameView = (EditText) findViewById(R.id.account_username);
+ mPasswordView = (EditText) findViewById(R.id.account_password);
+ mServerView = (EditText) findViewById(R.id.account_server);
+ mDomainView = (EditText) findViewById(R.id.account_domain);
+ mSslSecurityView = (CheckBox) findViewById(R.id.account_ssl);
+
+ mNextButton = (Button)findViewById(R.id.next);
+ mNextButton.setOnClickListener(this);
+
+ /*
+ * Calls validateFields() which enables or disables the Next button
+ * based on the fields' validity.
+ */
+ TextWatcher validationTextWatcher = new TextWatcher() {
+ public void afterTextChanged(Editable s) {
+ validateFields();
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+ };
+ mUsernameView.addTextChangedListener(validationTextWatcher);
+ mPasswordView.addTextChangedListener(validationTextWatcher);
+ mServerView.addTextChangedListener(validationTextWatcher);
+ mDomainView.addTextChangedListener(validationTextWatcher);
+
+ mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
+ mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
+
+ /*
+ * If we're being reloaded we override the original account with the one
+ * we saved
+ */
+ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
+ mAccount = (Account)savedInstanceState.getSerializable(EXTRA_ACCOUNT);
+ }
+
+ try {
+ URI uri = new URI(mAccount.getStoreUri());
+ String username = null;
+ String password = null;
+ if (uri.getUserInfo() != null) {
+ String[] userInfoParts = uri.getUserInfo().split(":", 2);
+ username = userInfoParts[0];
+ if (userInfoParts.length > 1) {
+ password = userInfoParts[1];
+ }
+ }
+
+ if (username != null) {
+ mUsernameView.setText(username);
+ }
+
+ if (password != null) {
+ mPasswordView.setText(password);
+ }
+
+ if (uri.getScheme().startsWith("eas")) {
+ // any other setup from mAccount can go here
+ } else {
+ throw new Error("Unknown account type: " + mAccount.getStoreUri());
+ }
+
+ if (uri.getHost() != null) {
+ mServerView.setText(uri.getHost());
+ }
+
+ String domain = uri.getPath();
+ if (!TextUtils.isEmpty(domain)) {
+ mDomainView.setText(domain.substring(1));
+ }
+
+ mSslSecurityView.setChecked(uri.getScheme().contains("ssl"));
+
+ } catch (URISyntaxException use) {
+ /*
+ * We should always be able to parse our own settings.
+ */
+ throw new Error(use);
+ }
+
+ validateFields();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(EXTRA_ACCOUNT, mAccount);
+ }
+
+ /**
+ * Check the values in the fields and decide if it makes sense to enable the "next" button
+ * NOTE: Does it make sense to extract & combine with similar code in AccountSetupIncoming?
+ */
+ private void validateFields() {
+ boolean enabled = Utility.requiredFieldValid(mUsernameView)
+ && Utility.requiredFieldValid(mPasswordView)
+ && Utility.requiredFieldValid(mServerView)
+ && Utility.requiredFieldValid(mDomainView);
+ if (enabled) {
+ try {
+ URI uri = getUri();
+ } catch (URISyntaxException use) {
+ enabled = false;
+ }
+ }
+ mNextButton.setEnabled(enabled);
+ Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == RESULT_OK) {
+ if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
+ mAccount.save(Preferences.getPreferences(this));
+ finish();
+ } else {
+ // Go directly to end - there is no 2nd screen for incoming settings
+ AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault);
+ finish();
+ }
+ }
+ }
+
+ /**
+ * Attempt to create a URI from the fields provided. Throws URISyntaxException if there's
+ * a problem with the user input.
+ * @return a URI built from the account setup fields
+ */
+ private URI getUri() throws URISyntaxException {
+ boolean sslRequired = mSslSecurityView.isChecked();
+ String scheme = sslRequired ? "eas+ssl+" : "eas";
+ String userInfo = mUsernameView.getText().toString().trim() + ":" +
+ mPasswordView.getText().toString().trim();
+ String host = mServerView.getText().toString().trim();
+ String path = "/" + mDomainView.getText().toString().trim();
+
+ URI uri = new URI(
+ scheme,
+ userInfo,
+ host,
+ 0,
+ path,
+ null,
+ null);
+
+ return uri;
+ }
+
+ private void onNext() {
+ try {
+ URI uri = getUri();
+ mAccount.setStoreUri(uri.toString());
+ } catch (URISyntaxException use) {
+ /*
+ * It's unrecoverable if we cannot create a URI from components that
+ * we validated to be safe.
+ */
+ throw new Error(use);
+ }
+
+ AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
+ }
+
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.next:
+ onNext();
+ break;
+ }
+ }
+}
diff --git a/src/com/android/email/mail/exchange/ExchangeFolderExample.java b/src/com/android/email/mail/exchange/ExchangeFolderExample.java
new file mode 100644
index 000000000..77b3e1761
--- /dev/null
+++ b/src/com/android/email/mail/exchange/ExchangeFolderExample.java
@@ -0,0 +1,163 @@
+/*
+ * 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.exchange;
+
+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;
+
+/**
+ * Sample code for implementing a new server folder. See also ExchangeStoreExample,
+ * ExchangeSenderExample, and ExchangeTransportExample.
+ */
+public class ExchangeFolderExample extends Folder {
+
+ private final ExchangeTransportExample mTransport;
+ private final ExchangeStoreExample mStore;
+ private final String mName;
+
+ public ExchangeFolderExample(ExchangeStoreExample 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 {
+ // TODO Implement this function
+ }
+
+ @Override
+ public void copyMessages(Message[] msgs, Folder folder) 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) throws MessagingException {
+ // TODO Implement this function
+ }
+
+ @Override
+ public void setFlags(Message[] messages, Flag[] flags, boolean value) throws MessagingException {
+ // TODO Implement this function
+ }
+
+
+}
diff --git a/src/com/android/email/mail/exchange/ExchangeSenderExample.java b/src/com/android/email/mail/exchange/ExchangeSenderExample.java
new file mode 100644
index 000000000..0ac12061d
--- /dev/null
+++ b/src/com/android/email/mail/exchange/ExchangeSenderExample.java
@@ -0,0 +1,97 @@
+/*
+ * 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.exchange;
+
+import com.android.email.mail.Message;
+import com.android.email.mail.MessagingException;
+import com.android.email.mail.Sender;
+
+import android.content.Context;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Sample code for implementing a new Sender. See also ExchangeSenderExample,
+ * ExchangeFolderExample, and ExchangeTransportExample.
+ *
+ * To enable this placeholder, please add:
+ *
+ * to res/xml/senders.xml
+ */
+public class ExchangeSenderExample extends Sender {
+
+ private final Context mContext;
+ private final ExchangeTransportExample mTransport;
+
+ /**
+ * Factory method.
+ */
+ public static Sender newInstance(String uri, Context context) throws MessagingException {
+ return new ExchangeSenderExample(uri, context);
+ }
+
+ private ExchangeSenderExample(String _uri, Context context) throws MessagingException {
+ mContext = context;
+
+ URI uri = null;
+ try {
+ uri = new URI(_uri);
+ } catch (URISyntaxException e) {
+ throw new MessagingException("Invalid uri for ExchangeSenderExample");
+ }
+
+ String scheme = uri.getScheme();
+ int connectionSecurity;
+ if (scheme.equals("eas")) {
+ connectionSecurity = ExchangeTransportExample.CONNECTION_SECURITY_NONE;
+ } else if (scheme.equals("eas+ssl+")) {
+ connectionSecurity = ExchangeTransportExample.CONNECTION_SECURITY_SSL_REQUIRED;
+ } else {
+ throw new MessagingException("Unsupported protocol");
+ }
+
+ mTransport = ExchangeTransportExample.getInstance(uri, context);
+ }
+
+ @Override
+ public void close() throws MessagingException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void open() throws MessagingException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void sendMessage(Message message) throws MessagingException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Get class of SettingActivity for this Sender class.
+ * @return Activity class that has class method actionEditOutgoingSettings().
+ */
+ @Override
+ public Class extends android.app.Activity> getSettingActivityClass() {
+ return com.android.email.activity.setup.AccountSetupExchange.class;
+ }
+}
diff --git a/src/com/android/email/mail/exchange/ExchangeStoreExample.java b/src/com/android/email/mail/exchange/ExchangeStoreExample.java
new file mode 100644
index 000000000..d70ca0658
--- /dev/null
+++ b/src/com/android/email/mail/exchange/ExchangeStoreExample.java
@@ -0,0 +1,123 @@
+/*
+ * 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.exchange;
+
+import com.android.email.mail.Folder;
+import com.android.email.mail.MessagingException;
+import com.android.email.mail.Store;
+
+import android.content.Context;
+
+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.
+ *
+ * To enable this placeholder, please add:
+ *
+ * to res/xml/stores.xml
+ */
+public class ExchangeStoreExample extends Store {
+
+ private final Context mContext;
+ private URI mUri;
+
+ private final ExchangeTransportExample mTransport;
+ private final HashMap mFolders = new HashMap();
+
+ /**
+ * Factory method.
+ */
+ public static Store newInstance(String uri, Context context)
+ throws MessagingException {
+ return new ExchangeStoreExample(uri, context);
+ }
+
+ /**
+ * eas://user:password@server/domain
+ *
+ * @param _uri
+ * @param application
+ * @throws MessagingException
+ */
+ private ExchangeStoreExample(String _uri, Context context) throws MessagingException {
+ mContext = context;
+ try {
+ mUri = new URI(_uri);
+ } catch (URISyntaxException e) {
+ throw new MessagingException("Invalid uri for ExchangeStoreExample");
+ }
+
+ String scheme = mUri.getScheme();
+ int connectionSecurity;
+ if (scheme.equals("eas")) {
+ connectionSecurity = ExchangeTransportExample.CONNECTION_SECURITY_NONE;
+ } else if (scheme.equals("eas+ssl+")) {
+ connectionSecurity = ExchangeTransportExample.CONNECTION_SECURITY_SSL_REQUIRED;
+ } else {
+ throw new MessagingException("Unsupported protocol");
+ }
+
+ mTransport = ExchangeTransportExample.getInstance(mUri, context);
+ }
+
+ /**
+ * Retrieve the underlying transport. Used primarily for testing.
+ * @return
+ */
+ /* package */ ExchangeTransportExample 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 ExchangeFolderExample(this, name);
+ mFolders.put(folder.getName(), folder);
+ }
+ return folder;
+ }
+ }
+
+ @Override
+ public Folder[] getPersonalNamespaces() throws MessagingException {
+ return new Folder[] {
+ getFolder(ExchangeTransportExample.FOLDER_INBOX),
+ };
+ }
+
+ /**
+ * 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;
+ }
+}
+
diff --git a/src/com/android/email/mail/exchange/ExchangeTransportExample.java b/src/com/android/email/mail/exchange/ExchangeTransportExample.java
new file mode 100644
index 000000000..de89c5d39
--- /dev/null
+++ b/src/com/android/email/mail/exchange/ExchangeTransportExample.java
@@ -0,0 +1,121 @@
+/*
+ * 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.exchange;
+
+import com.android.email.Email;
+import com.android.email.mail.MessagingException;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.net.URI;
+import java.util.HashMap;
+
+/**
+ * Sample code for implementing a new server transport. See also ExchangeStoreExample,
+ * ExchangeFolderExample, and ExchangeSenderExample.
+ */
+public class ExchangeTransportExample {
+ 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 final Context mContext;
+
+ private String mHost;
+ private String mDomain;
+ private String mUsername;
+ private String mPassword;
+
+ private static HashMap sUriToInstanceMap =
+ new HashMap();
+ private static final HashMap sFolderMap = new HashMap();
+
+
+ /**
+ * Public factory. The transport should be a singleton (per Uri)
+ */
+ public synchronized static ExchangeTransportExample 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();
+ ExchangeTransportExample transport = sUriToInstanceMap.get(key);
+ if (transport == null) {
+ transport = new ExchangeTransportExample(uri, context);
+ sUriToInstanceMap.put(key, transport);
+ }
+ return transport;
+ }
+
+ /**
+ * Private constructor - use public factory.
+ */
+ private ExchangeTransportExample(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
+ */
+ public void checkSettings(URI uri) throws MessagingException {
+ setUri(uri);
+ // Perform a server connection here
+ // Throw MessageException if not useable
+ }
+
+ /**
+ * Typical helper function: Return existence of a given folder
+ */
+ public boolean isFolderAvailable(final String folder) {
+ return sFolderMap.containsKey(folder);
+ }
+}
\ No newline at end of file