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