blob: eb56bf93030f795b90be399482edf59e08d5ae0a [file] [log] [blame]
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -07001/*
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.browse;
19
Tony Mantlercd10aee2014-01-24 15:04:43 -080020import android.app.Activity;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070021import android.app.Fragment;
22import android.app.LoaderManager;
Andrew Sapperstein7638a102013-06-25 13:54:52 -070023import android.content.CursorLoader;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070024import android.content.Loader;
Andrew Sapperstein7638a102013-06-25 13:54:52 -070025import android.database.Cursor;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070026import android.net.Uri;
27import android.os.Bundle;
28import android.os.Handler;
Andrew Sapperstein7638a102013-06-25 13:54:52 -070029import android.provider.OpenableColumns;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070030import android.view.LayoutInflater;
Andrew Sappersteinf59d01c2014-02-20 10:27:06 -080031import android.view.Menu;
32import android.view.MenuInflater;
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -070033import android.view.MenuItem;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070034import android.view.View;
35import android.view.ViewGroup;
36import android.webkit.WebView;
Tony Mantlercd10aee2014-01-24 15:04:43 -080037import android.widget.Toast;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070038
Tony Mantler821e5782014-01-06 15:33:43 -080039import com.android.emailcommon.mail.Address;
Andrew Sapperstein7638a102013-06-25 13:54:52 -070040import com.android.mail.R;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070041import com.android.mail.providers.Account;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070042import com.android.mail.ui.AbstractConversationWebViewClient;
43import com.android.mail.ui.ContactLoaderCallbacks;
44import com.android.mail.ui.SecureConversationViewController;
45import com.android.mail.ui.SecureConversationViewControllerCallbacks;
Paul Westbrook4b23dca2013-08-12 16:55:02 -070046import com.android.mail.utils.LogTag;
47import com.android.mail.utils.LogUtils;
Andrew Sappersteinf59d01c2014-02-20 10:27:06 -080048import com.android.mail.utils.Utils;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070049import com.google.common.collect.ImmutableList;
50import com.google.common.collect.Sets;
51
52import java.util.Collections;
53import java.util.HashMap;
54import java.util.List;
55import java.util.Map;
56import java.util.Set;
57
58/**
59 * Fragment that is used to view EML files. It is mostly stubs
60 * that calls {@link SecureConversationViewController} to do most
61 * of the rendering work.
62 */
63public class EmlMessageViewFragment extends Fragment
Andrew Sapperstein7638a102013-06-25 13:54:52 -070064 implements SecureConversationViewControllerCallbacks {
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070065 private static final String ARG_EML_FILE_URI = "eml_file_uri";
66 private static final String BASE_URI = "x-thread://message/rfc822/";
Andrew Sapperstein7638a102013-06-25 13:54:52 -070067
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070068 private static final int MESSAGE_LOADER = 0;
69 private static final int CONTACT_LOADER = 1;
Andrew Sapperstein7638a102013-06-25 13:54:52 -070070 private static final int FILENAME_LOADER = 2;
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070071
Paul Westbrook4b23dca2013-08-12 16:55:02 -070072 private static final String LOG_TAG = LogTag.getLogTag();
73
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070074 private final Handler mHandler = new Handler();
75
76 private EmlWebViewClient mWebViewClient;
77 private SecureConversationViewController mViewController;
78 private ContactLoaderCallbacks mContactLoaderCallbacks;
79
Andrew Sapperstein7638a102013-06-25 13:54:52 -070080 private final MessageLoadCallbacks mMessageLoadCallbacks = new MessageLoadCallbacks();
81 private final FilenameLoadCallbacks mFilenameLoadCallbacks = new FilenameLoadCallbacks();
82
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070083 private Uri mEmlFileUri;
84
Tony Mantlercd10aee2014-01-24 15:04:43 -080085 private boolean mMessageLoadFailed;
86
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -070087 /**
88 * Cache of email address strings to parsed Address objects.
89 * <p>
90 * Remember to synchronize on the map when reading or writing to this cache, because some
91 * instances use it off the UI thread (e.g. from WebView).
92 */
93 protected final Map<String, Address> mAddressCache = Collections.synchronizedMap(
94 new HashMap<String, Address>());
95
96 private class EmlWebViewClient extends AbstractConversationWebViewClient {
97 public EmlWebViewClient(Account account) {
98 super(account);
99 }
100
101 @Override
102 public void onPageFinished(WebView view, String url) {
Paul Westbrook4b23dca2013-08-12 16:55:02 -0700103 // Ignore unsafe calls made after a fragment is detached from an activity.
104 // This method needs to, for example, get at the loader manager, which needs
105 // the fragment to be added.
106 if (!isAdded()) {
107 LogUtils.d(LOG_TAG, "ignoring EMLVF.onPageFinished, url=%s fragment=%s", url,
108 EmlMessageViewFragment.this);
109 return;
110 }
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700111 mViewController.dismissLoadingStatus();
112
113 final Set<String> emailAddresses = Sets.newHashSet();
114 final List<Address> cacheCopy;
115 synchronized (mAddressCache) {
116 cacheCopy = ImmutableList.copyOf(mAddressCache.values());
117 }
118 for (Address addr : cacheCopy) {
119 emailAddresses.add(addr.getAddress());
120 }
121 final ContactLoaderCallbacks callbacks = getContactInfoSource();
122 callbacks.setSenders(emailAddresses);
123 getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
124 }
Tony Mantlercd10aee2014-01-24 15:04:43 -0800125 }
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700126
127 /**
128 * Creates a new instance of {@link EmlMessageViewFragment},
129 * initialized to display an eml file from the specified {@link Uri}.
130 */
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700131 public static EmlMessageViewFragment newInstance(Uri emlFileUri, Uri accountUri) {
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700132 EmlMessageViewFragment f = new EmlMessageViewFragment();
Andrew Sappersteinf59d01c2014-02-20 10:27:06 -0800133 Bundle args = new Bundle(1);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700134 args.putParcelable(ARG_EML_FILE_URI, emlFileUri);
135 f.setArguments(args);
136 return f;
137 }
138
139 /**
140 * Constructor needs to be public to handle orientation changes and activity
141 * lifecycle events.
142 */
Paul Westbrookf0ea4842013-08-13 16:41:18 -0700143 public EmlMessageViewFragment() {}
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700144
145 @Override
146 public void onCreate(Bundle savedState) {
147 super.onCreate(savedState);
148
149 Bundle args = getArguments();
150 mEmlFileUri = args.getParcelable(ARG_EML_FILE_URI);
151
152 mWebViewClient = new EmlWebViewClient(null);
153 mViewController = new SecureConversationViewController(this);
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700154
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -0700155 setHasOptionsMenu(true);
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700156 getActivity().getActionBar().setTitle(R.string.attached_message);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700157 }
158
159 @Override
160 public View onCreateView(LayoutInflater inflater, ViewGroup container,
161 Bundle savedInstanceState) {
162 return mViewController.onCreateView(inflater, container, savedInstanceState);
163 }
164
165 @Override
166 public void onActivityCreated(Bundle savedInstanceState) {
167 super.onActivityCreated(savedInstanceState);
Tony Mantlercd10aee2014-01-24 15:04:43 -0800168 if (mMessageLoadFailed) {
169 bailOutOnLoadError();
170 return;
171 }
Andy Huangb622d2b2013-06-12 13:47:17 -0700172 mWebViewClient.setActivity(getActivity());
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700173 mViewController.onActivityCreated(savedInstanceState);
174 }
175
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -0700176 @Override
Andrew Sapperstein381c3222014-04-20 12:23:57 -0700177 public void onDestroyView() {
178 super.onDestroyView();
179 mViewController.onDestroyView();
180 }
181
182 @Override
Andrew Sappersteinf59d01c2014-02-20 10:27:06 -0800183 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
184 if (Utils.isRunningKitkatOrLater()) {
185 inflater.inflate(R.menu.eml_fragment_menu, menu);
186 }
187 }
188
189 @Override
Andrew Sapperstein562c5ba2013-10-09 18:31:50 -0700190 public boolean onOptionsItemSelected(MenuItem item) {
191 final int itemId = item.getItemId();
192 if (itemId == R.id.print_message) {
193 mViewController.printMessage();
194 } else {
195 return super.onOptionsItemSelected(item);
196 }
197
198 return true;
199 }
200
Tony Mantlercd10aee2014-01-24 15:04:43 -0800201 private void bailOutOnLoadError() {
202 final Activity activity = getActivity();
203 Toast.makeText(activity, R.string.eml_loader_error_toast, Toast.LENGTH_LONG).show();
204 activity.finish();
205 }
206
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700207 // Start SecureConversationViewControllerCallbacks
208
209 @Override
210 public Handler getHandler() {
211 return mHandler;
212 }
213
214 @Override
215 public AbstractConversationWebViewClient getWebViewClient() {
216 return mWebViewClient;
217 }
218
219 @Override
220 public Fragment getFragment() {
221 return this;
222 }
223
224 @Override
225 public void setupConversationHeaderView(ConversationViewHeader headerView) {
226 // DO NOTHING
227 }
228
229 @Override
230 public boolean isViewVisibleToUser() {
231 return true;
232 }
233
234 @Override
235 public ContactLoaderCallbacks getContactInfoSource() {
236 if (mContactLoaderCallbacks == null) {
237 mContactLoaderCallbacks = new ContactLoaderCallbacks(getActivity());
238 }
239 return mContactLoaderCallbacks;
240 }
241
242 @Override
243 public ConversationAccountController getConversationAccountController() {
Andrew Sapperstein3de76ec2013-07-16 12:08:15 -0700244 return (EmlViewerActivity) getActivity();
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700245 }
246
247 @Override
248 public Map<String, Address> getAddressCache() {
249 return mAddressCache;
250 }
251
252 @Override
253 public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView) {
254 // DO NOTHING
255 }
256
257 @Override
258 public void startMessageLoader() {
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700259 final LoaderManager manager = getLoaderManager();
260 manager.initLoader(MESSAGE_LOADER, null, mMessageLoadCallbacks);
261 manager.initLoader(FILENAME_LOADER, null, mFilenameLoadCallbacks);
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700262 }
263
264 @Override
265 public String getBaseUri() {
266 return BASE_URI;
267 }
268
269 @Override
270 public boolean isViewOnlyMode() {
271 return true;
272 }
273
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700274 @Override
Andrew Sapperstein8ec43e82013-12-17 18:27:55 -0800275 public boolean shouldAlwaysShowImages() {
276 return false;
277 }
278
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700279 // End SecureConversationViewControllerCallbacks
280
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700281 private class MessageLoadCallbacks
282 implements LoaderManager.LoaderCallbacks<ConversationMessage> {
283 @Override
284 public Loader<ConversationMessage> onCreateLoader(int id, Bundle args) {
285 switch (id) {
286 case MESSAGE_LOADER:
287 return new EmlMessageLoader(getActivity(), mEmlFileUri);
288 default:
289 return null;
290 }
291 }
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700292
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700293 @Override
294 public void onLoadFinished(Loader<ConversationMessage> loader, ConversationMessage data) {
Tony Mantlercd10aee2014-01-24 15:04:43 -0800295 if (data == null) {
296 final Activity activity = getActivity();
297 if (activity != null) {
298 bailOutOnLoadError();
299 } else {
300 mMessageLoadFailed = true;
301 }
302 return;
303 }
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700304 mViewController.setSubject(data.subject);
305 mViewController.renderMessage(data);
306 }
307
308 @Override
309 public void onLoaderReset(Loader<ConversationMessage> loader) {
310 // Do nothing
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700311 }
312 }
313
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700314 private class FilenameLoadCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
315 @Override
316 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
317 switch (id) {
318 case FILENAME_LOADER:
319 return new CursorLoader(getActivity(), mEmlFileUri,
Andrew Sappersteinc9524ef2013-07-11 14:11:46 -0700320 new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE },
321 null, null, null);
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700322 default:
323 return null;
324 }
325 }
326
327 @Override
328 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Andrew Sapperstein459e2e72013-09-26 12:01:31 -0700329 if (data == null || !data.moveToFirst()) {
330 return;
331 }
332
Andrew Sapperstein7638a102013-06-25 13:54:52 -0700333 getActivity().getActionBar().setSubtitle(
334 data.getString(data.getColumnIndex(OpenableColumns.DISPLAY_NAME)));
335 }
336
337 @Override
338 public void onLoaderReset(Loader<Cursor> loader) {
339 }
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700340 }
341
Andrew Sapperstein4ddda2f2013-06-10 11:15:38 -0700342}