AI 144525: Add structural support for Exchange transport. The idea is that

the shipping client will include the necessary generic pieces for
  configuring an Exchange client (e.g. account setup) but will not
  include actual Exchange client code (e.g. transport / protocol).
  Also added a "sample code" implementation of Exchange for use
  as a starting point for implementors.  (Note, this will not ship
  in Donut, it's a placeholder for working on the "framework"
  aspects.)
  BUG=1740621,1740626

Automated import of CL 144525
This commit is contained in:
Andy Stadler 2009-04-03 16:02:55 -07:00 committed by The Android Open Source Project
parent 2b0c619f1e
commit 87c43cada6
12 changed files with 960 additions and 1 deletions

View File

@ -58,6 +58,11 @@
android:label="@string/account_setup_outgoing_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupExchange"
android:label="@string/account_setup_exchange_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupOptions"
android:label="@string/account_setup_options_title"

View File

@ -42,4 +42,13 @@
android:minWidth="@dimen/button_minWidth"
android:layout_gravity="center_horizontal"
/>
<Button
android:id="@+id/exchange"
android:text="@string/account_setup_account_type_exchange_action"
android:layout_height="wrap_content"
android:layout_width="150sp"
android:minWidth="@dimen/button_minWidth"
android:layout_gravity="center_horizontal"
android:visibility="gone"
/>
</LinearLayout>

View File

@ -0,0 +1,102 @@
<?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.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbarStyle="outsideInset">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:text="@string/account_setup_incoming_username_label"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary" />
<EditText
android:id="@+id/account_username"
android:inputType="textEmailAddress"
android:imeOptions="actionDone"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<TextView
android:text="@string/account_setup_incoming_password_label"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary" />
<EditText
android:id="@+id/account_password"
android:inputType="textPassword"
android:imeOptions="actionDone"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<!-- This text may be changed in code if the server is IMAP, etc. -->
<TextView
android:text="@string/account_setup_exchange_server_label"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary" />
<!-- Note: we use inputType=textUri as the closest approximation to a server name -->
<EditText
android:id="@+id/account_server"
android:inputType="textUri"
android:imeOptions="actionDone"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<TextView
android:text="@string/account_setup_exchange_domain_label"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary" />
<EditText
android:id="@+id/account_domain"
android:hint="@string/account_setup_exchange_domain_hint"
inputType="text"
android:imeOptions="actionDone"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<CheckBox
android:id="@+id/account_ssl"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="@string/account_setup_exchange_ssl_label" />
<View
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="54dip"
android:background="@android:drawable/bottom_bar">
<Button
android:id="@+id/next"
android:text="@string/next_action"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="@dimen/button_minWidth"
android:drawableRight="@drawable/button_indicator_next"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -238,6 +238,8 @@
<string name="account_setup_account_type_pop_action">POP3 account</string>
<!-- "Add new email account" screen, button name in response to what type of account this is -->
<string name="account_setup_account_type_imap_action">IMAP account</string>
<!-- "Add new email account" screen, button name in response to what type of account this is -->
<string name="account_setup_account_type_exchange_action">Exchange/ActiveSync</string>
<!-- "Incoming server settings" screen, label for text field -->
<string name="account_setup_incoming_title">Incoming server settings</string>
<!-- "Incoming server settings" screen, label for text field -->
@ -290,6 +292,17 @@
<string name="account_setup_outgoing_username_label">Username</string>
<!-- On "Outgoing server settings" screen, label for text field -->
<string name="account_setup_outgoing_password_label">Password</string>
<!-- Title of "Exchange server settings" screen -->
<string name="account_setup_exchange_title">Exchange server settings</string>
<!-- On "Exchange" setup screen, the name of the server -->
<string name="account_setup_exchange_server_label">Exchange Server</string>
<!-- On "Exchange" setup screen, the domain name label -->
<string name="account_setup_exchange_domain_label">Domain:</string>
<!-- On "Exchange" setup screen, the domain name hint -->
<string name="account_setup_exchange_domain_hint">Enter domain here</string>
<!-- On "Exchange" setup screen, the use-SSL checkbox label -->
<string name="account_setup_exchange_ssl_label">Use secure connection (SSL)</string>
<!-- In Account setup options screen, Activity title -->
<string name="account_setup_options_title">Account options</string>

View File

@ -31,4 +31,7 @@
<senders>
<sender scheme="smtp" class="com.android.email.mail.transport.SmtpSender" />
<!-- This is here for temporary demo purposes only. Do not ship with this. -->
<!-- sender scheme="eas" class="com.android.email.mail.exchange.ExchangeSenderExample" / -->
</senders>

View File

@ -33,4 +33,7 @@
<store scheme="local" class="com.android.email.mail.store.LocalStore" />
<store scheme="pop3" class="com.android.email.mail.store.Pop3Store" />
<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" / -->
</stores>

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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:
* <sender scheme="eas" class="com.android.email.mail.exchange.ExchangeSenderExample" />
* 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;
}
}

View File

@ -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:
* <store scheme="eas" class="com.android.email.mail.exchange.ExchangeStoreExample" />
* to res/xml/stores.xml
*/
public class ExchangeStoreExample extends Store {
private final Context mContext;
private URI mUri;
private final ExchangeTransportExample mTransport;
private final HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
/**
* 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;
}
}

View File

@ -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<String, ExchangeTransportExample> sUriToInstanceMap =
new HashMap<String, ExchangeTransportExample>();
private static final HashMap<String, Integer> sFolderMap = new HashMap<String, Integer>();
/**
* 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);
}
}