diff --git a/AndroidManifest.xml b/AndroidManifest.xml index dc5437b27..1f7f8ab12 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -206,6 +206,10 @@ + + diff --git a/res/layout/message_view.xml b/res/layout/message_view.xml index 3e236de66..acdd12be5 100644 --- a/res/layout/message_view.xml +++ b/res/layout/message_view.xml @@ -29,6 +29,7 @@ /> This class has very limited feature set compared to {@link MessageView}, that is: + *
    + *
  • No action buttons (can't reply, forward or delete) + *
  • No favorite starring. + *
  • No navigating around (no older/newer buttons) + *
+ * + * TODO Test it! + */ +public class MessageFileView extends MessageViewBase { + /** + * URI to the email (i.e. *.eml files, and possibly *.msg files) file that's being + */ + private Uri mFileEmailUri; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Intent intent = getIntent(); + mFileEmailUri = intent.getData(); + + // TODO set title here: "Viewing XXX.eml". + + // Hide all bottom buttons. + findViewById(R.id.button_panel).setVisibility(View.GONE); + findViewById(R.id.favorite).setVisibility(View.GONE); + } + + @Override + public void onResume() { + super.onResume(); + // Note: We don't have to close it even if an account has been deleted, unlike MessageView. + getFragment().openMessage(mFileEmailUri); + } + + /** @return always -1, as there's no account associated with EML files. */ + @Override + protected long getAccountId() { + return -1; + } + + // Note EML files can have ICS (calendar invitation) files, but we don't treat them as + // invitations at this point. (Only exchange provider sets the FLAG_INCOMING_MEETING_INVITE + // flag.) At any rate, it'd be weird to respond to an invitation in an EML that might not + // be addressed to you... + + // TODO Remove these callbacks below, when breaking down the fragment. + // MessageViewFragment for email files shouldn't have these callbacks. + + @Override + public void onRespondedToInvite(int response) { + // EML files shouldn't have calender response buttons. + throw new RuntimeException(); + } + + @Override + public void onCalendarLinkClicked(long epochEventStartTime) { + // EML files shouldn't have the "View in calender" button. + throw new RuntimeException(); + } + + @Override + public void onMessageSetUnread() { + // EML files shouldn't have the "mark unread" button. + throw new RuntimeException(); + } +} diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index 215903c0f..40b2b77de 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,27 @@ package com.android.email.activity; -import com.android.email.Controller; import com.android.email.Email; import com.android.email.R; import com.android.email.Utility; -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.provider.Browser; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; -// TODO Spin-off a new activity for EML files. - -public class MessageView extends Activity implements OnClickListener, MessageOrderManager.Callback, - MessageViewFragment.Callback { +/** + * Activity to show (non-EML) email messages. + * + * This activity shows regular email messages, which are not file-based. (i.e. not *.eml or *.msg) + * + * TODO Test it! + */ +public class MessageView extends MessageViewBase implements View.OnClickListener, + MessageOrderManager.Callback { private static final String EXTRA_MESSAGE_ID = "com.android.email.MessageView_message_id"; private static final String EXTRA_MAILBOX_ID = "com.android.email.MessageView_mailbox_id"; /* package */ static final String EXTRA_DISABLE_REPLY = @@ -46,28 +45,11 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd // for saveInstanceState() private static final String STATE_MESSAGE_ID = "messageId"; - private ProgressDialog mFetchAttachmentProgressDialog; - private MessageViewFragment mMessageViewFragment; - - /** - * If set, URI to the email (i.e. *.eml files, and possibly *.msg files) file that's being - * viewed. - * - * Use {@link #isViewingEmailFile()} to see if the activity is created for opening an EML file. - * - * TODO: We probably should split it into two different MessageViews, one for regular messages - * and the other for for EML files (these two will share the same base MessageView class) to - * eliminate the bunch of 'if {@link #isViewingEmailFile()}'s. - * Do this after making it into a fragment. - */ - private Uri mFileEmailUri; private long mMessageId; private long mMailboxId; private MessageOrderManager mOrderManager; - private Controller mController; - private View mMoveToNewer; private View mMoveToOlder; @@ -100,10 +82,6 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - setContentView(R.layout.message_view); - - mMessageViewFragment = (MessageViewFragment) findFragmentById(R.id.message_view_fragment); - mMessageViewFragment.setCallback(this); mMoveToNewer = findViewById(R.id.moveToNewer); mMoveToOlder = findViewById(R.id.moveToOlder); @@ -114,41 +92,21 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd findViewById(R.id.reply_all).setOnClickListener(this); findViewById(R.id.delete).setOnClickListener(this); - // TODO Turn it into a "managed" dialog? - // Managed dialogs survive activity re-creation. (e.g. orientation change) - mFetchAttachmentProgressDialog = new ProgressDialog(this); - mFetchAttachmentProgressDialog.setIndeterminate(true); - mFetchAttachmentProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - initFromIntent(); if (icicle != null) { mMessageId = icicle.getLong(STATE_MESSAGE_ID, mMessageId); } - - mController = Controller.getInstance(getApplication()); } - /* package */ void initFromIntent() { + private void initFromIntent() { Intent intent = getIntent(); - mFileEmailUri = intent.getData(); - if (mFileEmailUri == null) { - mMessageId = intent.getLongExtra(EXTRA_MESSAGE_ID, -1); - mMailboxId = intent.getLongExtra(EXTRA_MAILBOX_ID, -1); - } else { - mMessageId = -1; - mMailboxId = -1; - } - mDisableReplyAndForward = intent.getBooleanExtra(EXTRA_DISABLE_REPLY, false) - || isViewingEmailFile(); + mMessageId = intent.getLongExtra(EXTRA_MESSAGE_ID, -1); + mMailboxId = intent.getLongExtra(EXTRA_MAILBOX_ID, -1); + mDisableReplyAndForward = intent.getBooleanExtra(EXTRA_DISABLE_REPLY, false); if (mDisableReplyAndForward) { findViewById(R.id.reply).setEnabled(false); findViewById(R.id.reply_all).setEnabled(false); } - if (isViewingEmailFile()) { - // TODO set title here: "Viewing XXX.eml". - findViewById(R.id.delete).setEnabled(false); - findViewById(R.id.favorite).setVisibility(View.GONE); - } } @Override @@ -163,15 +121,13 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd public void onResume() { super.onResume(); - if (!isViewingEmailFile()) { - // Exit immediately if the accounts list has changed (e.g. externally deleted) - if (Email.getNotifyUiAccountsChanged()) { - Welcome.actionStart(this); - finish(); - return; - } - mOrderManager = new MessageOrderManager(this, mMailboxId, this); + // Exit immediately if the accounts list has changed (e.g. externally deleted) + if (Email.getNotifyUiAccountsChanged()) { + Welcome.actionStart(this); + finish(); + return; } + mOrderManager = new MessageOrderManager(this, mMailboxId, this); messageChanged(); } @@ -184,94 +140,32 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd super.onPause(); } - /** - * We override onDestroy to make sure that the WebView gets explicitly destroyed. - * Otherwise it can leak native references. - */ @Override - public void onDestroy() { - super.onDestroy(); - } - - // TODO Make EML specific activity, and this will be gone. - /** - * @return true if viewing an email file. (i.e. *.eml files) - */ - private boolean isViewingEmailFile() { - return mFileEmailUri != null; - } - - /** - * {@inheritDoc} - * - * This is intended to mirror the operation of the original - * (see android.webkit.CallbackProxy) with one addition of intent flags - * "FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET". This improves behavior when sublaunching - * other apps via embedded URI's. - * - * We also use this hook to catch "mailto:" links and handle them locally. - */ - @Override - public boolean onUrlInMessageClicked(String url) { - // hijack mailto: uri's and handle locally - if (url != null && url.toLowerCase().startsWith("mailto:")) { - // If it's showing an EML file, there might be no accounts, but then MessageCompose - // should close itself. - long senderAccountId = isViewingEmailFile() ? -1 : mMessageViewFragment.getAccountId(); - - // TODO if MessageCompose implements the account selector, we'll be able to just pass -1 - // as the account id. - return MessageCompose.actionCompose(MessageView.this, url, senderAccountId); - } - - // Handle most uri's via intent launch - boolean result = false; - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - intent.addCategory(Intent.CATEGORY_BROWSABLE); - intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName()); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - try { - startActivity(intent); - result = true; - } catch (ActivityNotFoundException ex) { - // No applications can handle it. Ignore. - } - return result; + protected long getAccountId() { + return getFragment().getAccountId(); } private void onReply() { - if (isViewingEmailFile()) { - return; - } MessageCompose.actionReply(this, mMessageId, false); finish(); } private void onReplyAll() { - if (isViewingEmailFile()) { - return; - } MessageCompose.actionReply(this, mMessageId, true); finish(); } private void onForward() { - if (isViewingEmailFile()) { - return; - } MessageCompose.actionForward(this, mMessageId); finish(); } private void onDeleteMessage() { - if (isViewingEmailFile()) { - return; - } // the delete triggers mCursorObserver in MessageOrderManager. // first move to older/newer before the actual delete long messageIdToDelete = mMessageId; boolean moved = moveToOlder() || moveToNewer(); - mController.deleteMessage(messageIdToDelete, -1); + getController().deleteMessage(messageIdToDelete, -1); Utility.showToast(this, getResources().getQuantityString(R.plurals.message_deleted_toast, 1)); if (!moved) { @@ -283,9 +177,6 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd } private boolean moveToOlder() { - if (isViewingEmailFile()) { - return false; - } if (mOrderManager != null && mOrderManager.moveToOlder()) { mMessageId = mOrderManager.getCurrentMessageId(); messageChanged(); @@ -295,9 +186,6 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd } private boolean moveToNewer() { - if (isViewingEmailFile()) { - return false; - } if (mOrderManager != null && mOrderManager.moveToNewer()) { mMessageId = mOrderManager.getCurrentMessageId(); messageChanged(); @@ -357,7 +245,7 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd onForward(); break; case R.id.mark_as_unread: - mMessageViewFragment.onMarkMessageAsRead(false); + getFragment().onMarkMessageAsRead(false); break; default: return false; @@ -373,9 +261,6 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - if (isViewingEmailFile()) { - return false; // No menu options for EML files. - } getMenuInflater().inflate(R.menu.message_view_option, menu); if (mDisableReplyAndForward) { menu.findItem(R.id.forward).setEnabled(false); @@ -385,17 +270,15 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd return true; } - // TODO This method name and logic are both not too great. Make it cleaner. - // - This method wouldn't be needed for the activity for EML. - // - Change it to something like openMessage(long messageId). + /** + * Sync the current message. + * - Set message id to the fragment and the message order manager. + * - Update the navigation arrows. + */ private void messageChanged() { - if (mFileEmailUri != null) { - mMessageViewFragment.openMessage(mFileEmailUri); - } else { - mMessageViewFragment.openMessage(mMessageId); - if (mOrderManager != null) { - mOrderManager.moveTo(mMessageId); - } + getFragment().openMessage(mMessageId); + if (mOrderManager != null) { + mOrderManager.moveTo(mMessageId); } updateNavigationArrows(); } @@ -422,46 +305,6 @@ public class MessageView extends Activity implements OnClickListener, MessageOrd updateNavigationArrows(); } - @Override - public void onLoadMessageStarted() { - setProgressBarIndeterminateVisibility(true); - } - - @Override - public void onLoadMessageFinished() { - setProgressBarIndeterminateVisibility(false); - } - - @Override - public void onLoadMessageError() { - onLoadMessageFinished(); - } - - @Override - public void onFetchAttachmentStarted(String attachmentName) { - mFetchAttachmentProgressDialog.setMessage( - getString(R.string.message_view_fetching_attachment_progress, - attachmentName)); - mFetchAttachmentProgressDialog.show(); - setProgressBarIndeterminateVisibility(true); - } - - @Override - public void onFetchAttachmentFinished() { - mFetchAttachmentProgressDialog.dismiss(); - setProgressBarIndeterminateVisibility(false); - } - - @Override - public void onFetchAttachmentError() { - onFetchAttachmentFinished(); - } - - @Override - public void onMessageNotExists() { // Probably meessage deleted. - finish(); - } - @Override public void onRespondedToInvite(int response) { if (!moveToOlder()) { diff --git a/src/com/android/email/activity/MessageViewBase.java b/src/com/android/email/activity/MessageViewBase.java new file mode 100644 index 000000000..120db8d4b --- /dev/null +++ b/src/com/android/email/activity/MessageViewBase.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.email.activity; + +import com.android.email.Controller; +import com.android.email.R; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Browser; + +/** + * Base class for {@link MessageView} and {@link MessageFileView}. + */ +public abstract class MessageViewBase extends Activity implements MessageViewFragment.Callback { + private ProgressDialog mFetchAttachmentProgressDialog; + private MessageViewFragment mMessageViewFragment; + private Controller mController; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.message_view); + + mMessageViewFragment = (MessageViewFragment) findFragmentById(R.id.message_view_fragment); + mMessageViewFragment.setCallback(this); + + // TODO Turn it into a "managed" dialog? + // Managed dialogs survive activity re-creation. (e.g. orientation change) + mFetchAttachmentProgressDialog = new ProgressDialog(this); + mFetchAttachmentProgressDialog.setIndeterminate(true); + mFetchAttachmentProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + + mController = Controller.getInstance(getApplication()); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + protected Controller getController() { + return mController; + } + + protected MessageViewFragment getFragment() { + return mMessageViewFragment; + } + + /** + * @return the account id for the current message, or -1 if there's no account associated with. + * (i.e. when opening an EML file.) + */ + protected abstract long getAccountId(); + + /** + * {@inheritDoc} + * + * This is intended to mirror the operation of the original + * (see android.webkit.CallbackProxy) with one addition of intent flags + * "FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET". This improves behavior when sublaunching + * other apps via embedded URI's. + * + * We also use this hook to catch "mailto:" links and handle them locally. + */ + @Override + public boolean onUrlInMessageClicked(String url) { + // hijack mailto: uri's and handle locally + if (url != null && url.toLowerCase().startsWith("mailto:")) { + // If it's showing an EML file, we pass -1 as the account id, and MessageCompose + // uses the default account. If there's no accounts set up, MessageCompose will close + // itself. + long senderAccountId = getAccountId(); + + // TODO if MessageCompose implements the account selector, we'll be able to just pass -1 + // as the account id. + return MessageCompose.actionCompose(MessageViewBase.this, url, senderAccountId); + } + + // Handle most uri's via intent launch + boolean result = false; + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName()); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + try { + startActivity(intent); + result = true; + } catch (ActivityNotFoundException ex) { + // No applications can handle it. Ignore. + } + return result; + } + + @Override + public void onLoadMessageStarted() { + setProgressBarIndeterminateVisibility(true); + } + + @Override + public void onLoadMessageFinished() { + setProgressBarIndeterminateVisibility(false); + } + + @Override + public void onLoadMessageError() { + onLoadMessageFinished(); + } + + @Override + public void onFetchAttachmentStarted(String attachmentName) { + mFetchAttachmentProgressDialog.setMessage( + getString(R.string.message_view_fetching_attachment_progress, + attachmentName)); + mFetchAttachmentProgressDialog.show(); + setProgressBarIndeterminateVisibility(true); + } + + @Override + public void onFetchAttachmentFinished() { + mFetchAttachmentProgressDialog.dismiss(); + setProgressBarIndeterminateVisibility(false); + } + + @Override + public void onFetchAttachmentError() { + onFetchAttachmentFinished(); + } + + @Override + public void onMessageNotExists() { // Probably meessage deleted. + finish(); + } + + @Override + public abstract void onRespondedToInvite(int response); + + @Override + public abstract void onCalendarLinkClicked(long epochEventStartTime); + + @Override + public abstract void onMessageSetUnread(); +}