1bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/*
2bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Copyright (C) 2010 The Android Open Source Project
3bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
4bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Licensed under the Apache License, Version 2.0 (the "License");
5bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * you may not use this file except in compliance with the License.
6bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * You may obtain a copy of the License at
7bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
8bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *      http://www.apache.org/licenses/LICENSE-2.0
9bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
10bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Unless required by applicable law or agreed to in writing, software
11bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * distributed under the License is distributed on an "AS IS" BASIS,
12bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * See the License for the specific language governing permissions and
14bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * limitations under the License.
15bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */
16bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
17bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpackage com.android.email.activity;
18bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
19bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.Activity;
20bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.DownloadManager;
21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.Fragment;
22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.LoaderManager.LoaderCallbacks;
23bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ActivityNotFoundException;
24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ContentResolver;
25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ContentUris;
26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.Context;
27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.Intent;
28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.Loader;
29bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.pm.PackageManager;
30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.res.Resources;
31bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.database.ContentObserver;
32bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.Bitmap;
33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.BitmapFactory;
34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.media.MediaScannerConnection;
35bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.net.Uri;
36bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Bundle;
37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Environment;
38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Handler;
39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.provider.ContactsContract;
40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.provider.ContactsContract.QuickContact;
41bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.text.SpannableStringBuilder;
42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.text.TextUtils;
43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.text.format.DateUtils;
44bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.util.Log;
45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.util.Patterns;
46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.LayoutInflater;
47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.View;
48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.ViewGroup;
49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.webkit.WebSettings;
50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.webkit.WebView;
51bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.webkit.WebViewClient;
52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.Button;
53bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.ImageView;
54bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.LinearLayout;
55bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.ProgressBar;
56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.TextView;
57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.AttachmentInfo;
59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Controller;
60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.ControllerResultUiThreadWrapper;
61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Email;
62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Preferences;
63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.R;
64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Throttle;
65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.mail.internet.EmailHtmlUtil;
66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.service.AttachmentDownloadService;
67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.Logging;
68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.mail.Address;
69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.mail.MessagingException;
70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.Account;
71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.EmailContent.Attachment;
72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.EmailContent.Body;
73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.EmailContent.Message;
74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.Mailbox;
75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.utility.AttachmentUtilities;
76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.utility.EmailAsyncTask;
77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.utility.Utility;
78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.google.common.collect.Maps;
79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport org.apache.commons.io.IOUtils;
81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.File;
83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.FileOutputStream;
84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.IOException;
85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.InputStream;
86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.OutputStream;
87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.Formatter;
88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.Map;
89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.regex.Matcher;
90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.regex.Pattern;
91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook// TODO Better handling of config changes.
93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook// - Retain the content; don't kick 3 async tasks every time
94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/**
96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Base class for {@link MessageViewFragment} and {@link MessageFileViewFragment}.
97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */
98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic abstract class MessageViewFragmentBase extends Fragment implements View.OnClickListener {
99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final String BUNDLE_KEY_CURRENT_TAB = "MessageViewFragmentBase.currentTab";
100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final String BUNDLE_KEY_PICTURE_LOADED = "MessageViewFragmentBase.pictureLoaded";
101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int PHOTO_LOADER_ID = 1;
102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected Context mContext;
103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // Regex that matches start of img tag. '<(?i)img\s+'.
105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final Pattern IMG_TAG_START_REGEX = Pattern.compile("<(?i)img\\s+");
106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // Regex that matches Web URL protocol part as case insensitive.
107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final Pattern WEB_URL_PROTOCOL = Pattern.compile("(?i)http|https://");
108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static int PREVIEW_ICON_WIDTH = 62;
110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static int PREVIEW_ICON_HEIGHT = 62;
111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // The different levels of zoom: read from the Preferences.
113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static String[] sZoomSizes = null;
114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mSubjectView;
116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mFromNameView;
117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mFromAddressView;
118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mDateTimeView;
119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mAddressesView;
120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private WebView mMessageContentView;
121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private LinearLayout mAttachments;
122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mTabSection;
123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ImageView mFromBadge;
124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ImageView mSenderPresenceView;
125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mMainView;
126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mLoadingProgress;
127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mDetailsCollapsed;
128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mDetailsExpanded;
129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mDetailsFilled;
130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mMessageTab;
132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mAttachmentTab;
133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mInviteTab;
134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // It is not really a tab, but looks like one of them.
135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mShowPicturesTab;
136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mAlwaysShowPicturesButton;
137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mAttachmentsScroll;
139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mInviteScroll;
140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private long mAccountId = Account.NO_ACCOUNT;
142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private long mMessageId = Message.NO_MESSAGE;
143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Message mMessage;
144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Controller mController;
146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ControllerResultUiThreadWrapper<ControllerResults> mControllerCallback;
147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // contains the HTML body. Is used by LoadAttachmentTask to display inline images.
149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // is null most of the time, is used transiently to pass info to LoadAttachementTask
150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String mHtmlTextRaw;
151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // contains the HTML content as set in WebView.
153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String mHtmlTextWebView;
154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mIsMessageLoadedForTest;
156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private MessageObserver mMessageObserver;
158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int CONTACT_STATUS_STATE_UNLOADED = 0;
160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int CONTACT_STATUS_STATE_UNLOADED_TRIGGERED = 1;
161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int CONTACT_STATUS_STATE_LOADED = 2;
162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mContactStatusState;
164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Uri mQuickContactLookupUri;
165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Flag for {@link #mTabFlags}: Message has attachment(s) */
167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_FLAGS_HAS_ATTACHMENT = 1;
168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Flag for {@link #mTabFlags}: Message contains invite.  This flag is only set by
171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * {@link MessageViewFragment}.
172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_FLAGS_HAS_INVITE = 2;
174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Flag for {@link #mTabFlags}: Message contains pictures */
176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_FLAGS_HAS_PICTURES = 4;
177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Flag for {@link #mTabFlags}: "Show pictures" has already been pressed */
179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_FLAGS_PICTURE_LOADED = 8;
180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Flags to control the tabs.
183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @see #updateTabs(int)
184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mTabFlags;
186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** # of attachments in the current message */
188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mAttachmentCount;
189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // Use (random) large values, to avoid confusion with TAB_FLAGS_*
191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_MESSAGE = 101;
192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_INVITE = 102;
193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected static final int TAB_ATTACHMENT = 103;
194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int TAB_NONE = 0;
195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Current tab */
197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mCurrentTab = TAB_NONE;
198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Tab that was selected in the previous activity instance.
200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Used to restore the current tab after screen rotation.
201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mRestoredTab = TAB_NONE;
203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mRestoredPictureLoaded;
205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public interface Callback {
209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when a link in a message is clicked.
211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *
212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param url link url that's clicked.
213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @return true if handled, false otherwise.
214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onUrlInMessageClicked(String url);
216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when the message specified doesn't exist, or is deleted/moved.
219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMessageNotExists();
221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Called when it starts loading a message. */
223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoadMessageStarted();
224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Called when it successfully finishes loading a message. */
226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoadMessageFinished();
227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Called when an error occurred during loading a message. */
229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoadMessageError(String errorMessage);
230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public static class EmptyCallback implements Callback {
233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static final Callback INSTANCE = new EmptyCallback();
234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override public void onLoadMessageError(String errorMessage) {}
235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override public void onLoadMessageFinished() {}
236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override public void onLoadMessageStarted() {}
237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override public void onMessageNotExists() {}
238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onUrlInMessageClicked(String url) {
240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return false;
241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Callback mCallback = EmptyCallback.INSTANCE;
245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onAttach(Activity activity) {
248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onAttach");
250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onAttach(activity);
252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onCreate(Bundle savedInstanceState) {
256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onCreate");
258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onCreate(savedInstanceState);
260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mContext = getActivity().getApplicationContext();
262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Initialize components, but don't "start" them.  Registering the controller callbacks
264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // and starting MessageObserver, should be done in onActivityCreated or later and be stopped
265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // in onDestroyView to prevent from getting callbacks when the fragment is in the back
266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // stack, but they'll start again when it's back from the back stack.
267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController = Controller.getInstance(mContext);
268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mControllerCallback = new ControllerResultUiThreadWrapper<ControllerResults>(
269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                new Handler(), new ControllerResults());
270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageObserver = new MessageObserver(new Handler(), mContext);
271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (savedInstanceState != null) {
273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            restoreInstanceState(savedInstanceState);
274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public View onCreateView(
279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
280bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onCreateView");
282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final View view = inflater.inflate(R.layout.message_view_fragment, container, false);
284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        cleanupDetachedViews();
286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSubjectView = (TextView) UiUtilities.getView(view, R.id.subject);
288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromNameView = (TextView) UiUtilities.getView(view, R.id.from_name);
289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromAddressView = (TextView) UiUtilities.getView(view, R.id.from_address);
290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAddressesView = (TextView) UiUtilities.getView(view, R.id.addresses);
291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDateTimeView = (TextView) UiUtilities.getView(view, R.id.datetime);
292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageContentView = (WebView) UiUtilities.getView(view, R.id.message_content);
293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachments = (LinearLayout) UiUtilities.getView(view, R.id.attachments);
294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mTabSection = UiUtilities.getView(view, R.id.message_tabs_section);
295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromBadge = (ImageView) UiUtilities.getView(view, R.id.badge);
296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSenderPresenceView = (ImageView) UiUtilities.getView(view, R.id.presence);
297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMainView = UiUtilities.getView(view, R.id.main_panel);
298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mLoadingProgress = UiUtilities.getView(view, R.id.loading_progress);
299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsCollapsed = UiUtilities.getView(view, R.id.sub_header_contents_collapsed);
300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsExpanded = UiUtilities.getView(view, R.id.sub_header_contents_expanded);
301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromNameView.setOnClickListener(this);
303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromAddressView.setOnClickListener(this);
304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromBadge.setOnClickListener(this);
305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSenderPresenceView.setOnClickListener(this);
306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageTab = UiUtilities.getView(view, R.id.show_message);
308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentTab = UiUtilities.getView(view, R.id.show_attachments);
309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mShowPicturesTab = UiUtilities.getView(view, R.id.show_pictures);
310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAlwaysShowPicturesButton = UiUtilities.getView(view, R.id.always_show_pictures_button);
311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Invite is only used in MessageViewFragment, but visibility is controlled here.
312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mInviteTab = UiUtilities.getView(view, R.id.show_invite);
313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageTab.setOnClickListener(this);
315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentTab.setOnClickListener(this);
316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mShowPicturesTab.setOnClickListener(this);
317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAlwaysShowPicturesButton.setOnClickListener(this);
318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mInviteTab.setOnClickListener(this);
319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsCollapsed.setOnClickListener(this);
320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsExpanded.setOnClickListener(this);
321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentsScroll = UiUtilities.getView(view, R.id.attachments_scroll);
323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mInviteScroll = UiUtilities.getView(view, R.id.invite_scroll);
324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        WebSettings webSettings = mMessageContentView.getSettings();
326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        boolean supportMultiTouch = mContext.getPackageManager()
327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        webSettings.setDisplayZoomControls(!supportMultiTouch);
329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        webSettings.setSupportZoom(true);
330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        webSettings.setBuiltInZoomControls(true);
331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageContentView.setWebViewClient(new CustomWebViewClient());
332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return view;
333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onActivityCreated(Bundle savedInstanceState) {
337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onActivityCreated");
339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onActivityCreated(savedInstanceState);
341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController.addResultCallback(mControllerCallback);
342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        resetView();
344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        new LoadMessageTask(true).executeParallel();
345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        UiUtilities.installFragment(this);
347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onStart() {
351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onStart");
353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onStart();
355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onResume() {
359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onResume");
361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onResume();
363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // We might have comes back from other full-screen activities.  If so, we need to update
365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // the attachment tab as system settings may have been updated that affect which
366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // options are available to the user.
367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateAttachmentTab();
368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onPause() {
372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onPause");
374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onPause();
376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onStop() {
380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onStop");
382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onStop();
384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDestroyView() {
388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDestroyView");
390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        UiUtilities.uninstallFragment(this);
392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController.removeResultCallback(mControllerCallback);
393bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        cancelAllTasks();
394bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
395bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // We should clean up the Webview here, but it can't release resources until it is
396bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // actually removed from the view tree.
397bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
398bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDestroyView();
399bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
400bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
401bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void cleanupDetachedViews() {
402bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // WebView cleanup must be done after it leaves the rendering tree, according to
403bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // its contract
404bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mMessageContentView != null) {
405bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView.destroy();
406bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView = null;
407bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
408bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
409bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
410bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
411bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDestroy() {
412bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
413bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDestroy");
414bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
415bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
416bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        cleanupDetachedViews();
417bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDestroy();
418bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
419bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
420bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
421bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDetach() {
422bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
423bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDetach");
424bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
425bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDetach();
426bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
427bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
428bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
429bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onSaveInstanceState(Bundle outState) {
430bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
431bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onSaveInstanceState");
432bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
433bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onSaveInstanceState(outState);
434bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        outState.putInt(BUNDLE_KEY_CURRENT_TAB, mCurrentTab);
435bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        outState.putBoolean(BUNDLE_KEY_PICTURE_LOADED, (mTabFlags & TAB_FLAGS_PICTURE_LOADED) != 0);
436bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
437bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
438bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void restoreInstanceState(Bundle state) {
439bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
440bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " restoreInstanceState");
441bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
442bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // At this point (in onCreate) no tabs are visible (because we don't know if the message has
443bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // an attachment or invite before loading it).  We just remember the tab here.
444bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // We'll make it current when the tab first becomes visible in updateTabs().
445bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mRestoredTab = state.getInt(BUNDLE_KEY_CURRENT_TAB);
446bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mRestoredPictureLoaded = state.getBoolean(BUNDLE_KEY_PICTURE_LOADED);
447bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
448bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
449bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void setCallback(Callback callback) {
450bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
451bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
452bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
453bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void cancelAllTasks() {
454bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageObserver.unregister();
455bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mTaskTracker.cancellAllInterrupt();
456bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
457bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
458bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected final Controller getController() {
459bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mController;
460bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
461bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
462bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected final Callback getCallback() {
463bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mCallback;
464bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
465bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
466bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public final Message getMessage() {
467bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mMessage;
468bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
469bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
470bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected final boolean isMessageOpen() {
471bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mMessage != null;
472bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
473bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
474bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
475bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Returns the account id of the current message, or -1 if unknown (message not open yet, or
476bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * viewing an EML message).
477bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
478bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public long getAccountId() {
479bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mAccountId;
480bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
481bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
482bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
483bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Show/hide the content.  We hide all the content (except for the bottom buttons) when loading,
484bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * to avoid flicker.
485bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
486bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showContent(boolean showContent, boolean showProgressWhenHidden) {
487bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mMainView, showContent);
488bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mLoadingProgress, !showContent && showProgressWhenHidden);
489bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
490bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
491bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // TODO: clean this up - most of this is not needed since the WebView and Fragment is not
492bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // reused for multiple messages.
493bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected void resetView() {
494bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        showContent(false, false);
495bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateTabs(0);
496bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        setCurrentTab(TAB_MESSAGE);
497bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mMessageContentView != null) {
498bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            blockNetworkLoads(true);
499bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView.scrollTo(0, 0);
500bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
501bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Dynamic configuration of WebView
502bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final WebSettings settings = mMessageContentView.getSettings();
503bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
504bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView.setInitialScale(getWebViewZoom());
505bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
506bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentsScroll.scrollTo(0, 0);
507bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mInviteScroll.scrollTo(0, 0);
508bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachments.removeAllViews();
509bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachments.setVisibility(View.GONE);
510bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initContactStatusViews();
511bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
512bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
513bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
514bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Returns the zoom scale (in percent) which is a combination of the user setting
515bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * (tiny, small, normal, large, huge) and the device density. The intention
516bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * is for the text to be physically equal in size over different density
517bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * screens.
518bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
519bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int getWebViewZoom() {
520bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        float density = mContext.getResources().getDisplayMetrics().density;
521bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        int zoom = Preferences.getPreferences(mContext).getTextZoom();
522bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (sZoomSizes == null) {
523bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            sZoomSizes = mContext.getResources()
524bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    .getStringArray(R.array.general_preference_text_zoom_size);
525bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
526bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return (int)(Float.valueOf(sZoomSizes[zoom]) * density * 100);
527bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
528bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
529bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void initContactStatusViews() {
530bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mContactStatusState = CONTACT_STATUS_STATE_UNLOADED;
531bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mQuickContactLookupUri = null;
532bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        showDefaultQuickContactBadgeImage();
533bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
534bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
535bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showDefaultQuickContactBadgeImage() {
536bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mFromBadge.setImageResource(R.drawable.ic_contact_picture);
537bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
538bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
539bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected final void addTabFlags(int tabFlags) {
540bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateTabs(mTabFlags | tabFlags);
541bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
542bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
543bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final void clearTabFlags(int tabFlags) {
544bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateTabs(mTabFlags & ~tabFlags);
545bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
546bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
547bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void setAttachmentCount(int count) {
548bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentCount = count;
549bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mAttachmentCount > 0) {
550bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            addTabFlags(TAB_FLAGS_HAS_ATTACHMENT);
551bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
552bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            clearTabFlags(TAB_FLAGS_HAS_ATTACHMENT);
553bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
554bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
555bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
556bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static void makeVisible(View v, boolean visible) {
557bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final int visibility = visible ? View.VISIBLE : View.GONE;
558bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if ((v != null) && (v.getVisibility() != visibility)) {
559bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            v.setVisibility(visibility);
560bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
561bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
562bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
563bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static boolean isVisible(View v) {
564bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return (v != null) && (v.getVisibility() == View.VISIBLE);
565bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
566bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
567bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
568bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Update the visual of the tabs.  (visibility, text, etc)
569bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
570bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateTabs(int tabFlags) {
571bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mTabFlags = tabFlags;
572bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
573bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (getView() == null) {
574bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
575bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
576bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
577bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        boolean messageTabVisible = (tabFlags & (TAB_FLAGS_HAS_INVITE | TAB_FLAGS_HAS_ATTACHMENT))
578bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                != 0;
579bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mMessageTab, messageTabVisible);
580bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mInviteTab, (tabFlags & TAB_FLAGS_HAS_INVITE) != 0);
581bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mAttachmentTab, (tabFlags & TAB_FLAGS_HAS_ATTACHMENT) != 0);
582bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
583bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final boolean hasPictures = (tabFlags & TAB_FLAGS_HAS_PICTURES) != 0;
584bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final boolean pictureLoaded = (tabFlags & TAB_FLAGS_PICTURE_LOADED) != 0;
585bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mShowPicturesTab, hasPictures && !pictureLoaded);
586bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
587bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachmentTab.setText(mContext.getResources().getQuantityString(
588bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                R.plurals.message_view_show_attachments_action,
589bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mAttachmentCount, mAttachmentCount));
590bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
591bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Hide the entire section if no tabs are visible.
592bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(mTabSection, isVisible(mMessageTab) || isVisible(mInviteTab)
593bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                || isVisible(mAttachmentTab) || isVisible(mShowPicturesTab)
594bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                || isVisible(mAlwaysShowPicturesButton));
595bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
596bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Restore previously selected tab after rotation
597bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mRestoredTab != TAB_NONE && isVisible(getTabViewForFlag(mRestoredTab))) {
598bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setCurrentTab(mRestoredTab);
599bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRestoredTab = TAB_NONE;
600bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
601bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
602bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
603bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
604bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Set the current tab.
605bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
606bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param tab any of {@link #TAB_MESSAGE}, {@link #TAB_ATTACHMENT} or {@link #TAB_INVITE}.
607bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
608bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void setCurrentTab(int tab) {
609bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mCurrentTab = tab;
610bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
611bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Hide & unselect all tabs
612bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(getTabContentViewForFlag(TAB_MESSAGE), false);
613bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(getTabContentViewForFlag(TAB_ATTACHMENT), false);
614bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(getTabContentViewForFlag(TAB_INVITE), false);
615bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        getTabViewForFlag(TAB_MESSAGE).setSelected(false);
616bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        getTabViewForFlag(TAB_ATTACHMENT).setSelected(false);
617bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        getTabViewForFlag(TAB_INVITE).setSelected(false);
618bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
619bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(getTabContentViewForFlag(mCurrentTab), true);
620bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        getTabViewForFlag(mCurrentTab).setSelected(true);
621bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
622bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
623bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View getTabViewForFlag(int tabFlag) {
624bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (tabFlag) {
625bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_MESSAGE:
626bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mMessageTab;
627bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_ATTACHMENT:
628bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mAttachmentTab;
629bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_INVITE:
630bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mInviteTab;
631bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
632bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        throw new IllegalArgumentException();
633bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
634bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
635bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View getTabContentViewForFlag(int tabFlag) {
636bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (tabFlag) {
637bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_MESSAGE:
638bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mMessageContentView;
639bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_ATTACHMENT:
640bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mAttachmentsScroll;
641bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case TAB_INVITE:
642bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return mInviteScroll;
643bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
644bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        throw new IllegalArgumentException();
645bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
646bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
647bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void blockNetworkLoads(boolean block) {
648bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mMessageContentView != null) {
649bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView.getSettings().setBlockNetworkLoads(block);
650bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
651bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
652bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
653bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void setMessageHtml(String html) {
654bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (html == null) {
655bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            html = "";
656bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
657bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mMessageContentView != null) {
658bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageContentView.loadDataWithBaseURL("email://", html, "text/html", "utf-8", null);
659bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
660bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
661bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
662bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
663bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Handle clicks on sender, which shows {@link QuickContact} or prompts to add
664bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * the sender as a contact.
665bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
666bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onClickSender() {
667bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!isMessageOpen()) return;
668bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Address senderEmail = Address.unpackFirst(mMessage.mFrom);
669bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (senderEmail == null) return;
670bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
671bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mContactStatusState == CONTACT_STATUS_STATE_UNLOADED) {
672bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Status not loaded yet.
673bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mContactStatusState = CONTACT_STATUS_STATE_UNLOADED_TRIGGERED;
674bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
675bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
676bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mContactStatusState == CONTACT_STATUS_STATE_UNLOADED_TRIGGERED) {
677bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return; // Already clicked, and waiting for the data.
678bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
679bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
680bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mQuickContactLookupUri != null) {
681bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            QuickContact.showQuickContact(mContext, mFromBadge, mQuickContactLookupUri,
682bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        QuickContact.MODE_MEDIUM, null);
683bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
684bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // No matching contact, ask user to create one
685bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final Uri mailUri = Uri.fromParts("mailto", senderEmail.getAddress(), null);
686bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final Intent intent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT,
687bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mailUri);
688bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
689bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Only provide personal name hint if we have one
690bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final String senderPersonal = senderEmail.getPersonal();
691bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!TextUtils.isEmpty(senderPersonal)) {
692bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                intent.putExtra(ContactsContract.Intents.Insert.NAME, senderPersonal);
693bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
694bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
695bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
696bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            startActivity(intent);
697bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
698bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
699bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
700bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static class ContactStatusLoaderCallbacks
701bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            implements LoaderCallbacks<ContactStatusLoader.Result> {
702bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static final String BUNDLE_EMAIL_ADDRESS = "email";
703bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final MessageViewFragmentBase mFragment;
704bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
705bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public ContactStatusLoaderCallbacks(MessageViewFragmentBase fragment) {
706bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFragment = fragment;
707bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
708bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
709bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static Bundle createArguments(String emailAddress) {
710bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Bundle b = new Bundle();
711bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            b.putString(BUNDLE_EMAIL_ADDRESS, emailAddress);
712bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return b;
713bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
714bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
715bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
716bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public Loader<ContactStatusLoader.Result> onCreateLoader(int id, Bundle args) {
717bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return new ContactStatusLoader(mFragment.mContext,
718bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    args.getString(BUNDLE_EMAIL_ADDRESS));
719bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
720bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
721bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
722bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoadFinished(Loader<ContactStatusLoader.Result> loader,
723bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                ContactStatusLoader.Result result) {
724bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean triggered =
725bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    (mFragment.mContactStatusState == CONTACT_STATUS_STATE_UNLOADED_TRIGGERED);
726bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFragment.mContactStatusState = CONTACT_STATUS_STATE_LOADED;
727bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFragment.mQuickContactLookupUri = result.mLookupUri;
728bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
729bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (result.isUnknown()) {
730bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mFragment.mSenderPresenceView.setVisibility(View.GONE);
731bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
732bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mFragment.mSenderPresenceView.setVisibility(View.VISIBLE);
733bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mFragment.mSenderPresenceView.setImageResource(result.mPresenceResId);
734bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
735bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (result.mPhoto != null) { // photo will be null if unknown.
736bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mFragment.mFromBadge.setImageBitmap(result.mPhoto);
737bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
738bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (triggered) {
739bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mFragment.onClickSender();
740bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
741bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
742bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
743bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
744bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoaderReset(Loader<ContactStatusLoader.Result> loader) {
745bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
746bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
747bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
748bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onSaveAttachment(MessageViewAttachmentInfo info) {
749bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!Utility.isExternalStorageMounted()) {
750bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            /*
751bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             * Abort early if there's no place to save the attachment. We don't want to spend
752bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             * the time downloading it and then abort.
753bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             */
754bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.showToast(getActivity(), R.string.message_view_status_attachment_not_saved);
755bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
756bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
757bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
758bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (info.isFileSaved()) {
759bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Nothing to do - we have the file saved.
760bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
761bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
762bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
763bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        File savedFile = performAttachmentSave(info);
764bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (savedFile != null) {
765bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.showToast(getActivity(), String.format(
766bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mContext.getString(R.string.message_view_status_attachment_saved),
767bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    savedFile.getName()));
768bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
769bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.showToast(getActivity(), R.string.message_view_status_attachment_not_saved);
770bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
771bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
772bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
773bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private File performAttachmentSave(MessageViewAttachmentInfo info) {
774bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Attachment attachment = Attachment.restoreAttachmentWithId(mContext, info.mId);
775bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Uri attachmentUri = AttachmentUtilities.getAttachmentUri(mAccountId, attachment.mId);
776bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
777bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        try {
778bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            File downloads = Environment.getExternalStoragePublicDirectory(
779bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    Environment.DIRECTORY_DOWNLOADS);
780bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            downloads.mkdirs();
781bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            File file = Utility.createUniqueFile(downloads, attachment.mFileName);
782bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
783bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mContext.getContentResolver(), attachmentUri);
784bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            InputStream in = mContext.getContentResolver().openInputStream(contentUri);
785bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            OutputStream out = new FileOutputStream(file);
786bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            IOUtils.copy(in, out);
787bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            out.flush();
788bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            out.close();
789bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            in.close();
790bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
791bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String absolutePath = file.getAbsolutePath();
792bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
793bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Although the download manager can scan media files, scanning only happens after the
794bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // user clicks on the item in the Downloads app. So, we run the attachment through
795bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // the media scanner ourselves so it gets added to gallery / music immediately.
796bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MediaScannerConnection.scanFile(mContext, new String[] {absolutePath},
797bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    null, null);
798bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
799bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            DownloadManager dm =
800bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
801bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            dm.addCompletedDownload(info.mName, info.mName,
802bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    false /* do not use media scanner */,
803bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    info.mContentType, absolutePath, info.mSize,
804bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    true /* show notification */);
805bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
806bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Cache the stored file information.
807bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            info.setSavedPath(absolutePath);
808bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
809bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Update our buttons.
810bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateAttachmentButtons(info);
811bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
812bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return file;
813bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
814bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } catch (IOException ioe) {
815bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Ignore. Callers will handle it from the return code.
816bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
817bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
818bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return null;
819bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
820bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
821bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onOpenAttachment(MessageViewAttachmentInfo info) {
822bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (info.mAllowInstall) {
823bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // The package installer is unable to install files from a content URI; it must be
824bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // given a file path. Therefore, we need to save it first in order to proceed
825bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!info.mAllowSave || !Utility.isExternalStorageMounted()) {
826bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Utility.showToast(getActivity(), R.string.message_view_status_attachment_not_saved);
827bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
828bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
829bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
830bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!info.isFileSaved()) {
831bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (performAttachmentSave(info) == null) {
832bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    // Saving failed for some reason - bail.
833bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    Utility.showToast(
834bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            getActivity(), R.string.message_view_status_attachment_not_saved);
835bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    return;
836bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
837bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
838bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
839bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        try {
840bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Intent intent = info.getAttachmentIntent(mContext, mAccountId);
841bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            startActivity(intent);
842bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } catch (ActivityNotFoundException e) {
843bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.showToast(getActivity(), R.string.message_view_display_attachment_toast);
844bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
845bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
846bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
847bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onInfoAttachment(final MessageViewAttachmentInfo attachment) {
848bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        AttachmentInfoDialog dialog =
849bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            AttachmentInfoDialog.newInstance(getActivity(), attachment.mDenyFlags);
850bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        dialog.show(getActivity().getFragmentManager(), null);
851bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
852bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
853bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onLoadAttachment(final MessageViewAttachmentInfo attachment) {
854bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachment.loadButton.setVisibility(View.GONE);
855bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // If there's nothing in the download queue, we'll probably start right away so wait a
856bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // second before showing the cancel button
857bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (AttachmentDownloadService.getQueueSize() == 0) {
858bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Set to invisible; if the button is still in this state one second from now, we'll
859bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // assume the download won't start right away, and we make the cancel button visible
860bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachment.cancelButton.setVisibility(View.GONE);
861bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Create the timed task that will change the button state
862bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            new EmailAsyncTask<Void, Void, Void>(mTaskTracker) {
863bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                @Override
864bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                protected Void doInBackground(Void... params) {
865bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    try {
866bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        Thread.sleep(1000L);
867bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    } catch (InterruptedException e) { }
868bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    return null;
869bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
870bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                @Override
871bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                protected void onSuccess(Void result) {
872bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    // If the timeout completes and the attachment has not loaded, show cancel
873bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (!attachment.loaded) {
874bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        attachment.cancelButton.setVisibility(View.VISIBLE);
875bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
876bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
877bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }.executeParallel();
878bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
879bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachment.cancelButton.setVisibility(View.VISIBLE);
880bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
881bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachment.showProgressIndeterminate();
882bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController.loadAttachment(attachment.mId, mMessageId, mAccountId);
883bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
884bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
885bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onCancelAttachment(MessageViewAttachmentInfo attachment) {
886bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Don't change button states if we couldn't cancel the download
887bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (AttachmentDownloadService.cancelQueuedAttachment(attachment.mId)) {
888bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachment.loadButton.setVisibility(View.VISIBLE);
889bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachment.cancelButton.setVisibility(View.GONE);
890bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachment.hideProgress();
891bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
892bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
893bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
894bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
895bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called by ControllerResults. Show the "View" and "Save" buttons; hide "Load" and "Stop"
896bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
897bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param attachmentId the attachment that was just downloaded
898bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
899bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void doFinishLoadAttachment(long attachmentId) {
900bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MessageViewAttachmentInfo info = findAttachmentInfo(attachmentId);
901bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (info != null) {
902bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            info.loaded = true;
903bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateAttachmentButtons(info);
904bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
905bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
906bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
907bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showPicturesInHtml() {
908bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        boolean picturesAlreadyLoaded = (mTabFlags & TAB_FLAGS_PICTURE_LOADED) != 0;
909bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if ((mMessageContentView != null) && !picturesAlreadyLoaded) {
910bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            blockNetworkLoads(false);
911bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // TODO: why is this calling setMessageHtml just because the images can load now?
912bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setMessageHtml(mHtmlTextWebView);
913bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
914bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Prompt the user to always show images from this sender.
915bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            makeVisible(UiUtilities.getView(getView(), R.id.always_show_pictures_button), true);
916bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
917bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            addTabFlags(TAB_FLAGS_PICTURE_LOADED);
918bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
919bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
920bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
921bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showDetails() {
922bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!isMessageOpen()) {
923bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
924bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
925bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
926bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!mDetailsFilled) {
927bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String date = formatDate(mMessage.mTimeStamp, true);
928bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final String SEPARATOR = "\n";
929bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String to = Address.toString(Address.unpack(mMessage.mTo), SEPARATOR);
930bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String cc = Address.toString(Address.unpack(mMessage.mCc), SEPARATOR);
931bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String bcc = Address.toString(Address.unpack(mMessage.mBcc), SEPARATOR);
932bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setDetailsRow(mDetailsExpanded, date, R.id.date, R.id.date_row);
933bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setDetailsRow(mDetailsExpanded, to, R.id.to, R.id.to_row);
934bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setDetailsRow(mDetailsExpanded, cc, R.id.cc, R.id.cc_row);
935bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            setDetailsRow(mDetailsExpanded, bcc, R.id.bcc, R.id.bcc_row);
936bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mDetailsFilled = true;
937bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
938bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
939bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsCollapsed.setVisibility(View.GONE);
940bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsExpanded.setVisibility(View.VISIBLE);
941bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
942bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
943bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void hideDetails() {
944bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsCollapsed.setVisibility(View.VISIBLE);
945bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDetailsExpanded.setVisibility(View.GONE);
946bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
947bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
948bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static void setDetailsRow(View root, String text, int textViewId, int rowViewId) {
949bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (TextUtils.isEmpty(text)) {
950bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            root.findViewById(rowViewId).setVisibility(View.GONE);
951bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
952bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
953bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ((TextView) UiUtilities.getView(root, textViewId)).setText(text);
954bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
955bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
956bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
957bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
958bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onClick(View view) {
959bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!isMessageOpen()) {
960bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return; // Ignore.
961bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
962bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (view.getId()) {
963bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.badge:
964bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onClickSender();
965bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
966bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.load:
967bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onLoadAttachment((MessageViewAttachmentInfo) view.getTag());
968bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
969bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.info:
970bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onInfoAttachment((MessageViewAttachmentInfo) view.getTag());
971bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
972bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.save:
973bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onSaveAttachment((MessageViewAttachmentInfo) view.getTag());
974bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
975bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.open:
976bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onOpenAttachment((MessageViewAttachmentInfo) view.getTag());
977bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
978bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.cancel:
979bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onCancelAttachment((MessageViewAttachmentInfo) view.getTag());
980bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
981bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.show_message:
982bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setCurrentTab(TAB_MESSAGE);
983bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
984bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.show_invite:
985bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setCurrentTab(TAB_INVITE);
986bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
987bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.show_attachments:
988bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setCurrentTab(TAB_ATTACHMENT);
989bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
990bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.show_pictures:
991bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                showPicturesInHtml();
992bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
993bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.always_show_pictures_button:
994bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setShowImagesForSender();
995bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
996bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.sub_header_contents_collapsed:
997bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                showDetails();
998bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
999bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.sub_header_contents_expanded:
1000bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                hideDetails();
1001bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
1002bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1003bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1004bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1005bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1006bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Start loading contact photo and presence.
1007bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1008bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void queryContactStatus() {
1009bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!isMessageOpen()) return;
1010bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initContactStatusViews(); // Initialize the state, just in case.
1011bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1012bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Find the sender email address, and start presence check.
1013bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Address sender = Address.unpackFirst(mMessage.mFrom);
1014bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (sender != null) {
1015bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String email = sender.getAddress();
1016bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (email != null) {
1017bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                getLoaderManager().restartLoader(PHOTO_LOADER_ID,
1018bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        ContactStatusLoaderCallbacks.createArguments(email),
1019bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        new ContactStatusLoaderCallbacks(this));
1020bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1021bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1022bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1023bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1024bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1025bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called by {@link LoadMessageTask} and {@link ReloadMessageTask} to load a message in a
1026bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * subclass specific way.
1027bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1028bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * NOTE This method is called on a worker thread!  Implementations must properly synchronize
1029bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * when accessing members.
1030bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1031bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param activity the parent activity.  Subclass use it as a context, and to show a toast.
1032bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1033bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected abstract Message openMessageSync(Activity activity);
1034bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1035bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1036bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called in a background thread to reload a new copy of the Message in case something has
1037bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * changed.
1038bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1039bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected Message reloadMessageSync(Activity activity) {
1040bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return openMessageSync(activity);
1041bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1042bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1043bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1044bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Async task for loading a single message outside of the UI thread
1045bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1046bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class LoadMessageTask extends EmailAsyncTask<Void, Void, Message> {
1047bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1048bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final boolean mOkToFetch;
1049bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Mailbox mMailbox;
1050bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1051bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1052bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Special constructor to cache some local info
1053bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1054bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public LoadMessageTask(boolean okToFetch) {
1055bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
1056bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mOkToFetch = okToFetch;
1057bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1058bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1059bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1060bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Message doInBackground(Void... params) {
1061bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Activity activity = getActivity();
1062bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Message message = null;
1063bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (activity != null) {
1064bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                message = openMessageSync(activity);
1065bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1066bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (message != null) {
1067bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mMailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);
1068bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (mMailbox == null) {
1069bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    message = null; // mailbox removed??
1070bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1071bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1072bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return message;
1073bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1074bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1075bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1076bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(Message message) {
1077bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (message == null) {
1078bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                resetView();
1079bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mCallback.onMessageNotExists();
1080bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1081bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1082bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageId = message.mId;
1083bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1084bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            reloadUiFromMessage(message, mOkToFetch);
1085bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            queryContactStatus();
1086bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            onMessageShown(mMessageId, mMailbox);
1087bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            RecentMailboxManager.getInstance(mContext).touch(mAccountId, message.mMailboxKey);
1088bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1089bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1090bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1091bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1092bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Kicked by {@link MessageObserver}.  Reload the message and update the views.
1093bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1094bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class ReloadMessageTask extends EmailAsyncTask<Void, Void, Message> {
1095bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public ReloadMessageTask() {
1096bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
1097bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1098bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1099bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Message doInBackground(Void... params) {
1101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Activity activity = getActivity();
1102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (activity == null) {
1103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return null;
1104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return reloadMessageSync(activity);
1106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(Message message) {
1111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (message == null || message.mMailboxKey != mMessage.mMailboxKey) {
1112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Message deleted or moved.
1113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mCallback.onMessageNotExists();
1114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessage = message;
1117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateHeaderView(mMessage);
1118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called when a message is shown to the user.
1123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected void onMessageShown(long messageId, Mailbox mailbox) {
1125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called when the message body is loaded.
1129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected void onPostLoadBody() {
1131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Async task for loading a single message body outside of the UI thread
1135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class LoadBodyTask extends EmailAsyncTask<Void, Void, String[]> {
1137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final long mId;
1139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private boolean mErrorLoadingMessageBody;
1140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final boolean mAutoShowPictures;
1141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Special constructor to cache some local info
1144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public LoadBodyTask(long messageId, boolean autoShowPictures) {
1146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
1147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mId = messageId;
1148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAutoShowPictures = autoShowPictures;
1149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected String[] doInBackground(Void... params) {
1153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            try {
1154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                String text = null;
1155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                String html = Body.restoreBodyHtmlWithMessageId(mContext, mId);
1156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (html == null) {
1157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    text = Body.restoreBodyTextWithMessageId(mContext, mId);
1158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return new String[] { text, html };
1160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } catch (RuntimeException re) {
1161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // This catches SQLiteException as well as other RTE's we've seen from the
1162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // database calls, such as IllegalStateException
1163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Log.d(Logging.LOG_TAG, "Exception while loading message body", re);
1164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mErrorLoadingMessageBody = true;
1165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return null;
1166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(String[] results) {
1171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (results == null) {
1172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (mErrorLoadingMessageBody) {
1173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    Utility.showToast(getActivity(), R.string.error_loading_message_body);
1174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                resetView();
1176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            reloadUiFromBody(results[0], results[1], mAutoShowPictures);    // text, html
1179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            onPostLoadBody();
1180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Async task for loading attachments
1185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Note:  This really should only be called when the message load is complete - or, we should
1187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * leave open a listener so the attachments can fill in as they are discovered.  In either case,
1188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * this implementation is incomplete, as it will fail to refresh properly if the message is
1189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * partially loaded at this time.
1190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class LoadAttachmentsTask extends EmailAsyncTask<Long, Void, Attachment[]> {
1192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public LoadAttachmentsTask() {
1193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
1194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Attachment[] doInBackground(Long... messageIds) {
1198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return Attachment.restoreAttachmentsWithMessageId(mContext, messageIds[0]);
1199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(Attachment[] attachments) {
1203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            try {
1204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (attachments == null) {
1205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    return;
1206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                boolean htmlChanged = false;
1208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                int numDisplayedAttachments = 0;
1209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                for (Attachment attachment : attachments) {
1210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (mHtmlTextRaw != null && attachment.mContentId != null
1211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            && attachment.mContentUri != null) {
1212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // for html body, replace CID for inline images
1213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // Regexp which matches ' src="cid:contentId"'.
1214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        String contentIdRe =
1215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            "\\s+(?i)src=\"cid(?-i):\\Q" + attachment.mContentId + "\\E\"";
1216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        String srcContentUri = " src=\"" + attachment.mContentUri + "\"";
1217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        mHtmlTextRaw = mHtmlTextRaw.replaceAll(contentIdRe, srcContentUri);
1218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        htmlChanged = true;
1219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    } else {
1220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        addAttachment(attachment);
1221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        numDisplayedAttachments++;
1222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setAttachmentCount(numDisplayedAttachments);
1225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mHtmlTextWebView = mHtmlTextRaw;
1226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mHtmlTextRaw = null;
1227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (htmlChanged) {
1228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    setMessageHtml(mHtmlTextWebView);
1229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } finally {
1231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                showContent(true, false);
1232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static Bitmap getPreviewIcon(Context context, AttachmentInfo attachment) {
1237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        try {
1238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return BitmapFactory.decodeStream(
1239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    context.getContentResolver().openInputStream(
1240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            AttachmentUtilities.getAttachmentThumbnailUri(
1241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    attachment.mAccountKey, attachment.mId,
1242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    PREVIEW_ICON_WIDTH,
1243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    PREVIEW_ICON_HEIGHT)));
1244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } catch (Exception e) {
1245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, "Attachment preview failed with exception " + e.getMessage());
1246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return null;
1247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Subclass of AttachmentInfo which includes our views and buttons related to attachment
1252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * handling, as well as our determination of suitability for viewing (based on availability of
1253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * a viewer app) and saving (based upon the presence of external storage)
1254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static class MessageViewAttachmentInfo extends AttachmentInfo {
1256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Button openButton;
1257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Button saveButton;
1258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Button loadButton;
1259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Button infoButton;
1260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private Button cancelButton;
1261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private ImageView iconView;
1262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static final Map<AttachmentInfo, String> sSavedFileInfos = Maps.newHashMap();
1264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Don't touch it directly from the outer class.
1266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final ProgressBar mProgressView;
1267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private boolean loaded;
1268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MessageViewAttachmentInfo(Context context, Attachment attachment,
1270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                ProgressBar progressView) {
1271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(context, attachment);
1272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mProgressView = progressView;
1273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Create a new attachment info based upon an existing attachment info. Display
1277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * related fields (such as views and buttons) are copied from old to new.
1278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MessageViewAttachmentInfo(Context context, MessageViewAttachmentInfo oldInfo) {
1280bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(context, oldInfo);
1281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            openButton = oldInfo.openButton;
1282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            saveButton = oldInfo.saveButton;
1283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            loadButton = oldInfo.loadButton;
1284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            infoButton = oldInfo.infoButton;
1285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            cancelButton = oldInfo.cancelButton;
1286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            iconView = oldInfo.iconView;
1287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mProgressView = oldInfo.mProgressView;
1288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            loaded = oldInfo.loaded;
1289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void hideProgress() {
1292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Don't use GONE, which'll break the layout.
1293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mProgressView.getVisibility() != View.INVISIBLE) {
1294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mProgressView.setVisibility(View.INVISIBLE);
1295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void showProgress(int progress) {
1299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mProgressView.getVisibility() != View.VISIBLE) {
1300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mProgressView.setVisibility(View.VISIBLE);
1301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mProgressView.isIndeterminate()) {
1303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mProgressView.setIndeterminate(false);
1304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mProgressView.setProgress(progress);
1306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Hide on completion.
1308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (progress == 100) {
1309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                hideProgress();
1310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void showProgressIndeterminate() {
1314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mProgressView.getVisibility() != View.VISIBLE) {
1315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mProgressView.setVisibility(View.VISIBLE);
1316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!mProgressView.isIndeterminate()) {
1318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mProgressView.setIndeterminate(true);
1319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Determines whether or not this attachment has a saved file in the external storage. That
1324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * is, the user has at some point clicked "save" for this attachment.
1325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *
1326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Note: this is an approximation and uses an in-memory cache that can get wiped when the
1327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * process dies, and so is somewhat conservative. Additionally, the user can modify the file
1328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * after saving, and so the file may not be the same (though this is unlikely).
1329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean isFileSaved() {
1331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String path = getSavedPath();
1332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (path == null) {
1333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return false;
1334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean savedFileExists = new File(path).exists();
1336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!savedFileExists) {
1337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Purge the cache entry.
1338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setSavedPath(null);
1339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return savedFileExists;
1341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private void setSavedPath(String path) {
1344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (path == null) {
1345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sSavedFileInfos.remove(this);
1346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sSavedFileInfos.put(this, path);
1348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Returns an absolute file path for the given attachment if it has been saved. If one is
1353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * not found, {@code null} is returned.
1354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *
1355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Clients are expected to validate that the file at the given path is still valid.
1356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private String getSavedPath() {
1358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return sSavedFileInfos.get(this);
1359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Uri getUriForIntent(Context context, long accountId) {
1363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Prefer to act on the saved file for intents.
1364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String path = getSavedPath();
1365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return (path != null)
1366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    ? Uri.parse("file://" + getSavedPath())
1367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    : super.getUriForIntent(context, accountId);
1368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Updates all current attachments on the attachment tab.
1373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateAttachmentTab() {
1375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
1376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            View view = mAttachments.getChildAt(i);
1377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageViewAttachmentInfo oldInfo = (MessageViewAttachmentInfo)view.getTag();
1378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageViewAttachmentInfo newInfo =
1379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    new MessageViewAttachmentInfo(getActivity(), oldInfo);
1380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateAttachmentButtons(newInfo);
1381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            view.setTag(newInfo);
1382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Updates the attachment buttons. Adjusts the visibility of the buttons as well
1387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * as updating any tag information associated with the buttons.
1388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateAttachmentButtons(MessageViewAttachmentInfo attachmentInfo) {
1390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ImageView attachmentIcon = attachmentInfo.iconView;
1391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button openButton = attachmentInfo.openButton;
1392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button saveButton = attachmentInfo.saveButton;
1393bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button loadButton = attachmentInfo.loadButton;
1394bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button infoButton = attachmentInfo.infoButton;
1395bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button cancelButton = attachmentInfo.cancelButton;
1396bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1397bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!attachmentInfo.mAllowView) {
1398bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            openButton.setVisibility(View.GONE);
1399bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1400bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!attachmentInfo.mAllowSave) {
1401bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            saveButton.setVisibility(View.GONE);
1402bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1403bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1404bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!attachmentInfo.mAllowView && !attachmentInfo.mAllowSave) {
1405bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // This attachment may never be viewed or saved, so block everything
1406bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachmentInfo.hideProgress();
1407bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            openButton.setVisibility(View.GONE);
1408bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            saveButton.setVisibility(View.GONE);
1409bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            loadButton.setVisibility(View.GONE);
1410bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            cancelButton.setVisibility(View.GONE);
1411bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            infoButton.setVisibility(View.VISIBLE);
1412bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else if (attachmentInfo.loaded) {
1413bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // If the attachment is loaded, show 100% progress
1414bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Note that for POP3 messages, the user will only see "Open" and "Save",
1415bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // because the entire message is loaded before being shown.
1416bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Hide "Load" and "Info", show "View" and "Save"
1417bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachmentInfo.showProgress(100);
1418bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachmentInfo.mAllowSave) {
1419bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                saveButton.setVisibility(View.VISIBLE);
1420bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1421bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                boolean isFileSaved = attachmentInfo.isFileSaved();
1422bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                saveButton.setEnabled(!isFileSaved);
1423bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (!isFileSaved) {
1424bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    saveButton.setText(R.string.message_view_attachment_save_action);
1425bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                } else {
1426bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    saveButton.setText(R.string.message_view_attachment_saved);
1427bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1428bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1429bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachmentInfo.mAllowView) {
1430bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Set the attachment action button text accordingly
1431bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (attachmentInfo.mContentType.startsWith("audio/") ||
1432bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        attachmentInfo.mContentType.startsWith("video/")) {
1433bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    openButton.setText(R.string.message_view_attachment_play_action);
1434bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                } else if (attachmentInfo.mAllowInstall) {
1435bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    openButton.setText(R.string.message_view_attachment_install_action);
1436bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                } else {
1437bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    openButton.setText(R.string.message_view_attachment_view_action);
1438bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1439bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                openButton.setVisibility(View.VISIBLE);
1440bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1441bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachmentInfo.mDenyFlags == AttachmentInfo.ALLOW) {
1442bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                infoButton.setVisibility(View.GONE);
1443bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1444bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                infoButton.setVisibility(View.VISIBLE);
1445bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1446bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            loadButton.setVisibility(View.GONE);
1447bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            cancelButton.setVisibility(View.GONE);
1448bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1449bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updatePreviewIcon(attachmentInfo);
1450bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1451bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // The attachment is not loaded, so present UI to start downloading it
1452bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1453bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Show "Load"; hide "View", "Save" and "Info"
1454bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            saveButton.setVisibility(View.GONE);
1455bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            openButton.setVisibility(View.GONE);
1456bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            infoButton.setVisibility(View.GONE);
1457bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1458bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // If the attachment is queued, show the indeterminate progress bar.  From this point,.
1459bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // any progress changes will cause this to be replaced by the normal progress bar
1460bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (AttachmentDownloadService.isAttachmentQueued(attachmentInfo.mId)) {
1461bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                attachmentInfo.showProgressIndeterminate();
1462bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                loadButton.setVisibility(View.GONE);
1463bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                cancelButton.setVisibility(View.VISIBLE);
1464bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1465bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                loadButton.setVisibility(View.VISIBLE);
1466bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                cancelButton.setVisibility(View.GONE);
1467bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1468bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1469bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        openButton.setTag(attachmentInfo);
1470bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        saveButton.setTag(attachmentInfo);
1471bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        loadButton.setTag(attachmentInfo);
1472bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        infoButton.setTag(attachmentInfo);
1473bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        cancelButton.setTag(attachmentInfo);
1474bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1475bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1476bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1477bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Copy data from a cursor-refreshed attachment into the UI.  Called from UI thread.
1478bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1479bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param attachment A single attachment loaded from the provider
1480bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1481bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void addAttachment(Attachment attachment) {
1482bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        LayoutInflater inflater = getActivity().getLayoutInflater();
1483bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        View view = inflater.inflate(R.layout.message_view_attachment, null);
1484bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1485bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        TextView attachmentName = (TextView) UiUtilities.getView(view, R.id.attachment_name);
1486bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        TextView attachmentInfoView = (TextView) UiUtilities.getView(view, R.id.attachment_info);
1487bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ImageView attachmentIcon = (ImageView) UiUtilities.getView(view, R.id.attachment_icon);
1488bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button openButton = (Button) UiUtilities.getView(view, R.id.open);
1489bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button saveButton = (Button) UiUtilities.getView(view, R.id.save);
1490bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button loadButton = (Button) UiUtilities.getView(view, R.id.load);
1491bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button infoButton = (Button) UiUtilities.getView(view, R.id.info);
1492bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Button cancelButton = (Button) UiUtilities.getView(view, R.id.cancel);
1493bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ProgressBar attachmentProgress = (ProgressBar) UiUtilities.getView(view, R.id.progress);
1494bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1495bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MessageViewAttachmentInfo attachmentInfo = new MessageViewAttachmentInfo(
1496bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mContext, attachment, attachmentProgress);
1497bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1498bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Check whether the attachment already exists
1499bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Utility.attachmentExists(mContext, attachment)) {
1500bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            attachmentInfo.loaded = true;
1501bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1502bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1503bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.openButton = openButton;
1504bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.saveButton = saveButton;
1505bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.loadButton = loadButton;
1506bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.infoButton = infoButton;
1507bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.cancelButton = cancelButton;
1508bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfo.iconView = attachmentIcon;
1509bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1510bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateAttachmentButtons(attachmentInfo);
1511bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1512bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        view.setTag(attachmentInfo);
1513bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        openButton.setOnClickListener(this);
1514bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        saveButton.setOnClickListener(this);
1515bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        loadButton.setOnClickListener(this);
1516bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        infoButton.setOnClickListener(this);
1517bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        cancelButton.setOnClickListener(this);
1518bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1519bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentName.setText(attachmentInfo.mName);
1520bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        attachmentInfoView.setText(UiUtilities.formatSize(mContext, attachmentInfo.mSize));
1521bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1522bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachments.addView(view);
1523bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAttachments.setVisibility(View.VISIBLE);
1524bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1525bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1526bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private MessageViewAttachmentInfo findAttachmentInfoFromView(long attachmentId) {
1527bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
1528bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageViewAttachmentInfo attachmentInfo =
1529bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    (MessageViewAttachmentInfo) mAttachments.getChildAt(i).getTag();
1530bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachmentInfo.mId == attachmentId) {
1531bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return attachmentInfo;
1532bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1533bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1534bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return null;
1535bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1536bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1537bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1538bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Reload the UI from a provider cursor.  {@link LoadMessageTask#onSuccess} calls it.
1539bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1540bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Update the header views, and start loading the body.
1541bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1542bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param message A copy of the message loaded from the database
1543bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param okToFetch If true, and message is not fully loaded, it's OK to fetch from
1544bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * the network.  Use false to prevent looping here.
1545bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1546bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected void reloadUiFromMessage(Message message, boolean okToFetch) {
1547bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessage = message;
1548bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAccountId = message.mAccountKey;
1549bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1550bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mMessageObserver.register(ContentUris.withAppendedId(Message.CONTENT_URI, mMessage.mId));
1551bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1552bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateHeaderView(mMessage);
1553bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1554bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Handle partially-loaded email, as follows:
1555bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // 1. Check value of message.mFlagLoaded
1556bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // 2. If != LOADED, ask controller to load it
1557bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // 3. Controller callback (after loaded) should trigger LoadBodyTask & LoadAttachmentsTask
1558bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // 4. Else start the loader tasks right away (message already loaded)
1559bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (okToFetch && message.mFlagLoaded != Message.FLAG_LOADED_COMPLETE) {
1560bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mControllerCallback.getWrappee().setWaitForLoadMessageId(message.mId);
1561bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mController.loadMessageForView(message.mId);
1562bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1563bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Address[] fromList = Address.unpack(mMessage.mFrom);
1564bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean autoShowImages = false;
1565bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            for (Address sender : fromList) {
1566bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                String email = sender.getAddress();
1567bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (shouldShowImagesFor(email)) {
1568bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    autoShowImages = true;
1569bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1570bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1571bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1572bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mControllerCallback.getWrappee().setWaitForLoadMessageId(Message.NO_MESSAGE);
1573bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Ask for body
1574bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            new LoadBodyTask(message.mId, autoShowImages).executeParallel();
1575bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1576bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1577bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1578bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    protected void updateHeaderView(Message message) {
1579bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSubjectView.setText(message.mSubject);
1580bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Address from = Address.unpackFirst(message.mFrom);
1581bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1582bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Set sender address/display name
1583bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Note we set " " for empty field, so TextView's won't get squashed.
1584bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Otherwise their height will be 0, which breaks the layout.
1585bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (from != null) {
1586bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final String fromFriendly = from.toFriendly();
1587bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final String fromAddress = from.getAddress();
1588bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFromNameView.setText(fromFriendly);
1589bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFromAddressView.setText(fromFriendly.equals(fromAddress) ? " " : fromAddress);
1590bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1591bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFromNameView.setText(" ");
1592bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mFromAddressView.setText(" ");
1593bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1594bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDateTimeView.setText(DateUtils.getRelativeTimeSpanString(mContext, message.mTimeStamp)
1595bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                .toString());
1596bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1597bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // To/Cc/Bcc
1598bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Resources res = mContext.getResources();
1599bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final SpannableStringBuilder ssb = new SpannableStringBuilder();
1600bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final String friendlyTo = Address.toFriendly(Address.unpack(message.mTo));
1601bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final String friendlyCc = Address.toFriendly(Address.unpack(message.mCc));
1602bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final String friendlyBcc = Address.toFriendly(Address.unpack(message.mBcc));
1603bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1604bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!TextUtils.isEmpty(friendlyTo)) {
1605bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.appendBold(ssb, res.getString(R.string.message_view_to_label));
1606bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(" ");
1607bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(friendlyTo);
1608bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1609bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!TextUtils.isEmpty(friendlyCc)) {
1610bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append("  ");
1611bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.appendBold(ssb, res.getString(R.string.message_view_cc_label));
1612bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(" ");
1613bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(friendlyCc);
1614bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1615bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!TextUtils.isEmpty(friendlyBcc)) {
1616bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append("  ");
1617bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Utility.appendBold(ssb, res.getString(R.string.message_view_bcc_label));
1618bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(" ");
1619bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ssb.append(friendlyBcc);
1620bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1621bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mAddressesView.setText(ssb);
1622bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1623bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1624bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1625bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return the given date/time in a human readable form.  The returned string always have
1626bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *     month and day (and year if {@code withYear} is set), so is usually long.
1627bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *     Use {@link DateUtils#getRelativeTimeSpanString} instead to save the screen real estate.
1628bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1629bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String formatDate(long millis, boolean withYear) {
1630bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        StringBuilder sb = new StringBuilder();
1631bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Formatter formatter = new Formatter(sb);
1632bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        DateUtils.formatDateRange(mContext, formatter, millis, millis,
1633bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                DateUtils.FORMAT_SHOW_DATE
1634bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                | DateUtils.FORMAT_ABBREV_ALL
1635bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                | DateUtils.FORMAT_SHOW_TIME
1636bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                | (withYear ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
1637bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return sb.toString();
1638bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1639bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1640bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1641bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Reload the body from the provider cursor.  This must only be called from the UI thread.
1642bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1643bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param bodyText text part
1644bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param bodyHtml html part
1645bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1646bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * TODO deal with html vs text and many other issues <- WHAT DOES IT MEAN??
1647bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1648bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void reloadUiFromBody(String bodyText, String bodyHtml, boolean autoShowPictures) {
1649bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        String text = null;
1650bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mHtmlTextRaw = null;
1651bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        boolean hasImages = false;
1652bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1653bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (bodyHtml == null) {
1654bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            text = bodyText;
1655bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            /*
1656bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             * Convert the plain text to HTML
1657bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             */
1658bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            StringBuffer sb = new StringBuffer("<html><body>");
1659bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (text != null) {
1660bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Escape any inadvertent HTML in the text message
1661bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                text = EmailHtmlUtil.escapeCharacterToDisplay(text);
1662bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Find any embedded URL's and linkify
1663bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Matcher m = Patterns.WEB_URL.matcher(text);
1664bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                while (m.find()) {
1665bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    int start = m.start();
1666bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    /*
1667bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                     * WEB_URL_PATTERN may match domain part of email address. To detect
1668bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                     * this false match, the character just before the matched string
1669bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                     * should not be '@'.
1670bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                     */
1671bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (start == 0 || text.charAt(start - 1) != '@') {
1672bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        String url = m.group();
1673bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        Matcher proto = WEB_URL_PROTOCOL.matcher(url);
1674bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        String link;
1675bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (proto.find()) {
1676bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // This is work around to force URL protocol part be lower case,
1677bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // because WebView could follow only lower case protocol link.
1678bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            link = proto.group().toLowerCase() + url.substring(proto.end());
1679bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        } else {
1680bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // Patterns.WEB_URL matches URL without protocol part,
1681bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // so added default protocol to link.
1682bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            link = "http://" + url;
1683bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
1684bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        String href = String.format("<a href=\"%s\">%s</a>", link, url);
1685bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        m.appendReplacement(sb, href);
1686bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1687bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    else {
1688bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        m.appendReplacement(sb, "$0");
1689bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1690bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1691bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                m.appendTail(sb);
1692bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1693bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            sb.append("</body></html>");
1694bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            text = sb.toString();
1695bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1696bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            text = bodyHtml;
1697bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mHtmlTextRaw = bodyHtml;
1698bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            hasImages = IMG_TAG_START_REGEX.matcher(text).find();
1699bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1700bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1701bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // TODO this is not really accurate.
1702bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // - Images aren't the only network resources.  (e.g. CSS)
1703bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // - If images are attached to the email and small enough, we download them at once,
1704bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        //   and won't need network access when they're shown.
1705bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (hasImages) {
1706bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mRestoredPictureLoaded || autoShowPictures) {
1707bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                blockNetworkLoads(false);
1708bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                addTabFlags(TAB_FLAGS_PICTURE_LOADED); // Set for next onSaveInstanceState
1709bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1710bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Make sure to reset the flag -- otherwise this will keep taking effect even after
1711bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // moving to another message.
1712bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mRestoredPictureLoaded = false;
1713bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1714bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                addTabFlags(TAB_FLAGS_HAS_PICTURES);
1715bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1716bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1717bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        setMessageHtml(text);
1718bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1719bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Ask for attachments after body
1720bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        new LoadAttachmentsTask().executeParallel(mMessage.mId);
1721bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1722bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mIsMessageLoadedForTest = true;
1723bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1724bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1725bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1726bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Overrides for WebView behaviors.
1727bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1728bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class CustomWebViewClient extends WebViewClient {
1729bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1730bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean shouldOverrideUrlLoading(WebView view, String url) {
1731bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return mCallback.onUrlInMessageClicked(url);
1732bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1733bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1734bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1735bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View findAttachmentView(long attachmentId) {
1736bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
1737bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            View view = mAttachments.getChildAt(i);
1738bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageViewAttachmentInfo attachment = (MessageViewAttachmentInfo) view.getTag();
1739bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachment.mId == attachmentId) {
1740bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return view;
1741bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1742bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1743bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return null;
1744bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1745bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1746bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private MessageViewAttachmentInfo findAttachmentInfo(long attachmentId) {
1747bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        View view = findAttachmentView(attachmentId);
1748bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (view != null) {
1749bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return (MessageViewAttachmentInfo)view.getTag();
1750bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1751bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return null;
1752bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1753bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1754bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1755bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Controller results listener.  We wrap it with {@link ControllerResultUiThreadWrapper},
1756bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * so all methods are called on the UI thread.
1757bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1758bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class ControllerResults extends Controller.Result {
1759bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private long mWaitForLoadMessageId;
1760bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1761bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void setWaitForLoadMessageId(long messageId) {
1762bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mWaitForLoadMessageId = messageId;
1763bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1764bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1765bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1766bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void loadMessageForViewCallback(MessagingException result, long accountId,
1767bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                long messageId, int progress) {
1768bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (messageId != mWaitForLoadMessageId) {
1769bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // We are not waiting for this message to load, so exit quickly
1770bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1771bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1772bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (result == null) {
1773bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                switch (progress) {
1774bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case 0:
1775bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        mCallback.onLoadMessageStarted();
1776bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // Loading from network -- show the progress icon.
1777bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        showContent(false, true);
1778bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
1779bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case 100:
1780bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        mWaitForLoadMessageId = -1;
1781bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        mCallback.onLoadMessageFinished();
1782bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // reload UI and reload everything else too
1783bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // pass false to LoadMessageTask to prevent looping here
1784bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        cancelAllTasks();
1785bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        new LoadMessageTask(false).executeParallel();
1786bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
1787bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    default:
1788bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // do nothing - we don't have a progress bar at this time
1789bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
1790bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1791bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else {
1792bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mWaitForLoadMessageId = Message.NO_MESSAGE;
1793bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                String error = mContext.getString(R.string.status_network_error);
1794bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mCallback.onLoadMessageError(error);
1795bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                resetView();
1796bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1797bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1798bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1799bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1800bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void loadAttachmentCallback(MessagingException result, long accountId,
1801bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                long messageId, long attachmentId, int progress) {
1802bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (messageId == mMessageId) {
1803bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (result == null) {
1804bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    showAttachmentProgress(attachmentId, progress);
1805bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    switch (progress) {
1806bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        case 100:
1807bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            final MessageViewAttachmentInfo attachmentInfo =
1808bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    findAttachmentInfoFromView(attachmentId);
1809bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            if (attachmentInfo != null) {
1810bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                updatePreviewIcon(attachmentInfo);
1811bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            }
1812bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            doFinishLoadAttachment(attachmentId);
1813bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
1814bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        default:
1815bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // do nothing - we don't have a progress bar at this time
1816bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
1817bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1818bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                } else {
1819bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    MessageViewAttachmentInfo attachment = findAttachmentInfo(attachmentId);
1820bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (attachment == null) {
1821bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // Called before LoadAttachmentsTask finishes.
1822bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // (Possible if you quickly close & re-open a message)
1823bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        return;
1824bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1825bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    attachment.cancelButton.setVisibility(View.GONE);
1826bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    attachment.loadButton.setVisibility(View.VISIBLE);
1827bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    attachment.hideProgress();
1828bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1829bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    final String error;
1830bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (result.getCause() instanceof IOException) {
1831bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        error = mContext.getString(R.string.status_network_error);
1832bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    } else {
1833bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        error = mContext.getString(
1834bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                R.string.message_view_load_attachment_failed_toast,
1835bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                attachment.mName);
1836bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1837bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mCallback.onLoadMessageError(error);
1838bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1839bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1840bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1841bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1842bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private void showAttachmentProgress(long attachmentId, int progress) {
1843bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageViewAttachmentInfo attachment = findAttachmentInfo(attachmentId);
1844bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (attachment != null) {
1845bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (progress == 0) {
1846bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    attachment.cancelButton.setVisibility(View.GONE);
1847bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1848bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                attachment.showProgress(progress);
1849bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1850bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1851bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1852bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1853bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1854bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Class to detect update on the current message (e.g. toggle star).  When it gets content
1855bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * change notifications, it kicks {@link ReloadMessageTask}.
1856bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1857bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class MessageObserver extends ContentObserver implements Runnable {
1858bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final Throttle mThrottle;
1859bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final ContentResolver mContentResolver;
1860bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1861bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private boolean mRegistered;
1862bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1863bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public MessageObserver(Handler handler, Context context) {
1864bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(handler);
1865bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mContentResolver = context.getContentResolver();
1866bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mThrottle = new Throttle("MessageObserver", this, handler);
1867bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1868bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1869bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void unregister() {
1870bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!mRegistered) {
1871bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1872bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1873bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mThrottle.cancelScheduledCallback();
1874bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mContentResolver.unregisterContentObserver(this);
1875bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRegistered = false;
1876bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1877bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1878bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void register(Uri notifyUri) {
1879bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            unregister();
1880bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mContentResolver.registerContentObserver(notifyUri, true, this);
1881bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRegistered = true;
1882bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1883bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1884bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1885bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean deliverSelfNotifications() {
1886bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return true;
1887bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1888bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1889bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1890bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onChange(boolean selfChange) {
1891bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mRegistered) {
1892bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mThrottle.onEvent();
1893bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1894bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1895bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1896bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** This method is delay-called by {@link Throttle} on the UI thread. */
1897bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1898bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void run() {
1899bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // This method is delay-called, so need to make sure if it's still registered.
1900bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mRegistered) {
1901bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                new ReloadMessageTask().cancelPreviousAndExecuteParallel();
1902bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1903bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1904bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1905bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1906bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updatePreviewIcon(MessageViewAttachmentInfo attachmentInfo) {
1907bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        new UpdatePreviewIconTask(attachmentInfo).executeParallel();
1908bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1909bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1910bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class UpdatePreviewIconTask extends EmailAsyncTask<Void, Void, Bitmap> {
1911bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @SuppressWarnings("hiding")
1912bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final Context mContext;
1913bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final MessageViewAttachmentInfo mAttachmentInfo;
1914bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1915bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public UpdatePreviewIconTask(MessageViewAttachmentInfo attachmentInfo) {
1916bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
1917bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mContext = getActivity();
1918bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAttachmentInfo = attachmentInfo;
1919bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1920bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1921bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1922bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Bitmap doInBackground(Void... params) {
1923bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return getPreviewIcon(mContext, mAttachmentInfo);
1924bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1925bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1926bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1927bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(Bitmap result) {
1928bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (result == null) {
1929bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1930bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1931bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAttachmentInfo.iconView.setImageBitmap(result);
1932bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1933bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1934bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1935bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean shouldShowImagesFor(String senderEmail) {
1936bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return Preferences.getPreferences(getActivity()).shouldShowImagesFor(senderEmail);
1937bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1938bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1939bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void setShowImagesForSender() {
1940bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        makeVisible(UiUtilities.getView(getView(), R.id.always_show_pictures_button), false);
1941bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Utility.showToast(getActivity(), R.string.message_view_always_show_pictures_confirmation);
1942bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1943bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Force redraw of the container.
1944bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateTabs(mTabFlags);
1945bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1946bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Address[] fromList = Address.unpack(mMessage.mFrom);
1947bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Preferences prefs = Preferences.getPreferences(getActivity());
1948bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        for (Address sender : fromList) {
1949bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String email = sender.getAddress();
1950bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            prefs.setSenderAsTrusted(email);
1951bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1952bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1953bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1954bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isMessageLoadedForTest() {
1955bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mIsMessageLoadedForTest;
1956bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1957bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1958bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void clearIsMessageLoadedForTest() {
1959bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mIsMessageLoadedForTest = true;
1960bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1961bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook}
1962