blob: a0e190e6e9aa147189e9077147290ca6d39a8fe6 [file] [log] [blame]
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -07001/*
2 * Copyright (C) 2013 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mail.ui;
19
20import android.app.Fragment;
Andrew Sapperstein21a6ed22013-07-17 18:33:00 -070021import android.app.FragmentManager;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070022import android.content.res.Resources;
Martin Hibdon50554de2014-01-07 16:28:04 -080023import android.graphics.Rect;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070024import android.os.Bundle;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.webkit.WebSettings;
29
30import com.android.mail.FormattedDateBuilder;
31import com.android.mail.R;
Andrew Sapperstein381c3222014-04-20 12:23:57 -070032import com.android.mail.browse.AttachmentActionHandler;
Andrew Sappersteine7e2c0a2013-08-02 17:09:54 -070033import com.android.mail.browse.BorderView;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070034import com.android.mail.browse.ConversationMessage;
35import com.android.mail.browse.ConversationViewAdapter;
Andrew Sapperstein381c3222014-04-20 12:23:57 -070036import com.android.mail.browse.ConversationViewAdapter.MessageFooterItem;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070037import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
38import com.android.mail.browse.ConversationViewHeader;
Andrew Sapperstein8ec43e82013-12-17 18:27:55 -080039import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreator;
40import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreatorHolder;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070041import com.android.mail.browse.MessageFooterView;
42import com.android.mail.browse.MessageHeaderView;
43import com.android.mail.browse.MessageScrollView;
44import com.android.mail.browse.MessageWebView;
Martin Hibdon50554de2014-01-07 16:28:04 -080045import com.android.mail.browse.ScrollNotifier.ScrollListener;
Andrew Sappersteine3077852013-12-11 18:04:25 -080046import com.android.mail.browse.WebViewContextMenu;
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -070047import com.android.mail.print.PrintUtils;
48import com.android.mail.providers.Conversation;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070049import com.android.mail.providers.Message;
50import com.android.mail.utils.ConversationViewUtils;
51
52/**
Martin Hibdon50554de2014-01-07 16:28:04 -080053 * Controller to do most of the heavy lifting for
54 * {@link SecureConversationViewFragment} and
55 * {@link com.android.mail.browse.EmlMessageViewFragment}. Currently that work
56 * is pretty much the rendering logic.
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070057 */
58public class SecureConversationViewController implements
Andrew Sapperstein381c3222014-04-20 12:23:57 -070059 MessageHeaderView.MessageHeaderViewCallbacks, ScrollListener,
60 MessageFooterView.MessageFooterCallbacks {
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070061 private static final String BEGIN_HTML =
Andrew Sappersteind4945f22014-03-29 18:36:09 -070062 "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070063 private static final String END_HTML = "</div></body>";
64
65 private final SecureConversationViewControllerCallbacks mCallbacks;
66
67 private MessageWebView mWebView;
68 private ConversationViewHeader mConversationHeaderView;
69 private MessageHeaderView mMessageHeaderView;
Martin Hibdon50554de2014-01-07 16:28:04 -080070 private MessageHeaderView mSnapHeaderView;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070071 private MessageFooterView mMessageFooterView;
72 private ConversationMessage mMessage;
73 private MessageScrollView mScrollView;
74
75 private ConversationViewProgressController mProgressController;
76 private FormattedDateBuilder mDateBuilder;
77
78 private int mSideMarginInWebPx;
79
80 public SecureConversationViewController(SecureConversationViewControllerCallbacks callbacks) {
81 mCallbacks = callbacks;
82 }
83
84 public View onCreateView(LayoutInflater inflater, ViewGroup container,
85 Bundle savedInstanceState) {
86 View rootView = inflater.inflate(R.layout.secure_conversation_view, container, false);
87 mScrollView = (MessageScrollView) rootView.findViewById(R.id.scroll_view);
88 mConversationHeaderView = (ConversationViewHeader) rootView.findViewById(R.id.conv_header);
89 mMessageHeaderView = (MessageHeaderView) rootView.findViewById(R.id.message_header);
Martin Hibdon50554de2014-01-07 16:28:04 -080090 mSnapHeaderView = (MessageHeaderView) rootView.findViewById(R.id.snap_header);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070091 mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
92
Martin Hibdon50554de2014-01-07 16:28:04 -080093 mScrollView.addScrollListener(this);
94
Andrew Sappersteine7e2c0a2013-08-02 17:09:54 -070095 // Add color backgrounds to the header and footer.
96 // Otherwise the backgrounds are grey. They can't
97 // be set in xml because that would add more overdraw
98 // in ConversationViewFragment.
99 final int color = rootView.getResources().getColor(
100 R.color.message_header_background_color);
101 mMessageHeaderView.setBackgroundColor(color);
Andrew Sapperstein2ed8c042014-02-27 18:52:32 -0800102 mSnapHeaderView.findViewById(R.id.upper_header).setBackgroundColor(color);
Andrew Sappersteine7e2c0a2013-08-02 17:09:54 -0700103 mMessageFooterView.setBackgroundColor(color);
104
105 ((BorderView) rootView.findViewById(R.id.top_border)).disableCardBottomBorder();
106 ((BorderView) rootView.findViewById(R.id.bottom_border)).disableCardTopBorder();
107
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700108 mProgressController = new ConversationViewProgressController(
109 mCallbacks.getFragment(), mCallbacks.getHandler());
110 mProgressController.instantiateProgressIndicators(rootView);
111 mWebView = (MessageWebView) rootView.findViewById(R.id.webview);
Andrew Sappersteine45fd112013-08-29 17:34:34 -0700112 mWebView.setOverScrollMode(View.OVER_SCROLL_NEVER);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700113 mWebView.setWebViewClient(mCallbacks.getWebViewClient());
Andrew Sapperstein8ec43e82013-12-17 18:27:55 -0800114 final InlineAttachmentViewIntentBuilderCreator creator =
115 InlineAttachmentViewIntentBuilderCreatorHolder.
Martin Hibdon50554de2014-01-07 16:28:04 -0800116 getInlineAttachmentViewIntentCreator();
Andrew Sapperstein8ec43e82013-12-17 18:27:55 -0800117 mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(
118 mCallbacks.getFragment().getActivity(),
Andy Huang3c6fd442014-03-24 19:56:46 -0700119 creator.createInlineAttachmentViewIntentBuilder(null, -1)));
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700120 mWebView.setFocusable(false);
121 final WebSettings settings = mWebView.getSettings();
122
123 settings.setJavaScriptEnabled(false);
124 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
125
126 ConversationViewUtils.setTextZoom(mCallbacks.getFragment().getResources(), settings);
127
128 settings.setSupportZoom(true);
129 settings.setBuiltInZoomControls(true);
130 settings.setDisplayZoomControls(false);
131
132 mScrollView.setInnerScrollableView(mWebView);
133
134 return rootView;
135 }
136
137 public void onActivityCreated(Bundle savedInstanceState) {
138 mCallbacks.setupConversationHeaderView(mConversationHeaderView);
139
140 final Fragment fragment = mCallbacks.getFragment();
141
142 mDateBuilder = new FormattedDateBuilder(fragment.getActivity());
Andrew Sapperstein14f93742013-07-25 14:29:56 -0700143 mMessageHeaderView.initialize(
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700144 mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700145 mMessageHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
146 mMessageHeaderView.setCallbacks(this);
147 mMessageHeaderView.setExpandable(false);
148 mMessageHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
149
Martin Hibdon50554de2014-01-07 16:28:04 -0800150 mSnapHeaderView.setSnappy();
151 mSnapHeaderView.initialize(
152 mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
153 mSnapHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
154 mSnapHeaderView.setCallbacks(this);
155 mSnapHeaderView.setExpandable(false);
156 mSnapHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
157
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700158 mCallbacks.setupMessageHeaderVeiledMatcher(mMessageHeaderView);
Martin Hibdon50554de2014-01-07 16:28:04 -0800159 mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700160
Andrew Sappersteinf59d01c2014-02-20 10:27:06 -0800161 mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager(),
Andrew Sapperstein381c3222014-04-20 12:23:57 -0700162 mCallbacks.getConversationAccountController(), this);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700163
164 mCallbacks.startMessageLoader();
165
166 mProgressController.showLoadingStatus(mCallbacks.isViewVisibleToUser());
167
168 final Resources r = mCallbacks.getFragment().getResources();
Andrew Sapperstein90eccf92013-08-22 11:53:23 -0700169 mSideMarginInWebPx = (int) (r.getDimensionPixelOffset(
170 R.dimen.conversation_message_content_margin_side) / r.getDisplayMetrics().density);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700171 }
172
Andrew Sapperstein381c3222014-04-20 12:23:57 -0700173 public void onDestroyView() {
174 AttachmentActionHandler.unregisterDismissListeners(mMessage.conversationUri);
175 }
176
Martin Hibdon50554de2014-01-07 16:28:04 -0800177 @Override
Andrew Sappersteind97eb872014-04-16 16:48:03 -0700178 public void onNotifierScroll(final int x, final int y) {
Martin Hibdon50554de2014-01-07 16:28:04 -0800179 // We need to decide whether or not to display the snap header.
180 // Get the location of the moveable message header inside the scroll view.
181 Rect rect = new Rect();
182 mScrollView.offsetDescendantRectToMyCoords(mMessageHeaderView, rect);
183
184 // If we have scrolled further than the distance from the top of the scrollView to the top
185 // of the message header, then the message header is at least partially ofscreen. As soon
186 // as the message header goes partially offscreen we need to display the snap header.
187 if (y > rect.top) {
188 mSnapHeaderView.setVisibility(View.VISIBLE);
189 } else {
190 mSnapHeaderView.setVisibility(View.GONE);
191 }
192 }
193
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700194 /**
195 * Populate the adapter with overlay views (message headers, super-collapsed
196 * blocks, a conversation header), and return an HTML document with spacer
197 * divs inserted for all overlays.
198 */
199 public void renderMessage(ConversationMessage message) {
200 mMessage = message;
201
Andrew Sapperstein8ec43e82013-12-17 18:27:55 -0800202 final boolean alwaysShowImages = mCallbacks.shouldAlwaysShowImages();
203 mWebView.getSettings().setBlockNetworkImage(
204 !alwaysShowImages && !mMessage.alwaysShowImages);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700205
206 // Add formatting to message body
207 // At this point, only adds margins.
208 StringBuilder dataBuilder = new StringBuilder(
209 String.format(BEGIN_HTML, mSideMarginInWebPx));
210 dataBuilder.append(mMessage.getBodyAsHtml());
211 dataBuilder.append(END_HTML);
212
213 mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(), dataBuilder.toString(),
214 "text/html", "utf-8", null);
215 final MessageHeaderItem item = ConversationViewAdapter.newMessageHeaderItem(
Andrew Sapperstein14f93742013-07-25 14:29:56 -0700216 null, mDateBuilder, mMessage, true, mMessage.alwaysShowImages);
Tony Mantler37ce6072013-10-04 09:28:43 -0700217 // Clear out the old info from the header before (re)binding
218 mMessageHeaderView.unbind();
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700219 mMessageHeaderView.bind(item, false);
Martin Hibdon50554de2014-01-07 16:28:04 -0800220
221 mSnapHeaderView.unbind();
222 mSnapHeaderView.bind(item, false);
223
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700224 if (mMessage.hasAttachments) {
225 mMessageFooterView.setVisibility(View.VISIBLE);
Andrew Sapperstein381c3222014-04-20 12:23:57 -0700226 mMessageFooterView.bind(
227 item, ConversationViewAdapter.newMessageFooterItem(null, item), false);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700228 }
229 }
230
231 public ConversationMessage getMessage() {
232 return mMessage;
233 }
234
235 public ConversationViewHeader getConversationHeaderView() {
236 return mConversationHeaderView;
237 }
238
239 public void dismissLoadingStatus() {
240 mProgressController.dismissLoadingStatus();
241 }
242
243 public void setSubject(String subject) {
Andrew Sappersteine6bf30d2014-03-13 19:19:26 -0700244 mConversationHeaderView.setSubject(subject);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700245 }
246
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -0700247 public void printMessage() {
248 final Conversation conversation = mMessage.getConversation();
Andrew Sapperstein234d3532013-10-29 14:54:04 -0700249 PrintUtils.printMessage(mCallbacks.getFragment().getActivity(), mMessage,
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -0700250 conversation != null ? conversation.subject : mMessage.subject,
251 mCallbacks.getAddressCache(), mCallbacks.getBaseUri(), false /* useJavascript */);
252
Andrew Sapperstein5c1692a2013-09-16 11:56:13 -0700253 }
254
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700255 // Start MessageHeaderViewCallbacks implementations
256
257 @Override
258 public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight) {
259 // Do nothing.
260 }
261
262 @Override
Andrew Sappersteincee3c902013-07-31 10:52:02 -0700263 public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight,
264 int topBorderHeight, int bottomBorderHeight) {
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700265 // Do nothing.
266 }
267
268 @Override
269 public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightBefore) {
270 // Do nothing.
271 }
272
273 @Override
274 public void showExternalResources(final Message msg) {
275 mWebView.getSettings().setBlockNetworkImage(false);
276 }
277
278 @Override
279 public void showExternalResources(final String rawSenderAddress) {
280 mWebView.getSettings().setBlockNetworkImage(false);
281 }
282
283 @Override
284 public boolean supportsMessageTransforms() {
285 return false;
286 }
287
288 @Override
289 public String getMessageTransforms(final Message msg) {
290 return null;
291 }
292
Andrew Sapperstein21a6ed22013-07-17 18:33:00 -0700293 @Override
294 public FragmentManager getFragmentManager() {
295 return mCallbacks.getFragment().getFragmentManager();
296 }
297
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700298 // End MessageHeaderViewCallbacks implementations
Andrew Sapperstein381c3222014-04-20 12:23:57 -0700299
300 // START MessageFooterCallbacks
301
302 @Override
303 public void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight) {
304 // Do nothing.
305 }
306
307 @Override
308 public MessageFooterView getViewForItem(MessageFooterItem item) {
309 return mMessageFooterView;
310 }
311
312 @Override
313 public int getUpdatedHeight(MessageFooterItem item) {
314 return 0; // should never get called since we'll always have a footer view
315 }
316
317 // END MessageFooterCallbacks
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700318}