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