Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2012 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.content.Loader;
     22 import android.net.Uri;
     23 import android.os.Bundle;
     24 import android.view.LayoutInflater;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.webkit.WebResourceResponse;
     28 import android.webkit.WebView;
     29 
     30 import com.android.emailcommon.mail.Address;
     31 import com.android.mail.browse.ConversationAccountController;
     32 import com.android.mail.browse.ConversationMessage;
     33 import com.android.mail.browse.ConversationViewHeader;
     34 import com.android.mail.browse.MessageCursor;
     35 import com.android.mail.browse.MessageHeaderView;
     36 import com.android.mail.compose.ComposeActivity;
     37 import com.android.mail.content.ObjectCursor;
     38 import com.android.mail.providers.Account;
     39 import com.android.mail.providers.Conversation;
     40 import com.android.mail.utils.LogTag;
     41 import com.android.mail.utils.LogUtils;
     42 import com.google.common.collect.ImmutableList;
     43 import com.google.common.collect.Sets;
     44 
     45 import java.util.HashSet;
     46 import java.util.List;
     47 import java.util.Map;
     48 import java.util.Set;
     49 
     50 public class SecureConversationViewFragment extends AbstractConversationViewFragment
     51         implements SecureConversationViewControllerCallbacks {
     52     private static final String LOG_TAG = LogTag.getLogTag();
     53 
     54     private SecureConversationViewController mViewController;
     55 
     56     private class SecureConversationWebViewClient extends AbstractConversationWebViewClient {
     57         public SecureConversationWebViewClient(Account account) {
     58             super(account);
     59         }
     60 
     61         @Override
     62         public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
     63             // try to load the url assuming it is a cid url
     64             final Uri uri = Uri.parse(url);
     65             final WebResourceResponse response = loadCIDUri(uri, mViewController.getMessage());
     66             if (response != null) {
     67                 return response;
     68             }
     69 
     70             // otherwise, attempt the default handling
     71             return super.shouldInterceptRequest(view, url);
     72         }
     73 
     74         @Override
     75         public void onPageFinished(WebView view, String url) {
     76             // Ignore unsafe calls made after a fragment is detached from an activity.
     77             // This method needs to, for example, get at the loader manager, which needs
     78             // the fragment to be added.
     79             if (!isAdded()) {
     80                 LogUtils.d(LOG_TAG, "ignoring SCVF.onPageFinished, url=%s fragment=%s", url,
     81                         SecureConversationViewFragment.this);
     82                 return;
     83             }
     84 
     85             if (isUserVisible()) {
     86                 onConversationSeen();
     87             }
     88 
     89             mViewController.dismissLoadingStatus();
     90 
     91             final Set<String> emailAddresses = Sets.newHashSet();
     92             final List<Address> cacheCopy;
     93             synchronized (mAddressCache) {
     94                 cacheCopy = ImmutableList.copyOf(mAddressCache.values());
     95             }
     96             for (Address addr : cacheCopy) {
     97                 emailAddresses.add(addr.getAddress());
     98             }
     99             final ContactLoaderCallbacks callbacks = getContactInfoSource();
    100             callbacks.setSenders(emailAddresses);
    101             getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
    102         }
    103     }
    104 
    105     /**
    106      * Creates a new instance of {@link ConversationViewFragment}, initialized
    107      * to display a conversation with other parameters inherited/copied from an
    108      * existing bundle, typically one created using {@link #makeBasicArgs}.
    109      */
    110     public static SecureConversationViewFragment newInstance(Bundle existingArgs,
    111             Conversation conversation) {
    112         SecureConversationViewFragment f = new SecureConversationViewFragment();
    113         Bundle args = new Bundle(existingArgs);
    114         args.putParcelable(ARG_CONVERSATION, conversation);
    115         f.setArguments(args);
    116         return f;
    117     }
    118 
    119     /**
    120      * Constructor needs to be public to handle orientation changes and activity
    121      * lifecycle events.
    122      */
    123     public SecureConversationViewFragment() {}
    124 
    125     @Override
    126     public void onCreate(Bundle savedState) {
    127         super.onCreate(savedState);
    128 
    129         mWebViewClient = new SecureConversationWebViewClient(mAccount);
    130         mViewController = new SecureConversationViewController(this);
    131     }
    132 
    133     @Override
    134     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    135             Bundle savedInstanceState) {
    136         return mViewController.onCreateView(inflater, container, savedInstanceState);
    137     }
    138 
    139     @Override
    140     public void onActivityCreated(Bundle savedInstanceState) {
    141         super.onActivityCreated(savedInstanceState);
    142         mViewController.onActivityCreated(savedInstanceState);
    143     }
    144 
    145     // Start implementations of SecureConversationViewControllerCallbacks
    146 
    147     @Override
    148     public Fragment getFragment() {
    149         return this;
    150     }
    151 
    152     @Override
    153     public AbstractConversationWebViewClient getWebViewClient() {
    154         return mWebViewClient;
    155     }
    156 
    157     @Override
    158     public void setupConversationHeaderView(ConversationViewHeader headerView) {
    159         headerView.setCallbacks(this, this, getListController());
    160         headerView.setFolders(mConversation);
    161         headerView.setSubject(mConversation.subject);
    162         headerView.setStarred(mConversation.starred);
    163     }
    164 
    165     @Override
    166     public boolean isViewVisibleToUser() {
    167         return isUserVisible();
    168     }
    169 
    170     @Override
    171     public ConversationAccountController getConversationAccountController() {
    172         return this;
    173     }
    174 
    175     @Override
    176     public Map<String, Address> getAddressCache() {
    177         return mAddressCache;
    178     }
    179 
    180     @Override
    181     public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView) {
    182         messageHeaderView.setVeiledMatcher(
    183                 ((ControllableActivity) getActivity()).getAccountController()
    184                         .getVeiledAddressMatcher());
    185     }
    186 
    187     @Override
    188     public void startMessageLoader() {
    189         getLoaderManager().initLoader(MESSAGE_LOADER, null, getMessageLoaderCallbacks());
    190     }
    191 
    192     @Override
    193     public String getBaseUri() {
    194         return mBaseUri;
    195     }
    196 
    197     @Override
    198     public boolean isViewOnlyMode() {
    199         return false;
    200     }
    201 
    202     // End implementations of SecureConversationViewControllerCallbacks
    203 
    204     @Override
    205     protected void markUnread() {
    206         super.markUnread();
    207         // Ignore unsafe calls made after a fragment is detached from an activity
    208         final ControllableActivity activity = (ControllableActivity) getActivity();
    209         final ConversationMessage message = mViewController.getMessage();
    210         if (activity == null || mConversation == null || message == null) {
    211             LogUtils.w(LOG_TAG, "ignoring markUnread for conv=%s",
    212                     mConversation != null ? mConversation.id : 0);
    213             return;
    214         }
    215         final HashSet<Uri> uris = new HashSet<Uri>();
    216         uris.add(message.uri);
    217         activity.getConversationUpdater().markConversationMessagesUnread(mConversation, uris,
    218                 mViewState.getConversationInfo());
    219     }
    220 
    221     @Override
    222     public void onAccountChanged(Account newAccount, Account oldAccount) {
    223         renderMessage(getMessageCursor());
    224     }
    225 
    226     @Override
    227     public void onConversationViewHeaderHeightChange(int newHeight) {
    228         // Do nothing.
    229     }
    230 
    231     @Override
    232     public void onUserVisibleHintChanged() {
    233         if (mActivity == null) {
    234             return;
    235         }
    236         if (isUserVisible()) {
    237             onConversationSeen();
    238         }
    239     }
    240 
    241     @Override
    242     protected void onMessageCursorLoadFinished(Loader<ObjectCursor<ConversationMessage>> loader,
    243             MessageCursor newCursor, MessageCursor oldCursor) {
    244         renderMessage(newCursor);
    245     }
    246 
    247     private void renderMessage(MessageCursor newCursor) {
    248         // ignore cursors that are still loading results
    249         if (newCursor == null || !newCursor.isLoaded()) {
    250             LogUtils.i(LOG_TAG, "CONV RENDER: existing cursor is null, rendering from scratch");
    251             return;
    252         }
    253         if (mActivity == null || mActivity.isFinishing()) {
    254             // Activity is finishing, just bail.
    255             return;
    256         }
    257         if (!newCursor.moveToFirst()) {
    258             LogUtils.e(LOG_TAG, "unable to open message cursor");
    259             return;
    260         }
    261 
    262         mViewController.renderMessage(newCursor.getMessage());
    263     }
    264 
    265     @Override
    266     public void onConversationUpdated(Conversation conv) {
    267         final ConversationViewHeader headerView = mViewController.getConversationHeaderView();
    268         if (headerView != null) {
    269             headerView.onConversationUpdated(conv);
    270         }
    271     }
    272 
    273     // Need this stub here
    274     @Override
    275     public boolean supportsMessageTransforms() {
    276         return false;
    277     }
    278 
    279     /**
    280      * Users are expected to use the Print item in the Message overflow menu to print the single
    281      * message.
    282      *
    283      * @return {@code false} because Print and Print All menu items are never shown in EMail.
    284      */
    285     @Override
    286     protected boolean shouldShowPrintInOverflow() {
    287         return false;
    288     }
    289 
    290     @Override
    291     protected void printConversation() {
    292         mViewController.printMessage();
    293     }
    294 
    295     @Override
    296     protected void handleReply() {
    297         final ConversationMessage msg = mViewController.getMessage();
    298         if (msg != null) {
    299             ComposeActivity.reply(getActivity(), mAccount, msg);
    300         }
    301     }
    302 
    303     @Override
    304     protected void handleReplyAll() {
    305         final ConversationMessage msg = mViewController.getMessage();
    306         if (msg != null) {
    307             ComposeActivity.replyAll(getActivity(), mAccount, msg);
    308         }
    309     }
    310 }
    311