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