2010-07-30 00:06:00 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2010-11-22 22:04:04 +00:00
|
|
|
import android.app.Activity;
|
2010-07-30 00:06:00 +00:00
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.view.LayoutInflater;
|
2010-11-08 23:04:10 +00:00
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
2010-07-30 00:06:00 +00:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
2011-07-08 03:10:54 +00:00
|
|
|
import android.view.accessibility.AccessibilityEvent;
|
2010-07-30 00:06:00 +00:00
|
|
|
import android.widget.ImageView;
|
2011-07-15 00:23:21 +00:00
|
|
|
import android.widget.PopupMenu;
|
|
|
|
import android.widget.PopupMenu.OnMenuItemClickListener;
|
|
|
|
|
|
|
|
import com.android.email.Email;
|
2011-09-14 08:00:24 +00:00
|
|
|
import com.android.email.Preferences;
|
2011-07-15 00:23:21 +00:00
|
|
|
import com.android.email.R;
|
|
|
|
import com.android.emailcommon.mail.MeetingInfo;
|
|
|
|
import com.android.emailcommon.mail.PackedString;
|
2011-07-20 20:03:54 +00:00
|
|
|
import com.android.emailcommon.provider.Account;
|
2011-07-15 00:23:21 +00:00
|
|
|
import com.android.emailcommon.provider.EmailContent.Message;
|
|
|
|
import com.android.emailcommon.provider.Mailbox;
|
|
|
|
import com.android.emailcommon.service.EmailServiceConstants;
|
|
|
|
import com.android.emailcommon.utility.Utility;
|
2010-07-30 00:06:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A {@link MessageViewFragmentBase} subclass for regular email messages. (regular as in "not eml
|
|
|
|
* files").
|
|
|
|
*/
|
2011-01-10 21:35:47 +00:00
|
|
|
public class MessageViewFragment extends MessageViewFragmentBase
|
2011-08-15 18:59:51 +00:00
|
|
|
implements MoveMessageToDialog.Callback, OnMenuItemClickListener {
|
2011-04-19 21:06:03 +00:00
|
|
|
/** Argument name(s) */
|
|
|
|
private static final String ARG_MESSAGE_ID = "messageId";
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
private ImageView mFavoriteIcon;
|
|
|
|
|
2010-09-30 21:25:57 +00:00
|
|
|
private View mReplyButton;
|
2011-06-15 01:06:24 +00:00
|
|
|
|
2010-09-30 21:25:57 +00:00
|
|
|
private View mReplyAllButton;
|
2011-06-15 01:06:24 +00:00
|
|
|
|
2011-09-14 08:00:24 +00:00
|
|
|
/* Nullable - not available on phone portrait. */
|
2010-09-30 21:25:57 +00:00
|
|
|
private View mForwardButton;
|
|
|
|
|
2011-07-15 00:23:21 +00:00
|
|
|
private View mMoreButton;
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
// calendar meeting invite answers
|
2011-08-15 18:59:51 +00:00
|
|
|
private View mMeetingYes;
|
|
|
|
private View mMeetingMaybe;
|
|
|
|
private View mMeetingNo;
|
2010-07-30 00:06:00 +00:00
|
|
|
private Drawable mFavoriteIconOn;
|
|
|
|
private Drawable mFavoriteIconOff;
|
|
|
|
|
2011-09-14 08:00:24 +00:00
|
|
|
/** Default to ReplyAll if true. Otherwise Reply. */
|
|
|
|
boolean mDefaultReplyAll;
|
|
|
|
|
|
|
|
/** Whether or not to enable Reply/ReplyAll and Forward buttons */
|
|
|
|
boolean mEnableReplyForwardButtons;
|
|
|
|
|
2011-07-20 20:03:54 +00:00
|
|
|
/** Whether or not the message can be moved from the mailbox it's in. */
|
|
|
|
private boolean mSupportsMove;
|
|
|
|
|
2011-04-12 18:08:16 +00:00
|
|
|
private int mPreviousMeetingResponse = EmailServiceConstants.MEETING_REQUEST_NOT_RESPONDED;
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
/**
|
|
|
|
* This class has more call backs than {@link MessageViewFragmentBase}.
|
|
|
|
*
|
|
|
|
* - EML files can't be "mark unread".
|
|
|
|
* - EML files can't have the invite buttons or the view in calender link.
|
|
|
|
* Note EML files can have ICS (calendar invitation) files, but we don't treat them as
|
|
|
|
* invites. (Only exchange provider sets the FLAG_INCOMING_MEETING_INVITE
|
|
|
|
* flag.)
|
|
|
|
* It'd be weird to respond to an invitation in an EML that might not be addressed to you...
|
|
|
|
*/
|
|
|
|
public interface Callback extends MessageViewFragmentBase.Callback {
|
|
|
|
/** Called when the "view in calendar" link is clicked. */
|
|
|
|
public void onCalendarLinkClicked(long epochEventStartTime);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when a calender response button is clicked.
|
|
|
|
*
|
|
|
|
* @param response one of {@link EmailServiceConstants#MEETING_REQUEST_ACCEPTED},
|
|
|
|
* {@link EmailServiceConstants#MEETING_REQUEST_DECLINED}, or
|
|
|
|
* {@link EmailServiceConstants#MEETING_REQUEST_TENTATIVE}.
|
|
|
|
*/
|
|
|
|
public void onRespondedToInvite(int response);
|
|
|
|
|
|
|
|
/** Called when the current message is set unread. */
|
|
|
|
public void onMessageSetUnread();
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
|
|
|
|
/**
|
2011-04-26 21:18:02 +00:00
|
|
|
* Called right before the current message will be deleted or moved to another mailbox.
|
|
|
|
*
|
|
|
|
* Callees will usually close the fragment.
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
*/
|
2011-04-26 21:18:02 +00:00
|
|
|
public void onBeforeMessageGone();
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
|
|
|
|
/** Called when the forward button is pressed. */
|
|
|
|
public void onForward();
|
|
|
|
/** Called when the reply button is pressed. */
|
|
|
|
public void onReply();
|
|
|
|
/** Called when the reply-all button is pressed. */
|
|
|
|
public void onReplyAll();
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static final class EmptyCallback extends MessageViewFragmentBase.EmptyCallback
|
|
|
|
implements Callback {
|
2011-04-20 17:47:04 +00:00
|
|
|
@SuppressWarnings("hiding")
|
2010-07-30 00:06:00 +00:00
|
|
|
public static final Callback INSTANCE = new EmptyCallback();
|
|
|
|
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
@Override public void onCalendarLinkClicked(long epochEventStartTime) { }
|
|
|
|
@Override public void onMessageSetUnread() { }
|
|
|
|
@Override public void onRespondedToInvite(int response) { }
|
2011-04-26 21:18:02 +00:00
|
|
|
@Override public void onBeforeMessageGone() { }
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
@Override public void onForward() { }
|
|
|
|
@Override public void onReply() { }
|
|
|
|
@Override public void onReplyAll() { }
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private Callback mCallback = EmptyCallback.INSTANCE;
|
|
|
|
|
2011-04-19 21:06:03 +00:00
|
|
|
/**
|
|
|
|
* Create a new instance with initialization parameters.
|
2011-04-26 00:12:40 +00:00
|
|
|
*
|
|
|
|
* This fragment should be created only with this method. (Arguments should always be set.)
|
2011-06-02 23:44:13 +00:00
|
|
|
*
|
|
|
|
* @param messageId ID of the message to open
|
2011-04-19 21:06:03 +00:00
|
|
|
*/
|
2011-06-29 21:41:49 +00:00
|
|
|
public static MessageViewFragment newInstance(long messageId) {
|
2011-06-02 23:44:13 +00:00
|
|
|
if (messageId == Message.NO_MESSAGE) {
|
2011-05-17 00:29:13 +00:00
|
|
|
throw new IllegalArgumentException();
|
2011-05-12 00:40:36 +00:00
|
|
|
}
|
2011-04-19 21:06:03 +00:00
|
|
|
final MessageViewFragment instance = new MessageViewFragment();
|
|
|
|
final Bundle args = new Bundle();
|
|
|
|
args.putLong(ARG_MESSAGE_ID, messageId);
|
|
|
|
instance.setArguments(args);
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2011-05-26 21:57:25 +00:00
|
|
|
/**
|
|
|
|
* We will display the message for this ID. This must never be a special message ID such as
|
|
|
|
* {@link Message#NO_MESSAGE}. Do NOT use directly; instead, use {@link #getMessageId()}.
|
|
|
|
* <p><em>NOTE:</em> Although we cannot force these to be immutable using Java language
|
|
|
|
* constructs, this <em>must</em> be considered immutable.
|
|
|
|
*/
|
|
|
|
private Long mImmutableMessageId;
|
2011-05-17 22:10:52 +00:00
|
|
|
|
|
|
|
private void initializeArgCache() {
|
2011-05-26 21:57:25 +00:00
|
|
|
if (mImmutableMessageId != null) return;
|
|
|
|
mImmutableMessageId = getArguments().getLong(ARG_MESSAGE_ID);
|
2011-05-17 22:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the message ID passed to {@link #newInstance}. Safe to call even before onCreate.
|
|
|
|
*/
|
|
|
|
public long getMessageId() {
|
|
|
|
initializeArgCache();
|
2011-05-26 21:57:25 +00:00
|
|
|
return mImmutableMessageId;
|
2011-05-12 00:40:36 +00:00
|
|
|
}
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
2011-06-07 21:00:43 +00:00
|
|
|
setHasOptionsMenu(true);
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
final Resources res = getActivity().getResources();
|
2011-09-12 19:35:03 +00:00
|
|
|
mFavoriteIconOn = res.getDrawable(R.drawable.btn_star_on_convo_holo_light);
|
|
|
|
mFavoriteIconOff = res.getDrawable(R.drawable.btn_star_off_convo_holo_light);
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
|
2011-09-14 08:00:24 +00:00
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
if (mMoreButton != null) {
|
|
|
|
mDefaultReplyAll = Preferences.getSharedPreferences(mContext).getBoolean(
|
|
|
|
Preferences.REPLY_ALL, Preferences.REPLY_ALL_DEFAULT);
|
|
|
|
|
|
|
|
int replyVisibility = View.GONE;
|
|
|
|
int replyAllVisibility = View.GONE;
|
|
|
|
if (mEnableReplyForwardButtons) {
|
|
|
|
replyVisibility = mDefaultReplyAll ? View.GONE : View.VISIBLE;
|
|
|
|
replyAllVisibility = mDefaultReplyAll ? View.VISIBLE : View.GONE;
|
|
|
|
}
|
|
|
|
mReplyButton.setVisibility(replyVisibility);
|
|
|
|
mReplyAllButton.setVisibility(replyAllVisibility);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
@Override
|
|
|
|
public View onCreateView(
|
|
|
|
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
|
|
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
|
|
|
|
2011-04-06 00:02:13 +00:00
|
|
|
mFavoriteIcon = (ImageView) UiUtilities.getView(view, R.id.favorite);
|
|
|
|
mReplyButton = UiUtilities.getView(view, R.id.reply);
|
2011-09-14 08:00:24 +00:00
|
|
|
mReplyAllButton = UiUtilities.getView(view, R.id.reply_all);
|
2011-06-15 01:06:24 +00:00
|
|
|
mForwardButton = UiUtilities.getViewOrNull(view, R.id.forward);
|
2011-08-15 18:59:51 +00:00
|
|
|
mMeetingYes = UiUtilities.getView(view, R.id.accept);
|
|
|
|
mMeetingMaybe = UiUtilities.getView(view, R.id.maybe);
|
|
|
|
mMeetingNo = UiUtilities.getView(view, R.id.decline);
|
2011-09-14 08:00:24 +00:00
|
|
|
mMoreButton = UiUtilities.getViewOrNull(view, R.id.more);
|
2010-07-30 00:06:00 +00:00
|
|
|
|
2010-09-30 21:25:57 +00:00
|
|
|
mFavoriteIcon.setOnClickListener(this);
|
|
|
|
mReplyButton.setOnClickListener(this);
|
2011-09-14 08:00:24 +00:00
|
|
|
mReplyAllButton.setOnClickListener(this);
|
|
|
|
if (mMoreButton != null) {
|
2011-07-15 00:23:21 +00:00
|
|
|
mMoreButton.setOnClickListener(this);
|
2011-06-15 01:06:24 +00:00
|
|
|
}
|
2011-09-14 08:00:24 +00:00
|
|
|
if (mForwardButton != null) {
|
|
|
|
mForwardButton.setOnClickListener(this);
|
|
|
|
}
|
2011-08-15 18:59:51 +00:00
|
|
|
mMeetingYes.setOnClickListener(this);
|
|
|
|
mMeetingMaybe.setOnClickListener(this);
|
|
|
|
mMeetingNo.setOnClickListener(this);
|
2011-04-06 00:02:13 +00:00
|
|
|
UiUtilities.getView(view, R.id.invite_link).setOnClickListener(this);
|
2010-07-30 00:06:00 +00:00
|
|
|
|
2010-09-30 21:25:57 +00:00
|
|
|
enableReplyForwardButtons(false);
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
2010-11-08 23:04:10 +00:00
|
|
|
@Override
|
|
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
|
|
inflater.inflate(R.menu.message_view_fragment_option, menu);
|
|
|
|
}
|
|
|
|
|
2011-07-20 20:03:54 +00:00
|
|
|
@Override
|
|
|
|
public void onPrepareOptionsMenu(Menu menu) {
|
|
|
|
menu.findItem(R.id.move).setVisible(mSupportsMove);
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:25:57 +00:00
|
|
|
private void enableReplyForwardButtons(boolean enabled) {
|
2011-09-14 08:00:24 +00:00
|
|
|
mEnableReplyForwardButtons = enabled;
|
2010-09-30 21:25:57 +00:00
|
|
|
// We don't have disabled button assets, so let's hide them for now
|
|
|
|
final int visibility = enabled ? View.VISIBLE : View.GONE;
|
2011-09-14 08:00:24 +00:00
|
|
|
|
|
|
|
// Modify Reply All button only if there's no overflow OR there is
|
|
|
|
// overflow but default is to show the Reply All button
|
|
|
|
if (mMoreButton == null || mDefaultReplyAll) {
|
|
|
|
UiUtilities.setVisibilitySafe(mReplyAllButton, visibility);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify Reply button only if there's no overflow OR there is
|
|
|
|
// overflow but default is to show the Reply button
|
|
|
|
if (mMoreButton == null || !mDefaultReplyAll) {
|
|
|
|
UiUtilities.setVisibilitySafe(mReplyButton, visibility);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mForwardButton != null) {
|
2011-06-15 01:06:24 +00:00
|
|
|
mForwardButton.setVisibility(visibility);
|
2011-09-14 08:00:24 +00:00
|
|
|
}
|
|
|
|
if (mMoreButton != null) {
|
2011-08-11 21:12:48 +00:00
|
|
|
mMoreButton.setVisibility(visibility);
|
2011-06-15 01:06:24 +00:00
|
|
|
}
|
2010-09-22 23:17:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setCallback(Callback callback) {
|
|
|
|
mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
|
|
|
|
super.setCallback(mCallback);
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 01:44:05 +00:00
|
|
|
@Override
|
|
|
|
protected void resetView() {
|
|
|
|
super.resetView();
|
2011-04-12 18:08:16 +00:00
|
|
|
mPreviousMeetingResponse = EmailServiceConstants.MEETING_REQUEST_NOT_RESPONDED;
|
2010-09-30 01:44:05 +00:00
|
|
|
}
|
|
|
|
|
2010-10-12 21:47:29 +00:00
|
|
|
/**
|
|
|
|
* NOTE See the comment on the super method. It's called on a worker thread.
|
|
|
|
*/
|
2010-07-30 00:06:00 +00:00
|
|
|
@Override
|
2010-11-22 22:04:04 +00:00
|
|
|
protected Message openMessageSync(Activity activity) {
|
2011-05-17 22:10:52 +00:00
|
|
|
return Message.restoreMessageWithId(activity, getMessageId());
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
@Override
|
2011-07-20 20:03:54 +00:00
|
|
|
protected void onMessageShown(long messageId, Mailbox mailbox) {
|
|
|
|
super.onMessageShown(messageId, mailbox);
|
|
|
|
|
|
|
|
Account account = Account.restoreAccountWithId(mContext, getAccountId());
|
|
|
|
boolean supportsMove = account.supportsMoveMessages(mContext)
|
|
|
|
&& mailbox.canHaveMessagesMoved();
|
|
|
|
if (mSupportsMove != supportsMove) {
|
|
|
|
mSupportsMove = supportsMove;
|
|
|
|
Activity host = getActivity();
|
|
|
|
if (host != null) {
|
|
|
|
host.invalidateOptionsMenu();
|
|
|
|
}
|
|
|
|
}
|
Let MessageViewFragment own bottom buttons.
Create a custom view containing the bottons below MVF
(delete, move, reply, etc) and let MVF own this.
These buttons used to be owned by the XL activity itself, because
the UI for these commands will most likely be totally different
from the tablet UI, so the fragment having them looked wrong.
However, this made it harder to make changes suggested by the latest
mock, such as "put reply/forward in the message header".
I think the buttons are semantically part of the message view anyway,
so the fragment owning UI for these commands is probably the way to go.
(And let's worry about the phone UI later.)
Reason for the use of a custom view is that it will make it easier
to make non-trivial UI changes, e.g. "combine reply, reply-all and
forward and make it dropdown."
Also removed obsolete TODOs from MessageListXL.
Change-Id: Ibf93f4c70fe07bdbbe33d2adb6bbd2b96812830d
2010-09-16 18:16:46 +00:00
|
|
|
|
|
|
|
// Disable forward/reply buttons as necessary.
|
2011-07-20 20:03:54 +00:00
|
|
|
enableReplyForwardButtons(Mailbox.isMailboxTypeReplyAndForwardable(mailbox.mType));
|
2010-09-22 23:17:53 +00:00
|
|
|
}
|
|
|
|
|
2011-07-08 03:10:54 +00:00
|
|
|
/**
|
|
|
|
* Sets the content description for the star icon based on whether it's currently starred.
|
|
|
|
*/
|
|
|
|
private void setStarContentDescription(boolean isFavorite) {
|
|
|
|
if (isFavorite) {
|
|
|
|
mFavoriteIcon.setContentDescription(
|
|
|
|
mContext.getResources().getString(R.string.remove_star_action));
|
|
|
|
} else {
|
|
|
|
mFavoriteIcon.setContentDescription(
|
|
|
|
mContext.getResources().getString(R.string.set_star_action));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
/**
|
|
|
|
* Toggle favorite status and write back to provider
|
|
|
|
*/
|
|
|
|
private void onClickFavorite() {
|
2011-05-12 00:40:36 +00:00
|
|
|
if (!isMessageOpen()) return;
|
2010-07-30 00:06:00 +00:00
|
|
|
Message message = getMessage();
|
|
|
|
|
|
|
|
// Update UI
|
|
|
|
boolean newFavorite = ! message.mFlagFavorite;
|
|
|
|
mFavoriteIcon.setImageDrawable(newFavorite ? mFavoriteIconOn : mFavoriteIconOff);
|
|
|
|
|
2011-07-08 03:10:54 +00:00
|
|
|
// Handle accessibility event
|
|
|
|
setStarContentDescription(newFavorite);
|
|
|
|
mFavoriteIcon.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
// Update provider
|
|
|
|
message.mFlagFavorite = newFavorite;
|
|
|
|
getController().setMessageFavorite(message.mId, newFavorite);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set message read/unread.
|
|
|
|
*/
|
|
|
|
public void onMarkMessageAsRead(boolean isRead) {
|
2011-05-12 00:40:36 +00:00
|
|
|
if (!isMessageOpen()) return;
|
2010-07-30 00:06:00 +00:00
|
|
|
Message message = getMessage();
|
|
|
|
if (message.mFlagRead != isRead) {
|
|
|
|
message.mFlagRead = isRead;
|
|
|
|
getController().setMessageRead(message.mId, isRead);
|
|
|
|
if (!isRead) { // Became unread. We need to close the message.
|
|
|
|
mCallback.onMessageSetUnread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a service message indicating that a meeting invite button has been clicked.
|
|
|
|
*/
|
|
|
|
private void onRespondToInvite(int response, int toastResId) {
|
2011-05-12 00:40:36 +00:00
|
|
|
if (!isMessageOpen()) return;
|
2010-07-30 00:06:00 +00:00
|
|
|
Message message = getMessage();
|
|
|
|
// do not send twice in a row the same response
|
|
|
|
if (mPreviousMeetingResponse != response) {
|
|
|
|
getController().sendMeetingResponse(message.mId, response);
|
|
|
|
mPreviousMeetingResponse = response;
|
|
|
|
}
|
|
|
|
Utility.showToast(getActivity(), toastResId);
|
|
|
|
mCallback.onRespondedToInvite(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onInviteLinkClicked() {
|
2011-05-12 00:40:36 +00:00
|
|
|
if (!isMessageOpen()) return;
|
2010-07-30 00:06:00 +00:00
|
|
|
Message message = getMessage();
|
|
|
|
String startTime = new PackedString(message.mMeetingInfo).get(MeetingInfo.MEETING_DTSTART);
|
|
|
|
if (startTime != null) {
|
|
|
|
long epochTimeMillis = Utility.parseEmailDateTimeToMillis(startTime);
|
|
|
|
mCallback.onCalendarLinkClicked(epochTimeMillis);
|
|
|
|
} else {
|
|
|
|
Email.log("meetingInfo without DTSTART " + message.mMeetingInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClick(View view) {
|
|
|
|
if (!isMessageOpen()) {
|
|
|
|
return; // Ignore.
|
|
|
|
}
|
|
|
|
switch (view.getId()) {
|
2010-09-30 21:25:57 +00:00
|
|
|
case R.id.reply:
|
|
|
|
mCallback.onReply();
|
|
|
|
return;
|
|
|
|
case R.id.reply_all:
|
|
|
|
mCallback.onReplyAll();
|
|
|
|
return;
|
|
|
|
case R.id.forward:
|
|
|
|
mCallback.onForward();
|
|
|
|
return;
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
case R.id.favorite:
|
|
|
|
onClickFavorite();
|
|
|
|
return;
|
2010-09-30 21:25:57 +00:00
|
|
|
|
2011-01-10 21:35:47 +00:00
|
|
|
case R.id.invite_link:
|
|
|
|
onInviteLinkClicked();
|
|
|
|
return;
|
2011-07-15 00:23:21 +00:00
|
|
|
|
2011-08-15 18:59:51 +00:00
|
|
|
case R.id.accept:
|
|
|
|
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_ACCEPTED,
|
|
|
|
R.string.message_view_invite_toast_yes);
|
|
|
|
return;
|
|
|
|
case R.id.maybe:
|
|
|
|
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_TENTATIVE,
|
|
|
|
R.string.message_view_invite_toast_maybe);
|
|
|
|
return;
|
|
|
|
case R.id.decline:
|
|
|
|
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_DECLINED,
|
|
|
|
R.string.message_view_invite_toast_no);
|
2011-08-18 19:15:37 +00:00
|
|
|
return;
|
2011-08-15 18:59:51 +00:00
|
|
|
|
2011-07-15 00:23:21 +00:00
|
|
|
case R.id.more: {
|
|
|
|
PopupMenu popup = new PopupMenu(getActivity(), mMoreButton);
|
2011-09-14 08:00:24 +00:00
|
|
|
Menu menu = popup.getMenu();
|
2011-07-15 00:23:21 +00:00
|
|
|
popup.getMenuInflater().inflate(R.menu.message_header_overflow_menu,
|
2011-09-14 08:00:24 +00:00
|
|
|
menu);
|
|
|
|
|
|
|
|
// Remove Reply if ReplyAll icon is visible or vice versa
|
|
|
|
menu.removeItem(mDefaultReplyAll ? R.id.reply_all : R.id.reply);
|
2011-07-15 00:23:21 +00:00
|
|
|
popup.setOnMenuItemClickListener(this);
|
|
|
|
popup.show();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-01-10 21:35:47 +00:00
|
|
|
}
|
|
|
|
super.onClick(view);
|
|
|
|
}
|
|
|
|
|
2011-07-15 00:23:21 +00:00
|
|
|
@Override
|
|
|
|
public boolean onMenuItemClick(MenuItem item) {
|
|
|
|
if (isMessageOpen()) {
|
|
|
|
switch (item.getItemId()) {
|
2011-09-14 08:00:24 +00:00
|
|
|
case R.id.reply:
|
|
|
|
mCallback.onReply();
|
|
|
|
return true;
|
2011-07-15 00:23:21 +00:00
|
|
|
case R.id.reply_all:
|
|
|
|
mCallback.onReplyAll();
|
|
|
|
return true;
|
|
|
|
case R.id.forward:
|
|
|
|
mCallback.onForward();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-08 23:04:10 +00:00
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.move:
|
|
|
|
onMove();
|
|
|
|
return true;
|
|
|
|
case R.id.delete:
|
|
|
|
onDelete();
|
|
|
|
return true;
|
|
|
|
case R.id.mark_as_unread:
|
|
|
|
onMarkAsUnread();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onMove() {
|
2011-05-17 22:10:52 +00:00
|
|
|
MoveMessageToDialog dialog = MoveMessageToDialog.newInstance(new long[] {getMessageId()},
|
2011-04-26 21:18:02 +00:00
|
|
|
this);
|
|
|
|
dialog.show(getFragmentManager(), "dialog");
|
|
|
|
}
|
|
|
|
|
|
|
|
// MoveMessageToDialog$Callback
|
|
|
|
@Override
|
|
|
|
public void onMoveToMailboxSelected(long newMailboxId, long[] messageIds) {
|
|
|
|
mCallback.onBeforeMessageGone();
|
|
|
|
ActivityHelper.moveMessages(mContext, newMailboxId, messageIds);
|
2010-11-08 23:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void onDelete() {
|
2011-04-26 21:18:02 +00:00
|
|
|
mCallback.onBeforeMessageGone();
|
2011-05-17 22:10:52 +00:00
|
|
|
ActivityHelper.deleteMessage(mContext, getMessageId());
|
2010-11-08 23:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void onMarkAsUnread() {
|
|
|
|
onMarkMessageAsRead(false);
|
|
|
|
}
|
|
|
|
|
2010-07-30 00:06:00 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*
|
|
|
|
* Mark the current as unread.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void onPostLoadBody() {
|
|
|
|
onMarkMessageAsRead(true);
|
2011-07-08 03:10:54 +00:00
|
|
|
|
|
|
|
// Initialize star content description for accessibility
|
|
|
|
Message message = getMessage();
|
|
|
|
setStarContentDescription(message.mFlagFavorite);
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-10-02 01:04:12 +00:00
|
|
|
protected void updateHeaderView(Message message) {
|
|
|
|
super.updateHeaderView(message);
|
2010-07-30 00:06:00 +00:00
|
|
|
|
|
|
|
mFavoriteIcon.setImageDrawable(message.mFlagFavorite ? mFavoriteIconOn : mFavoriteIconOff);
|
2010-10-02 01:04:12 +00:00
|
|
|
|
2010-10-04 23:00:02 +00:00
|
|
|
// Enable the invite tab if necessary
|
|
|
|
if ((message.mFlags & Message.FLAG_INCOMING_MEETING_INVITE) != 0) {
|
|
|
|
addTabFlags(TAB_FLAGS_HAS_INVITE);
|
|
|
|
}
|
2010-07-30 00:06:00 +00:00
|
|
|
}
|
|
|
|
}
|