SecureConversationViewController.java revision 3c6fd44f9ae0cf60248dc64ee74d46afed633c45
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.support.v4.text.BidiFormatter;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.webkit.WebSettings;
30
31import com.android.mail.FormattedDateBuilder;
32import com.android.mail.R;
33import com.android.mail.browse.BorderView;
34import com.android.mail.browse.ConversationMessage;
35import com.android.mail.browse.ConversationViewAdapter;
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    private static final String BEGIN_HTML =
60                                           "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
61    private static final String END_HTML = "</div></body>";
62
63    private final SecureConversationViewControllerCallbacks mCallbacks;
64    private final BidiFormatter mBidiFormatter;
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        mBidiFormatter = BidiFormatter.getInstance();
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);
90        mSnapHeaderView = (MessageHeaderView) rootView.findViewById(R.id.snap_header);
91        mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
92
93        mScrollView.addScrollListener(this);
94
95        // 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);
102        mSnapHeaderView.findViewById(R.id.upper_header).setBackgroundColor(color);
103        mMessageFooterView.setBackgroundColor(color);
104
105        ((BorderView) rootView.findViewById(R.id.top_border)).disableCardBottomBorder();
106        ((BorderView) rootView.findViewById(R.id.bottom_border)).disableCardTopBorder();
107
108        mProgressController = new ConversationViewProgressController(
109                mCallbacks.getFragment(), mCallbacks.getHandler());
110        mProgressController.instantiateProgressIndicators(rootView);
111        mWebView = (MessageWebView) rootView.findViewById(R.id.webview);
112        mWebView.setOverScrollMode(View.OVER_SCROLL_NEVER);
113        mWebView.setWebViewClient(mCallbacks.getWebViewClient());
114        final InlineAttachmentViewIntentBuilderCreator creator =
115                InlineAttachmentViewIntentBuilderCreatorHolder.
116                        getInlineAttachmentViewIntentCreator();
117        mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(
118                mCallbacks.getFragment().getActivity(),
119                creator.createInlineAttachmentViewIntentBuilder(null, -1)));
120        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());
143        mMessageHeaderView.initialize(
144                mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
145        mMessageHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
146        mMessageHeaderView.setCallbacks(this);
147        mMessageHeaderView.setExpandable(false);
148        mMessageHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
149
150        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
158        mCallbacks.setupMessageHeaderVeiledMatcher(mMessageHeaderView);
159        mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
160
161        mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager(),
162                mCallbacks.getConversationAccountController());
163
164        mCallbacks.startMessageLoader();
165
166        mProgressController.showLoadingStatus(mCallbacks.isViewVisibleToUser());
167
168        final Resources r = mCallbacks.getFragment().getResources();
169        mSideMarginInWebPx = (int) (r.getDimensionPixelOffset(
170                R.dimen.conversation_message_content_margin_side) / r.getDisplayMetrics().density);
171    }
172
173    @Override
174    public void onNotifierScroll(final int y) {
175        // We need to decide whether or not to display the snap header.
176        // Get the location of the moveable message header inside the scroll view.
177        Rect rect = new Rect();
178        mScrollView.offsetDescendantRectToMyCoords(mMessageHeaderView, rect);
179
180        // If we have scrolled further than the distance from the top of the scrollView to the top
181        // of the message header, then the message header is at least partially ofscreen. As soon
182        // as the message header goes partially offscreen we need to display the snap header.
183        if (y > rect.top) {
184            mSnapHeaderView.setVisibility(View.VISIBLE);
185        } else {
186            mSnapHeaderView.setVisibility(View.GONE);
187        }
188    }
189
190    /**
191     * Populate the adapter with overlay views (message headers, super-collapsed
192     * blocks, a conversation header), and return an HTML document with spacer
193     * divs inserted for all overlays.
194     */
195    public void renderMessage(ConversationMessage message) {
196        mMessage = message;
197
198        final boolean alwaysShowImages = mCallbacks.shouldAlwaysShowImages();
199        mWebView.getSettings().setBlockNetworkImage(
200                !alwaysShowImages && !mMessage.alwaysShowImages);
201
202        // Add formatting to message body
203        // At this point, only adds margins.
204        StringBuilder dataBuilder = new StringBuilder(
205                String.format(BEGIN_HTML, mSideMarginInWebPx));
206        dataBuilder.append(mMessage.getBodyAsHtml());
207        dataBuilder.append(END_HTML);
208
209        mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(), dataBuilder.toString(),
210                "text/html", "utf-8", null);
211        final MessageHeaderItem item = ConversationViewAdapter.newMessageHeaderItem(
212                null, mDateBuilder, mMessage, true, mMessage.alwaysShowImages);
213        // Clear out the old info from the header before (re)binding
214        mMessageHeaderView.unbind();
215        mMessageHeaderView.bind(item, false);
216
217        mSnapHeaderView.unbind();
218        mSnapHeaderView.bind(item, false);
219
220        if (mMessage.hasAttachments) {
221            mMessageFooterView.setVisibility(View.VISIBLE);
222            mMessageFooterView.bind(item, false);
223        }
224    }
225
226    public ConversationMessage getMessage() {
227        return mMessage;
228    }
229
230    public ConversationViewHeader getConversationHeaderView() {
231        return mConversationHeaderView;
232    }
233
234    public void dismissLoadingStatus() {
235        mProgressController.dismissLoadingStatus();
236    }
237
238    public void setSubject(String subject) {
239        mConversationHeaderView.setSubject(subject);
240    }
241
242    public void printMessage() {
243        final Conversation conversation = mMessage.getConversation();
244        PrintUtils.printMessage(mCallbacks.getFragment().getActivity(), mMessage,
245                conversation != null ? conversation.subject : mMessage.subject,
246                mCallbacks.getAddressCache(), mCallbacks.getBaseUri(), false /* useJavascript */);
247
248    }
249
250    public BidiFormatter getBidiFormatter() {
251        return mBidiFormatter;
252    }
253
254    // Start MessageHeaderViewCallbacks implementations
255
256    @Override
257    public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight) {
258        // Do nothing.
259    }
260
261    @Override
262    public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight,
263            int topBorderHeight, int bottomBorderHeight) {
264        // Do nothing.
265    }
266
267    @Override
268    public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightBefore) {
269        // Do nothing.
270    }
271
272    @Override
273    public void showExternalResources(final Message msg) {
274        mWebView.getSettings().setBlockNetworkImage(false);
275    }
276
277    @Override
278    public void showExternalResources(final String rawSenderAddress) {
279        mWebView.getSettings().setBlockNetworkImage(false);
280    }
281
282    @Override
283    public boolean supportsMessageTransforms() {
284        return false;
285    }
286
287    @Override
288    public String getMessageTransforms(final Message msg) {
289        return null;
290    }
291
292    @Override
293    public FragmentManager getFragmentManager() {
294        return mCallbacks.getFragment().getFragmentManager();
295    }
296
297    // End MessageHeaderViewCallbacks implementations
298}
299