SecureConversationViewController.java revision f8e065aab2cbff4dee86cc931a4d9c88dec37345
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 // TODO - re-enable when dogfooders howl 186// if (y > rect.top) { 187// mSnapHeaderView.setVisibility(View.VISIBLE); 188// } else { 189 mSnapHeaderView.setVisibility(View.GONE); 190// } 191 } 192 193 /** 194 * Populate the adapter with overlay views (message headers, super-collapsed 195 * blocks, a conversation header), and return an HTML document with spacer 196 * divs inserted for all overlays. 197 */ 198 public void renderMessage(ConversationMessage message) { 199 mMessage = message; 200 201 final boolean alwaysShowImages = mCallbacks.shouldAlwaysShowImages(); 202 mWebView.getSettings().setBlockNetworkImage( 203 !alwaysShowImages && !mMessage.alwaysShowImages); 204 205 // Add formatting to message body 206 // At this point, only adds margins. 207 StringBuilder dataBuilder = new StringBuilder( 208 String.format(BEGIN_HTML, mSideMarginInWebPx)); 209 dataBuilder.append(mMessage.getBodyAsHtml()); 210 dataBuilder.append(END_HTML); 211 212 mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(), dataBuilder.toString(), 213 "text/html", "utf-8", null); 214 final MessageHeaderItem item = ConversationViewAdapter.newMessageHeaderItem( 215 null, mDateBuilder, mMessage, true, mMessage.alwaysShowImages); 216 // Clear out the old info from the header before (re)binding 217 mMessageHeaderView.unbind(); 218 mMessageHeaderView.bind(item, false); 219 220 mSnapHeaderView.unbind(); 221 mSnapHeaderView.bind(item, false); 222 223 if (mMessage.hasAttachments) { 224 mMessageFooterView.setVisibility(View.VISIBLE); 225 mMessageFooterView.bind( 226 item, ConversationViewAdapter.newMessageFooterItem(null, item), false); 227 } 228 } 229 230 public ConversationMessage getMessage() { 231 return mMessage; 232 } 233 234 public ConversationViewHeader getConversationHeaderView() { 235 return mConversationHeaderView; 236 } 237 238 public void dismissLoadingStatus() { 239 mProgressController.dismissLoadingStatus(); 240 } 241 242 public void setSubject(String subject) { 243 mConversationHeaderView.setSubject(subject); 244 } 245 246 public void printMessage() { 247 final Conversation conversation = mMessage.getConversation(); 248 PrintUtils.printMessage(mCallbacks.getFragment().getActivity(), mMessage, 249 conversation != null ? conversation.subject : mMessage.subject, 250 mCallbacks.getAddressCache(), mCallbacks.getBaseUri(), false /* useJavascript */); 251 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 // Do nothing. 264 } 265 266 @Override 267 public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightBefore) { 268 // Do nothing. 269 } 270 271 @Override 272 public void showExternalResources(final Message msg) { 273 mWebView.getSettings().setBlockNetworkImage(false); 274 } 275 276 @Override 277 public void showExternalResources(final String rawSenderAddress) { 278 mWebView.getSettings().setBlockNetworkImage(false); 279 } 280 281 @Override 282 public boolean supportsMessageTransforms() { 283 return false; 284 } 285 286 @Override 287 public String getMessageTransforms(final Message msg) { 288 return null; 289 } 290 291 @Override 292 public boolean isSecure() { 293 return true; 294 } 295 296 @Override 297 public FragmentManager getFragmentManager() { 298 return mCallbacks.getFragment().getFragmentManager(); 299 } 300 301 // End MessageHeaderViewCallbacks implementations 302 303 // START MessageFooterCallbacks 304 305 @Override 306 public void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight) { 307 // Do nothing. 308 } 309 310 @Override 311 public MessageFooterView getViewForItem(MessageFooterItem item) { 312 return mMessageFooterView; 313 } 314 315 @Override 316 public int getUpdatedHeight(MessageFooterItem item) { 317 return 0; // should never get called since we'll always have a footer view 318 } 319 320 // END MessageFooterCallbacks 321} 322