SecureConversationViewController.java revision 59ccec3db4710f2aea6a4a9a30160ad19331367d
1/*
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;
21import android.app.FragmentManager;
22import android.content.res.Resources;
23import android.graphics.Rect;
24import 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;
32import com.android.mail.browse.AttachmentActionHandler;
33import com.android.mail.browse.ConversationMessage;
34import com.android.mail.browse.ConversationViewAdapter;
35import com.android.mail.browse.ConversationViewAdapter.MessageFooterItem;
36import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
37import com.android.mail.browse.ConversationViewHeader;
38import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreator;
39import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreatorHolder;
40import com.android.mail.browse.MessageFooterView;
41import com.android.mail.browse.MessageHeaderView;
42import com.android.mail.browse.MessageScrollView;
43import com.android.mail.browse.MessageWebView;
44import com.android.mail.browse.ScrollNotifier.ScrollListener;
45import com.android.mail.browse.WebViewContextMenu;
46import com.android.mail.print.PrintUtils;
47import com.android.mail.providers.Conversation;
48import com.android.mail.providers.Message;
49import com.android.mail.utils.ConversationViewUtils;
50
51/**
52 * Controller to do most of the heavy lifting for
53 * {@link SecureConversationViewFragment} and
54 * {@link com.android.mail.browse.EmlMessageViewFragment}. Currently that work
55 * is pretty much the rendering logic.
56 */
57public class SecureConversationViewController implements
58        MessageHeaderView.MessageHeaderViewCallbacks, ScrollListener,
59        MessageFooterView.MessageFooterCallbacks {
60    private static final String BEGIN_HTML =
61            "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
62    private static final String END_HTML = "</div></body>";
63
64    private final SecureConversationViewControllerCallbacks mCallbacks;
65
66    private MessageWebView mWebView;
67    private ConversationViewHeader mConversationHeaderView;
68    private MessageHeaderView mMessageHeaderView;
69    private MessageHeaderView mSnapHeaderView;
70    private MessageFooterView mMessageFooterView;
71    private ConversationMessage mMessage;
72    private MessageScrollView mScrollView;
73
74    private ConversationViewProgressController mProgressController;
75    private FormattedDateBuilder mDateBuilder;
76
77    private int mSideMarginInWebPx;
78
79    public SecureConversationViewController(SecureConversationViewControllerCallbacks callbacks) {
80        mCallbacks = callbacks;
81    }
82
83    public View onCreateView(LayoutInflater inflater, ViewGroup container,
84            Bundle savedInstanceState) {
85        View rootView = inflater.inflate(R.layout.secure_conversation_view, container, false);
86        mScrollView = (MessageScrollView) rootView.findViewById(R.id.scroll_view);
87        mConversationHeaderView = (ConversationViewHeader) rootView.findViewById(R.id.conv_header);
88        mMessageHeaderView = (MessageHeaderView) rootView.findViewById(R.id.message_header);
89        mSnapHeaderView = (MessageHeaderView) rootView.findViewById(R.id.snap_header);
90        mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
91
92        mScrollView.addScrollListener(this);
93
94        // Add color backgrounds to the header and footer.
95        // Otherwise the backgrounds are grey. They can't
96        // be set in xml because that would add more overdraw
97        // in ConversationViewFragment.
98        final int color = rootView.getResources().getColor(
99                R.color.message_header_background_color);
100        mMessageHeaderView.setBackgroundColor(color);
101        mSnapHeaderView.setBackgroundColor(color);
102        mMessageFooterView.setBackgroundColor(color);
103
104        mProgressController = new ConversationViewProgressController(
105                mCallbacks.getFragment(), mCallbacks.getHandler());
106        mProgressController.instantiateProgressIndicators(rootView);
107        mWebView = (MessageWebView) rootView.findViewById(R.id.webview);
108        mWebView.setOverScrollMode(View.OVER_SCROLL_NEVER);
109        mWebView.setWebViewClient(mCallbacks.getWebViewClient());
110        final InlineAttachmentViewIntentBuilderCreator creator =
111                InlineAttachmentViewIntentBuilderCreatorHolder.
112                        getInlineAttachmentViewIntentCreator();
113        mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(
114                mCallbacks.getFragment().getActivity(),
115                creator.createInlineAttachmentViewIntentBuilder(null, -1)));
116        mWebView.setFocusable(false);
117        final WebSettings settings = mWebView.getSettings();
118
119        settings.setJavaScriptEnabled(false);
120        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
121
122        ConversationViewUtils.setTextZoom(mCallbacks.getFragment().getResources(), settings);
123
124        settings.setSupportZoom(true);
125        settings.setBuiltInZoomControls(true);
126        settings.setDisplayZoomControls(false);
127
128        mScrollView.setInnerScrollableView(mWebView);
129
130        return rootView;
131    }
132
133    public void onActivityCreated(Bundle savedInstanceState) {
134        mCallbacks.setupConversationHeaderView(mConversationHeaderView);
135
136        final Fragment fragment = mCallbacks.getFragment();
137
138        mDateBuilder = new FormattedDateBuilder(fragment.getActivity());
139        mMessageHeaderView.initialize(
140                mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
141        mMessageHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
142        mMessageHeaderView.setCallbacks(this);
143        mMessageHeaderView.setExpandable(false);
144        mMessageHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
145
146        mSnapHeaderView.setSnappy();
147        mSnapHeaderView.initialize(
148                mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
149        mSnapHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
150        mSnapHeaderView.setCallbacks(this);
151        mSnapHeaderView.setExpandable(false);
152        mSnapHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
153
154        mCallbacks.setupMessageHeaderVeiledMatcher(mMessageHeaderView);
155        mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
156
157        mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager(),
158                mCallbacks.getConversationAccountController(), this);
159
160        mCallbacks.startMessageLoader();
161
162        mProgressController.showLoadingStatus(mCallbacks.isViewVisibleToUser());
163
164        final Resources r = mCallbacks.getFragment().getResources();
165        mSideMarginInWebPx = (int) (r.getDimensionPixelOffset(
166                R.dimen.conversation_message_content_margin_side) / r.getDisplayMetrics().density);
167    }
168
169    public void onDestroyView() {
170        if (mMessage != null && mMessage.getConversation() != null) {
171            AttachmentActionHandler.unregisterDismissListeners(mMessage.getConversation().uri);
172        }
173    }
174
175    @Override
176    public void onNotifierScroll(final int y) {
177        // We need to decide whether or not to display the snap header.
178        // Get the location of the moveable message header inside the scroll view.
179        Rect rect = new Rect();
180        mScrollView.offsetDescendantRectToMyCoords(mMessageHeaderView, rect);
181
182        // If we have scrolled further than the distance from the top of the scrollView to the top
183        // of the message header, then the message header is at least partially ofscreen. As soon
184        // as the message header goes partially offscreen we need to display the snap header.
185        if (y > rect.top) {
186            mSnapHeaderView.setVisibility(View.VISIBLE);
187        } else {
188            mSnapHeaderView.setVisibility(View.GONE);
189        }
190    }
191
192    /**
193     * Populate the adapter with overlay views (message headers, super-collapsed
194     * blocks, a conversation header), and return an HTML document with spacer
195     * divs inserted for all overlays.
196     */
197    public void renderMessage(ConversationMessage message) {
198        mMessage = message;
199
200        final boolean alwaysShowImages = mCallbacks.shouldAlwaysShowImages();
201        mWebView.getSettings().setBlockNetworkImage(
202                !alwaysShowImages && !mMessage.alwaysShowImages);
203
204        // Add formatting to message body
205        // At this point, only adds margins.
206        StringBuilder dataBuilder = new StringBuilder(
207                String.format(BEGIN_HTML, mSideMarginInWebPx));
208        dataBuilder.append(mMessage.getBodyAsHtml());
209        dataBuilder.append(END_HTML);
210
211        mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(), dataBuilder.toString(),
212                "text/html", "utf-8", null);
213        final MessageHeaderItem item = ConversationViewAdapter.newMessageHeaderItem(
214                null, mDateBuilder, mMessage, true, mMessage.alwaysShowImages);
215        // Clear out the old info from the header before (re)binding
216        mMessageHeaderView.unbind();
217        mMessageHeaderView.bind(item, false);
218
219        mSnapHeaderView.unbind();
220        mSnapHeaderView.bind(item, false);
221
222        if (mMessage.hasAttachments) {
223            mMessageFooterView.setVisibility(View.VISIBLE);
224            mMessageFooterView.bind(
225                    item, ConversationViewAdapter.newMessageFooterItem(null, item), false);
226        }
227    }
228
229    public ConversationMessage getMessage() {
230        return mMessage;
231    }
232
233    public ConversationViewHeader getConversationHeaderView() {
234        return mConversationHeaderView;
235    }
236
237    public void dismissLoadingStatus() {
238        mProgressController.dismissLoadingStatus();
239    }
240
241    public void setSubject(String subject) {
242        mConversationHeaderView.setSubject(subject);
243    }
244
245    public void printMessage() {
246        final Conversation conversation = mMessage.getConversation();
247        PrintUtils.printMessage(mCallbacks.getFragment().getActivity(), mMessage,
248                conversation != null ? conversation.subject : mMessage.subject,
249                mCallbacks.getAddressCache(), mCallbacks.getBaseUri(), false /* useJavascript */);
250
251    }
252
253    // Start MessageHeaderViewCallbacks implementations
254
255    @Override
256    public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight) {
257        // Do nothing.
258    }
259
260    @Override
261    public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight) {
262        // Do nothing.
263    }
264
265    @Override
266    public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightBefore) {
267        // Do nothing.
268    }
269
270    @Override
271    public void showExternalResources(final Message msg) {
272        mWebView.getSettings().setBlockNetworkImage(false);
273    }
274
275    @Override
276    public void showExternalResources(final String rawSenderAddress) {
277        mWebView.getSettings().setBlockNetworkImage(false);
278    }
279
280    @Override
281    public boolean supportsMessageTransforms() {
282        return false;
283    }
284
285    @Override
286    public String getMessageTransforms(final Message msg) {
287        return null;
288    }
289
290    @Override
291    public boolean isSecure() {
292        return true;
293    }
294
295    @Override
296    public FragmentManager getFragmentManager() {
297        return mCallbacks.getFragment().getFragmentManager();
298    }
299
300    // End MessageHeaderViewCallbacks implementations
301
302    // START MessageFooterCallbacks
303
304    @Override
305    public void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight) {
306        // Do nothing.
307    }
308
309    @Override
310    public MessageFooterView getViewForItem(MessageFooterItem item) {
311        return mMessageFooterView;
312    }
313
314    @Override
315    public int getUpdatedHeight(MessageFooterItem item) {
316        return 0; // should never get called since we'll always have a footer view
317    }
318
319    // END MessageFooterCallbacks
320}
321