Home | History | Annotate | Download | only in browse
      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.browse;
     19 
     20 import android.app.Fragment;
     21 import android.app.LoaderManager;
     22 import android.content.CursorLoader;
     23 import android.content.Loader;
     24 import android.database.Cursor;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.provider.OpenableColumns;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.webkit.WebView;
     33 
     34 import com.android.mail.R;
     35 import com.android.mail.providers.Account;
     36 import com.android.mail.providers.Address;
     37 import com.android.mail.ui.AbstractConversationWebViewClient;
     38 import com.android.mail.ui.ContactLoaderCallbacks;
     39 import com.android.mail.ui.SecureConversationViewController;
     40 import com.android.mail.ui.SecureConversationViewControllerCallbacks;
     41 import com.android.mail.utils.LogTag;
     42 import com.android.mail.utils.LogUtils;
     43 import com.google.common.collect.ImmutableList;
     44 import com.google.common.collect.Sets;
     45 
     46 import java.util.Collections;
     47 import java.util.HashMap;
     48 import java.util.List;
     49 import java.util.Map;
     50 import java.util.Set;
     51 
     52 /**
     53  * Fragment that is used to view EML files. It is mostly stubs
     54  * that calls {@link SecureConversationViewController} to do most
     55  * of the rendering work.
     56  */
     57 public class EmlMessageViewFragment extends Fragment
     58         implements SecureConversationViewControllerCallbacks {
     59     private static final String ARG_EML_FILE_URI = "eml_file_uri";
     60     private static final String ARG_ACCOUNT_URI = "account_uri";
     61     private static final String BASE_URI = "x-thread://message/rfc822/";
     62 
     63     private static final int MESSAGE_LOADER = 0;
     64     private static final int CONTACT_LOADER = 1;
     65     private static final int FILENAME_LOADER = 2;
     66 
     67     private static final String LOG_TAG = LogTag.getLogTag();
     68 
     69     private final Handler mHandler = new Handler();
     70 
     71     private EmlWebViewClient mWebViewClient;
     72     private SecureConversationViewController mViewController;
     73     private ContactLoaderCallbacks mContactLoaderCallbacks;
     74 
     75     private final MessageLoadCallbacks mMessageLoadCallbacks = new MessageLoadCallbacks();
     76     private final FilenameLoadCallbacks mFilenameLoadCallbacks = new FilenameLoadCallbacks();
     77 
     78     private Uri mEmlFileUri;
     79     private Uri mAccountUri;
     80 
     81     /**
     82      * Cache of email address strings to parsed Address objects.
     83      * <p>
     84      * Remember to synchronize on the map when reading or writing to this cache, because some
     85      * instances use it off the UI thread (e.g. from WebView).
     86      */
     87     protected final Map<String, Address> mAddressCache = Collections.synchronizedMap(
     88             new HashMap<String, Address>());
     89 
     90     private class EmlWebViewClient extends AbstractConversationWebViewClient {
     91         public EmlWebViewClient(Account account) {
     92             super(account);
     93         }
     94 
     95         @Override
     96         public void onPageFinished(WebView view, String url) {
     97             // Ignore unsafe calls made after a fragment is detached from an activity.
     98             // This method needs to, for example, get at the loader manager, which needs
     99             // the fragment to be added.
    100             if (!isAdded()) {
    101                 LogUtils.d(LOG_TAG, "ignoring EMLVF.onPageFinished, url=%s fragment=%s", url,
    102                         EmlMessageViewFragment.this);
    103                 return;
    104             }
    105             mViewController.dismissLoadingStatus();
    106 
    107             final Set<String> emailAddresses = Sets.newHashSet();
    108             final List<Address> cacheCopy;
    109             synchronized (mAddressCache) {
    110                 cacheCopy = ImmutableList.copyOf(mAddressCache.values());
    111             }
    112             for (Address addr : cacheCopy) {
    113                 emailAddresses.add(addr.getAddress());
    114             }
    115             final ContactLoaderCallbacks callbacks = getContactInfoSource();
    116             callbacks.setSenders(emailAddresses);
    117             getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
    118         }
    119     };
    120 
    121     /**
    122      * Creates a new instance of {@link EmlMessageViewFragment},
    123      * initialized to display an eml file from the specified {@link Uri}.
    124      */
    125     public static EmlMessageViewFragment newInstance(Uri emlFileUri, Uri accountUri) {
    126         EmlMessageViewFragment f = new EmlMessageViewFragment();
    127         Bundle args = new Bundle();
    128         args.putParcelable(ARG_EML_FILE_URI, emlFileUri);
    129         args.putParcelable(ARG_ACCOUNT_URI, accountUri);
    130         f.setArguments(args);
    131         return f;
    132     }
    133 
    134     /**
    135      * Constructor needs to be public to handle orientation changes and activity
    136      * lifecycle events.
    137      */
    138     public EmlMessageViewFragment() {}
    139 
    140     @Override
    141     public void onCreate(Bundle savedState) {
    142         super.onCreate(savedState);
    143 
    144         Bundle args = getArguments();
    145         mEmlFileUri = args.getParcelable(ARG_EML_FILE_URI);
    146         mAccountUri = args.getParcelable(ARG_ACCOUNT_URI);
    147 
    148         mWebViewClient = new EmlWebViewClient(null);
    149         mViewController = new SecureConversationViewController(this);
    150 
    151         getActivity().getActionBar().setTitle(R.string.attached_message);
    152     }
    153 
    154     @Override
    155     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    156             Bundle savedInstanceState) {
    157         return mViewController.onCreateView(inflater, container, savedInstanceState);
    158     }
    159 
    160     @Override
    161     public void onActivityCreated(Bundle savedInstanceState) {
    162         super.onActivityCreated(savedInstanceState);
    163         mWebViewClient.setActivity(getActivity());
    164         mViewController.onActivityCreated(savedInstanceState);
    165     }
    166 
    167     // Start SecureConversationViewControllerCallbacks
    168 
    169     @Override
    170     public Handler getHandler() {
    171         return mHandler;
    172     }
    173 
    174     @Override
    175     public AbstractConversationWebViewClient getWebViewClient() {
    176         return mWebViewClient;
    177     }
    178 
    179     @Override
    180     public Fragment getFragment() {
    181         return this;
    182     }
    183 
    184     @Override
    185     public void setupConversationHeaderView(ConversationViewHeader headerView) {
    186         // DO NOTHING
    187     }
    188 
    189     @Override
    190     public boolean isViewVisibleToUser() {
    191         return true;
    192     }
    193 
    194     @Override
    195     public ContactLoaderCallbacks getContactInfoSource() {
    196         if (mContactLoaderCallbacks == null) {
    197             mContactLoaderCallbacks = new ContactLoaderCallbacks(getActivity());
    198         }
    199         return mContactLoaderCallbacks;
    200     }
    201 
    202     @Override
    203     public ConversationAccountController getConversationAccountController() {
    204         return (EmlViewerActivity) getActivity();
    205     }
    206 
    207     @Override
    208     public Map<String, Address> getAddressCache() {
    209         return mAddressCache;
    210     }
    211 
    212     @Override
    213     public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView) {
    214         // DO NOTHING
    215     }
    216 
    217     @Override
    218     public void startMessageLoader() {
    219         final LoaderManager manager = getLoaderManager();
    220         manager.initLoader(MESSAGE_LOADER, null, mMessageLoadCallbacks);
    221         manager.initLoader(FILENAME_LOADER, null, mFilenameLoadCallbacks);
    222     }
    223 
    224     @Override
    225     public String getBaseUri() {
    226         return BASE_URI;
    227     }
    228 
    229     @Override
    230     public boolean isViewOnlyMode() {
    231         return true;
    232     }
    233 
    234     @Override
    235     public Uri getAccountUri() {
    236         return mAccountUri;
    237     }
    238 
    239     // End SecureConversationViewControllerCallbacks
    240 
    241     private class MessageLoadCallbacks
    242             implements LoaderManager.LoaderCallbacks<ConversationMessage> {
    243         @Override
    244         public Loader<ConversationMessage> onCreateLoader(int id, Bundle args) {
    245             switch (id) {
    246                 case MESSAGE_LOADER:
    247                     return new EmlMessageLoader(getActivity(), mEmlFileUri);
    248                 default:
    249                     return null;
    250             }
    251         }
    252 
    253         @Override
    254         public void onLoadFinished(Loader<ConversationMessage> loader, ConversationMessage data) {
    255             mViewController.setSubject(data.subject);
    256             mViewController.renderMessage(data);
    257         }
    258 
    259         @Override
    260         public void onLoaderReset(Loader<ConversationMessage> loader) {
    261             // Do nothing
    262         }
    263     }
    264 
    265     private class FilenameLoadCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
    266         @Override
    267         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    268             switch (id) {
    269                 case FILENAME_LOADER:
    270                     return new CursorLoader(getActivity(), mEmlFileUri,
    271                             new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE },
    272                             null, null, null);
    273                 default:
    274                     return null;
    275             }
    276         }
    277 
    278         @Override
    279         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    280             if (data == null || !data.moveToFirst()) {
    281                 return;
    282             }
    283 
    284             getActivity().getActionBar().setSubtitle(
    285                     data.getString(data.getColumnIndex(OpenableColumns.DISPLAY_NAME)));
    286         }
    287 
    288         @Override
    289         public void onLoaderReset(Loader<Cursor> loader) {
    290         }
    291     }
    292 
    293 }
    294