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.ListFragment;
21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.LoaderManager;
22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ClipData;
23bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ContentUris;
24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.Context;
25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.Loader;
26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.res.Configuration;
27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.res.Resources;
28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.database.Cursor;
29bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.Canvas;
30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.Point;
31bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.PointF;
32bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.Rect;
33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.Typeface;
34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.graphics.drawable.Drawable;
35bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Bundle;
36bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Parcelable;
37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.text.TextPaint;
38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.util.Log;
39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.ActionMode;
40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.DragEvent;
41bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.LayoutInflater;
42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.Menu;
43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.MenuInflater;
44bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.MenuItem;
45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.MotionEvent;
46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.View;
47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.View.DragShadowBuilder;
48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.View.OnDragListener;
49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.View.OnTouchListener;
50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.view.ViewGroup;
51bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.AdapterView;
52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.AdapterView.OnItemLongClickListener;
53bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.ListView;
54bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.TextView;
55bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.widget.Toast;
56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Controller;
58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Email;
59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.MessageListContext;
60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.NotificationController;
61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.R;
62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.RefreshManager;
63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.activity.MessagesAdapter.SearchResultsCursor;
64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.provider.EmailProvider;
65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.Logging;
66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.Account;
67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.EmailContent.Message;
68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.Mailbox;
69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.utility.EmailAsyncTask;
70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.utility.Utility;
71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.google.common.annotations.VisibleForTesting;
72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.HashMap;
74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.Set;
75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/**
77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Message list.
78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * See the class javadoc for {@link MailboxListFragment} for notes on {@link #getListView()} and
80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * {@link #isViewCreated()}.
81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */
82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic class MessageListFragment extends ListFragment
83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        implements OnItemLongClickListener, MessagesAdapter.Callback,
84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MoveMessageToDialog.Callback, OnDragListener, OnTouchListener {
85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final String BUNDLE_LIST_STATE = "MessageListFragment.state.listState";
86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final String BUNDLE_KEY_SELECTED_MESSAGE_ID
87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            = "messageListFragment.state.listState.selected_message_id";
88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int LOADER_ID_MESSAGES_LOADER = 1;
90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Argument name(s) */
92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final String ARG_LIST_CONTEXT = "listContext";
93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // Controller access
95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Controller mController;
96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private RefreshManager mRefreshManager;
97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final RefreshListener mRefreshListener = new RefreshListener();
98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // UI Support
100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Activity mActivity;
101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Callback mCallback = EmptyCallback.INSTANCE;
102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mIsViewCreated;
103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mListPanel;
105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mListFooterView;
106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mListFooterText;
107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private View mListFooterProgress;
108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ViewGroup mSearchHeader;
109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ViewGroup mWarningContainer;
110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mSearchHeaderText;
111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private TextView mSearchHeaderCount;
112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int LIST_FOOTER_MODE_NONE = 0;
114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final int LIST_FOOTER_MODE_MORE = 1;
115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mListFooterMode;
116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private MessagesAdapter mListAdapter;
118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mIsFirstLoad;
119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** ID of the message to hightlight. */
121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private long mSelectedMessageId = -1;
122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Account mAccount;
124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Mailbox mMailbox;
125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** The original mailbox being searched, if this list is showing search results. */
126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Mailbox mSearchedMailbox;
127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mIsEasAccount;
128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mIsRefreshable;
129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int mCountTotalAccounts;
130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // Misc members
132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mShowSendCommand;
134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mShowMoveCommand;
135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * If true, we disable the CAB even if there are selected messages.
138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * It's used in portrait on the tablet when the message view becomes visible and the message
139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * list gets pushed out of the screen, in which case we want to keep the selection but the CAB
140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * should be gone.
141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mDisableCab;
143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** true between {@link #onResume} and {@link #onPause}. */
145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean mResumed;
146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * {@link ActionMode} shown when 1 or more message is selected.
149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private ActionMode mSelectionMode;
151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private SelectionModeCallback mLastSelectionModeCallback;
152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Parcelable mSavedListState;
154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Callback interface that owning activities must implement
159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public interface Callback {
161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static final int TYPE_REGULAR = 0;
162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static final int TYPE_DRAFT = 1;
163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static final int TYPE_TRASH = 2;
164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when the specified mailbox does not exist.
167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMailboxNotFound(boolean firstLoad);
169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when the user wants to open a message.
172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Note {@code mailboxId} is of the actual mailbox of the message, which is different from
173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * {@link MessageListFragment#getMailboxId} if it's magic mailboxes.
174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *
175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param messageId the message ID of the message
176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param messageMailboxId the mailbox ID of the message.
177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *     This will never take values like {@link Mailbox#QUERY_ALL_INBOXES}.
178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param listMailboxId the mailbox ID of the listbox shown on this fragment.
179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *     This can be that of a magic mailbox, e.g.  {@link Mailbox#QUERY_ALL_INBOXES}.
180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param type {@link #TYPE_REGULAR}, {@link #TYPE_DRAFT} or {@link #TYPE_TRASH}.
181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId,
183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                int type);
184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when an operation is initiated that can potentially advance the current
187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * message selection (e.g. a delete operation may advance the selection).
188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param affectedMessages the messages the operation will apply to
189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onAdvancingOpAccepted(Set<Long> affectedMessages);
191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when a drag & drop is initiated.
194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         *
195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @return true if drag & drop is allowed
196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onDragStarted();
198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Called when a drag & drop is ended.
201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onDragEnded();
203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static final class EmptyCallback implements Callback {
206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public static final Callback INSTANCE = new EmptyCallback();
207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMailboxNotFound(boolean isFirstLoad) {
210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMessageOpen(
214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                long messageId, long messageMailboxId, long listMailboxId, int type) {
215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onDragStarted() {
223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return false; // We don't know -- err on the safe side.
224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onDragEnded() {
228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Create a new instance with initialization parameters.
233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * This fragment should be created only with this method.  (Arguments should always be set.)
235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param listContext The list context to show messages for
237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public static MessageListFragment newInstance(MessageListContext listContext) {
239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final MessageListFragment instance = new MessageListFragment();
240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Bundle args = new Bundle();
241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        args.putParcelable(ARG_LIST_CONTEXT, listContext);
242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        instance.setArguments(args);
243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return instance;
244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * The context describing the contents to be shown in the list.
248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Do not use directly; instead, use the getters such as {@link #getAccountId()}.
249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * <p><em>NOTE:</em> Although we cannot force these to be immutable using Java language
250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * constructs, this <em>must</em> be considered immutable.
251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private MessageListContext mListContext;
253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void initializeArgCache() {
255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mListContext != null) return;
256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListContext = getArguments().getParcelable(ARG_LIST_CONTEXT);
257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return the account ID passed to {@link #newInstance}.  Safe to call even before onCreate.
261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * NOTE it may return {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public long getAccountId() {
265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initializeArgCache();
266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mListContext.mAccountId;
267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return the mailbox ID passed to {@link #newInstance}.  Safe to call even before onCreate.
271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public long getMailboxId() {
273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initializeArgCache();
274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mListContext.getMailboxId();
275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if the mailbox is a combined mailbox.  Safe to call even before onCreate.
279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
280bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isCombinedMailbox() {
281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return getAccountId() == Account.ACCOUNT_ID_COMBINED_VIEW;
282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public MessageListContext getListContext() {
285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initializeArgCache();
286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mListContext;
287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return Whether or not initial data is loaded in this list.
291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean hasDataLoaded() {
293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mCountTotalAccounts > 0;
294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return The account object, when known. Null if not yet known.
298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public Account getAccount() {
300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mAccount;
301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return The mailbox where the messages belong in, when known. Null if not yet known.
305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public Mailbox getMailbox() {
307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mMailbox;
308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return Whether or not this message list is showing a user's inbox.
312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *     Note that combined inbox view is treated as an inbox view.
313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isInboxList() {
315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MessageListContext listContext = getListContext();
316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        long accountId = listContext.mAccountId;
317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return listContext.getMailboxId() == Mailbox.QUERY_ALL_INBOXES;
319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!hasDataLoaded()) {
322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // If the data hasn't finished loading, we don't have the full mailbox - infer from ID.
323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            long inboxId = Mailbox.findMailboxOfType(mActivity, accountId, Mailbox.TYPE_INBOX);
324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return listContext.getMailboxId() == inboxId;
325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return (mMailbox != null) && (mMailbox.mType == Mailbox.TYPE_INBOX);
327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return The mailbox being searched, when known. Null if not yet known or if not a search
331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *    result.
332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public Mailbox getSearchedMailbox() {
334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mSearchedMailbox;
335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onAttach(Activity activity) {
339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onAttach");
341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onAttach(activity);
343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onCreate(Bundle savedInstanceState) {
347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onCreate");
349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onCreate(savedInstanceState);
351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mActivity = getActivity();
353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        setHasOptionsMenu(true);
354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController = Controller.getInstance(mActivity);
355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mRefreshManager = RefreshManager.getInstance(mActivity);
356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListAdapter = new MessagesAdapter(mActivity, this, getListContext().isSearch());
358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mIsFirstLoad = true;
359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public View onCreateView(
363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onCreateView");
366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Use a custom layout, which includes the original layout with "send messages" panel.
368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        View root = inflater.inflate(R.layout.message_list_fragment,null);
369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mIsViewCreated = true;
370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListPanel = UiUtilities.getView(root, R.id.list_panel);
371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return root;
372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void setLayout(ThreePaneLayout layout) {
375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (UiUtilities.useTwoPane(mActivity)) {
376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListAdapter.setLayout(layout);
377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void initSearchHeader() {
381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mSearchHeader == null) {
382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ViewGroup root = (ViewGroup) getView();
383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSearchHeader = (ViewGroup) LayoutInflater.from(mActivity).inflate(
384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    R.layout.message_list_search_header, root, false);
385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSearchHeaderText = UiUtilities.getView(mSearchHeader, R.id.search_header_text);
386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSearchHeaderCount = UiUtilities.getView(mSearchHeader, R.id.search_count);
387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Add above the actual list.
389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            root.addView(mSearchHeader, 0);
390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
393bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
394bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if the content view is created and not destroyed yet. (i.e. between
395bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * {@link #onCreateView} and {@link #onDestroyView}.
396bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
397bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean isViewCreated() {
398bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Note that we don't use "getView() != null".  This method is used in updateSelectionMode()
399bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // to determine if CAB shold be shown.  But because it's called from onDestroyView(), at
400bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // this point the fragment still has views but we want to hide CAB, we can't use
401bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // getView() here.
402bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mIsViewCreated;
403bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
404bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
405bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
406bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onActivityCreated(Bundle savedInstanceState) {
407bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
408bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onActivityCreated");
409bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
410bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onActivityCreated(savedInstanceState);
411bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
412bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final ListView lv = getListView();
413bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        lv.setOnItemLongClickListener(this);
414bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        lv.setOnTouchListener(this);
415bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        lv.setItemsCanFocus(false);
416bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
417bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
418bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListFooterView = getActivity().getLayoutInflater().inflate(
419bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                R.layout.message_list_item_footer, lv, false);
420bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        setEmptyText(getString(R.string.message_list_no_messages));
421bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
422bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (savedInstanceState != null) {
423bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Fragment doesn't have this method.  Call it manually.
424bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            restoreInstanceState(savedInstanceState);
425bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
426bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
427bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        startLoading();
428bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
429bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        UiUtilities.installFragment(this);
430bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
431bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
432bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
433bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onStart() {
434bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
435bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onStart");
436bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
437bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onStart();
438bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
439bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
440bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
441bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onResume() {
442bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
443bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onResume");
444bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
445bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onResume();
446bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        adjustMessageNotification(false);
447bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mRefreshManager.registerListener(mRefreshListener);
448bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mResumed = true;
449bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
450bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
451bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
452bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onPause() {
453bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
454bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onPause");
455bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
456bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mResumed = false;
457bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSavedListState = getListView().onSaveInstanceState();
458bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        adjustMessageNotification(true);
459bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onPause();
460bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
461bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
462bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
463bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onStop() {
464bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
465bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onStop");
466bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
467bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mTaskTracker.cancellAllInterrupt();
468bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mRefreshManager.unregisterListener(mRefreshListener);
469bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
470bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onStop();
471bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
472bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
473bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
474bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDestroyView() {
475bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
476bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDestroyView");
477bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
478bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mIsViewCreated = false; // Clear this first for updateSelectionMode(). See isViewCreated().
479bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        UiUtilities.uninstallFragment(this);
480bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateSelectionMode();
481bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
482bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Reset the footer mode since we just blew away the footer view we were holding on to.
483bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // This will get re-updated when/if this fragment is restored.
484bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListFooterMode = LIST_FOOTER_MODE_NONE;
485bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDestroyView();
486bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
487bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
488bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
489bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDestroy() {
490bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
491bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDestroy");
492bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
493bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
494bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        finishSelectionMode();
495bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDestroy();
496bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
497bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
498bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
499bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onDetach() {
500bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
501bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onDetach");
502bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
503bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onDetach();
504bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
505bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
506bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
507bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onSaveInstanceState(Bundle outState) {
508bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
509bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " onSaveInstanceState");
510bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
511bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        super.onSaveInstanceState(outState);
512bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListAdapter.onSaveInstanceState(outState);
513bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (isViewCreated()) {
514bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            outState.putParcelable(BUNDLE_LIST_STATE, getListView().onSaveInstanceState());
515bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
516bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        outState.putLong(BUNDLE_KEY_SELECTED_MESSAGE_ID, mSelectedMessageId);
517bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
518bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
519bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @VisibleForTesting
520bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    void restoreInstanceState(Bundle savedInstanceState) {
521bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
522bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " restoreInstanceState");
523bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
524bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListAdapter.loadState(savedInstanceState);
525bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE);
526bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSelectedMessageId = savedInstanceState.getLong(BUNDLE_KEY_SELECTED_MESSAGE_ID);
527bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
528bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
529bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
530bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onPrepareOptionsMenu(Menu menu) {
531bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MenuItem send = menu.findItem(R.id.send);
532bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (send != null) {
533bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            send.setVisible(mShowSendCommand);
534bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
535bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
536bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
537bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
538bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean onOptionsItemSelected(MenuItem item) {
539bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (item.getItemId()) {
540bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case R.id.send:
541bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onSendPendingMessages();
542bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return true;
543bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
544bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
545bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return false;
546bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
547bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
548bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void setCallback(Callback callback) {
549bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mCallback = (callback != null) ? callback : EmptyCallback.INSTANCE;
550bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
551bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
552bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
553bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * This method must be called when the fragment is hidden/shown.
554bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
555bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onHidden(boolean hidden) {
556bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // When hidden, we need to disable CAB.
557bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (hidden == mDisableCab) {
558bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
559bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
560bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mDisableCab = hidden;
561bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateSelectionMode();
562bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
563bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
564bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void setSelectedMessage(long messageId) {
565bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mSelectedMessageId == messageId) {
566bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
567bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
568bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSelectedMessageId = messageId;
569bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mResumed) {
570bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            highlightSelectedMessage(true);
571bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
572bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
573bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
574bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
575bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if the mailbox is refreshable.  false otherwise, or unknown yet.
576bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
577bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isRefreshable() {
578bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mIsRefreshable;
579bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
580bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
581bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
582bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return the number of messages that are currently selected.
583bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
584bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int getSelectedCount() {
585bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mListAdapter.getSelectedSet().size();
586bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
587bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
588bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
589bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if the list is in the "selection" mode.
590bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
591bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isInSelectionMode() {
592bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mSelectionMode != null;
593bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
594bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
595bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
596bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called when a message is clicked.
597bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
598bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
599bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onListItemClick(ListView parent, View view, int position, long id) {
600bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (view != mListFooterView) {
601bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageListItem itemView = (MessageListItem) view;
602bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            onMessageOpen(itemView.mMailboxId, id);
603bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
604bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            doFooterClick();
605bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
606bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
607bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
608bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    // This is tentative drag & drop UI
609bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static class ShadowBuilder extends DragShadowBuilder {
610bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static Drawable sBackground;
611bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Paint information for the move message text */
612bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static TextPaint sMessagePaint;
613bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Paint information for the message count */
614bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static TextPaint sCountPaint;
615bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** The x location of any touch event; used to ensure the drag overlay is drawn correctly */
616bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static int sTouchX;
617bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
618bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Width of the draggable view */
619bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final int mDragWidth;
620bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Height of the draggable view */
621bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final int mDragHeight;
622bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
623bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final String mMessageText;
624bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final PointF mMessagePoint;
625bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
626bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final String mCountText;
627bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final PointF mCountPoint;
628bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private int mOldOrientation = Configuration.ORIENTATION_UNDEFINED;
629bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
630bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Margin applied to the right of count text */
631bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static float sCountMargin;
632bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Margin applied to left of the message text */
633bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static float sMessageMargin;
634bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /** Vertical offset of the drag view */
635bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private static int sDragOffset;
636bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
637bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public ShadowBuilder(View view, int count) {
638bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(view);
639bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Resources res = view.getResources();
640bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int newOrientation = res.getConfiguration().orientation;
641bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
642bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mDragHeight = view.getHeight();
643bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mDragWidth = view.getWidth();
644bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
645bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // TODO: Can we define a layout for the contents of the drag area?
646bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (sBackground == null || mOldOrientation != newOrientation) {
647bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mOldOrientation = newOrientation;
648bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
649bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sBackground = res.getDrawable(R.drawable.list_pressed_holo);
650bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sBackground.setBounds(0, 0, mDragWidth, mDragHeight);
651bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
652bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sDragOffset = (int)res.getDimension(R.dimen.message_list_drag_offset);
653bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
654bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sMessagePaint = new TextPaint();
655bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                float messageTextSize;
656bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                messageTextSize = res.getDimension(R.dimen.message_list_drag_message_font_size);
657bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sMessagePaint.setTextSize(messageTextSize);
658bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sMessagePaint.setTypeface(Typeface.DEFAULT_BOLD);
659bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sMessagePaint.setAntiAlias(true);
660bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sMessageMargin = res.getDimension(R.dimen.message_list_drag_message_right_margin);
661bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
662bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sCountPaint = new TextPaint();
663bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                float countTextSize;
664bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                countTextSize = res.getDimension(R.dimen.message_list_drag_count_font_size);
665bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sCountPaint.setTextSize(countTextSize);
666bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sCountPaint.setTypeface(Typeface.DEFAULT_BOLD);
667bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sCountPaint.setAntiAlias(true);
668bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sCountMargin = res.getDimension(R.dimen.message_list_drag_count_left_margin);
669bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
670bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
671bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Calculate layout positions
672bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Rect b = new Rect();
673bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
674bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageText = res.getQuantityString(R.plurals.move_messages, count, count);
675bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            sMessagePaint.getTextBounds(mMessageText, 0, mMessageText.length(), b);
676bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessagePoint = new PointF(mDragWidth - b.right - sMessageMargin,
677bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    (mDragHeight - b.top)/ 2);
678bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
679bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCountText = Integer.toString(count);
680bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            sCountPaint.getTextBounds(mCountText, 0, mCountText.length(), b);
681bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCountPoint = new PointF(sCountMargin,
682bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    (mDragHeight - b.top) / 2);
683bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
684bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
685bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
686bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
687bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            shadowSize.set(mDragWidth, mDragHeight);
688bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            shadowTouchPoint.set(sTouchX, (mDragHeight / 2) + sDragOffset);
689bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
690bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
691bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
692bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onDrawShadow(Canvas canvas) {
693bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super.onDrawShadow(canvas);
694bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            sBackground.draw(canvas);
695bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            canvas.drawText(mMessageText, mMessagePoint.x, mMessagePoint.y, sMessagePaint);
696bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            canvas.drawText(mCountText, mCountPoint.x, mCountPoint.y, sCountPaint);
697bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
698bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
699bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
700bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
701bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean onDrag(View view, DragEvent event) {
702bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch(event.getAction()) {
703bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case DragEvent.ACTION_DRAG_ENDED:
704bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (event.getResult()) {
705bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    onDeselectAll(); // Clear the selection
706bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
707bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mCallback.onDragEnded();
708bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
709bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
710bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return false;
711bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
712bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
713bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
714bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean onTouch(View v, MotionEvent event) {
715bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (event.getAction() == MotionEvent.ACTION_DOWN) {
716bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Save the touch location to draw the drag overlay at the correct location
717bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ShadowBuilder.sTouchX = (int)event.getX();
718bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
719bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // don't do anything, let the system process the event
720bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return false;
721bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
722bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
723bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
724bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
725bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (view != mListFooterView) {
726bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Always toggle the item.
727bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessageListItem listItem = (MessageListItem) view;
728bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean toggled = false;
729bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!mListAdapter.isSelected(listItem)) {
730bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                toggleSelection(listItem);
731bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                toggled = true;
732bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
733bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
734bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Additionally, check to see if we can drag the item.
735bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!mCallback.onDragStarted()) {
736bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return toggled; // D&D not allowed.
737bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
738bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // We can't move from combined accounts view
739bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // We also need to check the actual mailbox to see if we can move items from it
740bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final long mailboxId = getMailboxId();
741bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mAccount == null || mMailbox == null) {
742bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return false;
743bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else if (mailboxId > 0 && !mMailbox.canHaveMessagesMoved()) {
744bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return false;
745bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
746bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Start drag&drop.
747bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
748bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Create ClipData with the Uri of the message we're long clicking
749bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            ClipData data = ClipData.newUri(mActivity.getContentResolver(),
750bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    MessageListItem.MESSAGE_LIST_ITEMS_CLIP_LABEL, Message.CONTENT_URI.buildUpon()
751bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    .appendPath(Long.toString(listItem.mMessageId))
752bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    .appendQueryParameter(
753bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            EmailProvider.MESSAGE_URI_PARAMETER_MAILBOX_ID,
754bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            Long.toString(mailboxId))
755bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            .build());
756bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Set<Long> selectedMessageIds = mListAdapter.getSelectedSet();
757bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int size = selectedMessageIds.size();
758bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Add additional Uri's for any other selected messages
759bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            for (Long messageId: selectedMessageIds) {
760bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (messageId.longValue() != listItem.mMessageId) {
761bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    data.addItem(new ClipData.Item(
762bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            ContentUris.withAppendedId(Message.CONTENT_URI, messageId)));
763bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
764bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
765bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Start dragging now
766bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            listItem.setOnDragListener(this);
767bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            listItem.startDrag(data, new ShadowBuilder(listItem, size), null, 0);
768bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return true;
769bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
770bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return false;
771bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
772bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
773bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void toggleSelection(MessageListItem itemView) {
774bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        itemView.invalidate();
775bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListAdapter.toggleSelected(itemView);
776bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
777bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
778bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
779bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Called when a message on the list is selected
780bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
781bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param messageMailboxId the actual mailbox ID of the message.  Note it's different than
782bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *        what is returned by {@link #getMailboxId()} for combined mailboxes.
783bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *        ({@link #getMailboxId()} may return special mailbox values such as
784bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *        {@link Mailbox#QUERY_ALL_INBOXES})
785bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param messageId ID of the message to open.
786bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
787bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onMessageOpen(final long messageMailboxId, final long messageId) {
788bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if ((mMailbox != null) && (mMailbox.mId == messageMailboxId)) {
789bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Normal case - the message belongs in the mailbox list we're viewing.
790bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCallback.onMessageOpen(messageId, messageMailboxId,
791bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    getMailboxId(), callbackTypeForMailboxType(mMailbox.mType));
792bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
793bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
794bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
795bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Weird case - a virtual mailbox where the messages could come from different mailbox
796bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // types - here we have to query the DB for the type.
797bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        new MessageOpenTask(messageMailboxId, messageId).cancelPreviousAndExecuteParallel();
798bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
799bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
800bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int callbackTypeForMailboxType(int mailboxType) {
801bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (mailboxType) {
802bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case Mailbox.TYPE_DRAFTS:
803bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return Callback.TYPE_DRAFT;
804bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case Mailbox.TYPE_TRASH:
805bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return Callback.TYPE_TRASH;
806bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            default:
807bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return Callback.TYPE_REGULAR;
808bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
809bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
810bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
811bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
812bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Task to look up the mailbox type for a message, and kicks the callback.
813bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
814bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class MessageOpenTask extends EmailAsyncTask<Void, Void, Integer> {
815bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final long mMessageMailboxId;
816bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private final long mMessageId;
817bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
818bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public MessageOpenTask(long messageMailboxId, long messageId) {
819bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            super(mTaskTracker);
820bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageMailboxId = messageMailboxId;
821bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMessageId = messageId;
822bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
823bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
824bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
825bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected Integer doInBackground(Void... params) {
826bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Restore the mailbox type.  Note we can't use mMailbox.mType here, because
827bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // we don't have mMailbox for combined mailbox.
828bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // ("All Starred" can contain any kind of messages.)
829bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return callbackTypeForMailboxType(
830bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    Mailbox.getMailboxType(mActivity, mMessageMailboxId));
831bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
832bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
833bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
834bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        protected void onSuccess(Integer type) {
835bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (type == null) {
836bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
837bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
838bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCallback.onMessageOpen(mMessageId, mMessageMailboxId, getMailboxId(), type);
839bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
840bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
841bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
842bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showMoveMessagesDialog(Set<Long> selectedSet) {
843bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        long[] messageIds = Utility.toPrimitiveLongArray(selectedSet);
844bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MoveMessageToDialog dialog = MoveMessageToDialog.newInstance(messageIds, this);
845bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        dialog.show(getFragmentManager(), "dialog");
846bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
847bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
848bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
849bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onMoveToMailboxSelected(long newMailboxId, long[] messageIds) {
850bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Context context = getActivity();
851bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (context == null) {
852bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Detached from activity. This callback was really delayed or a monkey was involved.
853bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
854bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
855bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
856bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mCallback.onAdvancingOpAccepted(Utility.toLongSet(messageIds));
857bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ActivityHelper.moveMessages(context, newMailboxId, messageIds);
858bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
859bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Move is async, so we can't refresh now.  Instead, just clear the selection.
860bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        onDeselectAll();
861bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
862bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
863bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
864bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Refresh the list.  NOOP for special mailboxes (e.g. combined inbox).
865bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
866bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Note: Manual refresh is enabled even for push accounts.
867bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
868bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onRefresh(boolean userRequest) {
869bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mIsRefreshable) {
870bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRefreshManager.refreshMessageList(getAccountId(), getMailboxId(), userRequest);
871bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
872bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
873bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
874bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onDeselectAll() {
875bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListAdapter.clearSelection();
876bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (isInSelectionMode()) {
877bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            finishSelectionMode();
878bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
879bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
880bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
881bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
882bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Load more messages.  NOOP for special mailboxes (e.g. combined inbox).
883bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
884bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onLoadMoreMessages() {
885bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mIsRefreshable) {
886bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRefreshManager.loadMoreMessages(getAccountId(), getMailboxId());
887bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
888bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
889bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
890bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onSendPendingMessages() {
891bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        RefreshManager rm = RefreshManager.getInstance(mActivity);
892bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (getMailboxId() == Mailbox.QUERY_ALL_OUTBOX) {
893bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            rm.sendPendingMessagesForAllAccounts();
894bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else if (mMailbox != null) { // Magic boxes don't have a specific account id.
895bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            rm.sendPendingMessages(mMailbox.mAccountKey);
896bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
897bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
898bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
899bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
900bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Toggles a set read/unread states.  Note, the default behavior is "mark unread", so the
901bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * sense of the helper methods is "true=unread"; this may be called from the UI thread
902bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
903bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param selectedSet The current list of selected items
904bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
905bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void toggleRead(Set<Long> selectedSet) {
906bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        toggleMultiple(selectedSet, new MultiToggleHelper() {
907bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
908bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            @Override
909bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            public boolean getField(Cursor c) {
910bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return c.getInt(MessagesAdapter.COLUMN_READ) == 0;
911bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
912bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
913bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            @Override
914bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            public void setField(long messageId, boolean newValue) {
915bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mController.setMessageReadSync(messageId, !newValue);
916bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
917bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        });
918bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
919bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
920bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
921bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Toggles a set of favorites (stars); this may be called from the UI thread
922bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
923bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param selectedSet The current list of selected items
924bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
925bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void toggleFavorite(Set<Long> selectedSet) {
926bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        toggleMultiple(selectedSet, new MultiToggleHelper() {
927bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
928bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            @Override
929bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            public boolean getField(Cursor c) {
930bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return c.getInt(MessagesAdapter.COLUMN_FAVORITE) != 0;
931bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
932bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
933bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            @Override
934bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            public void setField(long messageId, boolean newValue) {
935bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mController.setMessageFavoriteSync(messageId, newValue);
936bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             }
937bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        });
938bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
939bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
940bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void deleteMessages(Set<Long> selectedSet) {
941bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final long[] messageIds = Utility.toPrimitiveLongArray(selectedSet);
942bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController.deleteMessages(messageIds);
943bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Toast.makeText(mActivity, mActivity.getResources().getQuantityString(
944bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                R.plurals.message_deleted_toast, messageIds.length), Toast.LENGTH_SHORT).show();
945bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        selectedSet.clear();
946bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Message deletion is async... Can't refresh the list immediately.
947bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
948bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
949bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private interface MultiToggleHelper {
950bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
951bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Return true if the field of interest is "set".  If one or more are false, then our
952bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * bulk action will be to "set".  If all are set, our bulk action will be to "clear".
953bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param c the cursor, positioned to the item of interest
954bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @return true if the field at this row is "set"
955bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
956bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean getField(Cursor c);
957bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
958bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
959bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Set or clear the field of interest; setField is called asynchronously via EmailAsyncTask
960bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param messageId the message id of the current message
961bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * @param newValue the new value to be set at this row
962bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
963bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void setField(long messageId, boolean newValue);
964bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
965bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
966bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
967bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Toggle multiple fields in a message, using the following logic:  If one or more fields
968bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * are "clear", then "set" them.  If all fields are "set", then "clear" them all.  Provider
969bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * calls are applied asynchronously in setField
970bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
971bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param selectedSet the set of messages that are selected
972bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param helper functions to implement the specific getter & setter
973bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
974bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void toggleMultiple(final Set<Long> selectedSet, final MultiToggleHelper helper) {
975bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Cursor c = mListAdapter.getCursor();
976bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (c == null || c.isClosed()) {
977bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
978bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
979bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
980bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final HashMap<Long, Boolean> setValues = new HashMap<Long, Boolean>();
981bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        boolean allWereSet = true;
982bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
983bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        c.moveToPosition(-1);
984bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        while (c.moveToNext()) {
985bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            long id = c.getInt(MessagesAdapter.COLUMN_ID);
986bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (selectedSet.contains(id)) {
987bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                boolean value = helper.getField(c);
988bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setValues.put(id, value);
989bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                allWereSet = allWereSet && value;
990bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
991bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
992bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
993bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!setValues.isEmpty()) {
994bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final boolean newValue = !allWereSet;
995bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            c.moveToPosition(-1);
996bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // TODO: we should probably put up a dialog or some other progress indicator for this.
997bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            EmailAsyncTask.runAsyncParallel(new Runnable() {
998bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook               @Override
999bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                public void run() {
1000bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                   for (long id : setValues.keySet()) {
1001bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                       if (setValues.get(id) != newValue) {
1002bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                           helper.setField(id, newValue);
1003bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                       }
1004bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                   }
1005bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }});
1006bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1007bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1008bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1009bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1010bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Test selected messages for showing appropriate labels
1011bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param selectedSet
1012bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param columnId
1013bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param defaultflag
1014bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true when the specified flagged message is selected
1015bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1016bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean testMultiple(Set<Long> selectedSet, int columnId, boolean defaultflag) {
1017bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final Cursor c = mListAdapter.getCursor();
1018bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (c == null || c.isClosed()) {
1019bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return false;
1020bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1021bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        c.moveToPosition(-1);
1022bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        while (c.moveToNext()) {
1023bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            long id = c.getInt(MessagesAdapter.COLUMN_ID);
1024bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (selectedSet.contains(Long.valueOf(id))) {
1025bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (c.getInt(columnId) == (defaultflag ? 1 : 0)) {
1026bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    return true;
1027bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1028bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1029bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1030bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return false;
1031bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1032bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1033bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1034bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if one or more non-starred messages are selected.
1035bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1036bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean doesSelectionContainNonStarredMessage() {
1037bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return testMultiple(mListAdapter.getSelectedSet(), MessagesAdapter.COLUMN_FAVORITE,
1038bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                false);
1039bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1040bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1041bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1042bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @return true if one or more read messages are selected.
1043bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1044bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean doesSelectionContainReadMessage() {
1045bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return testMultiple(mListAdapter.getSelectedSet(), MessagesAdapter.COLUMN_READ, true);
1046bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1047bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1048bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1049bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Implements a timed refresh of "stale" mailboxes.  This should only happen when
1050bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * multiple conditions are true, including:
1051bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *   Only refreshable mailboxes.
1052bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *   Only when the mailbox is "stale" (currently set to 5 minutes since last refresh)
1053bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Note we do this even if it's a push account; even on Exchange only inbox can be pushed.
1054bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1055bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void autoRefreshStaleMailbox() {
1056bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!mIsRefreshable) {
1057bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Not refreshable (special box such as drafts, or magic boxes)
1058bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1059bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1060bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!mRefreshManager.isMailboxStale(getMailboxId())) {
1061bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1062bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1063bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        onRefresh(false);
1064bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1065bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1066bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Implements {@link MessagesAdapter.Callback} */
1067bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
1068bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onAdapterFavoriteChanged(MessageListItem itemView, boolean newFavorite) {
1069bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mController.setMessageFavorite(itemView.mMessageId, newFavorite);
1070bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1071bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1072bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Implements {@link MessagesAdapter.Callback} */
1073bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
1074bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void onAdapterSelectedChanged(MessageListItem itemView, boolean newSelected,
1075bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int mSelectedCount) {
1076bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateSelectionMode();
1077bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1078bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1079bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateSearchHeader(Cursor cursor) {
1080bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        MessageListContext listContext = getListContext();
1081bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!listContext.isSearch() || cursor == null) {
1082bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            UiUtilities.setVisibilitySafe(mSearchHeader, View.GONE);
1083bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1084bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1085bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1086bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        SearchResultsCursor searchCursor = (SearchResultsCursor) cursor;
1087bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        initSearchHeader();
1088bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSearchHeader.setVisibility(View.VISIBLE);
1089bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        String header = mActivity.getString(R.string.search_header_text_fmt);
1090bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSearchHeaderText.setText(header);
1091bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        int resultCount = searchCursor.getResultsCount();
1092bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Don't show a negative value here; this means that the server request failed
1093bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // TODO Use some other text for this case (e.g. "search failed")?
1094bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (resultCount < 0) {
1095bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            resultCount = 0;
1096bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1097bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSearchHeaderCount.setText(UiUtilities.getMessageCountForUi(
1098bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mActivity, resultCount, false /* replaceZeroWithBlank */));
1099bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private int determineFooterMode() {
1102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        int result = LIST_FOOTER_MODE_NONE;
1103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if ((mMailbox == null)
1104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                || (mMailbox.mType == Mailbox.TYPE_OUTBOX)
1105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                || (mMailbox.mType == Mailbox.TYPE_DRAFTS)) {
1106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return result; // No footer
1107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mMailbox.mType == Mailbox.TYPE_SEARCH) {
1109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Determine how many results have been loaded.
1110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Cursor c = mListAdapter.getCursor();
1111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (c == null || c.isClosed()) {
1112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Unknown yet - don't do anything.
1113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return result;
1114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int total = ((SearchResultsCursor) c).getResultsCount();
1116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int loaded = c.getCount();
1117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (loaded < total) {
1119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                result = LIST_FOOTER_MODE_MORE;
1120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else if (!mIsEasAccount) {
1122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // IMAP, POP has "load more" for regular mailboxes.
1123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            result = LIST_FOOTER_MODE_MORE;
1124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return result;
1126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateFooterView() {
1129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Only called from onLoadFinished -- always has views.
1130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        int mode = determineFooterMode();
1131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mListFooterMode == mode) {
1132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mListFooterMode = mode;
1135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ListView lv = getListView();
1137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mListFooterMode != LIST_FOOTER_MODE_NONE) {
1138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            lv.addFooterView(mListFooterView);
1139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (getListAdapter() != null) {
1140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Already have an adapter - reset it to force the mode. But save the scroll
1141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // position so that we don't get kicked to the top.
1142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Parcelable listState = lv.onSaveInstanceState();
1143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setListAdapter(mListAdapter);
1144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                lv.onRestoreInstanceState(listState);
1145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListFooterProgress = mListFooterView.findViewById(R.id.progress);
1148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListFooterText = (TextView) mListFooterView.findViewById(R.id.main_text);
1149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            lv.removeFooterView(mListFooterView);
1151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateListFooter();
1153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Set the list footer text based on mode and the current "network active" status
1157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateListFooter() {
1159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mListFooterMode != LIST_FOOTER_MODE_NONE) {
1160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int footerTextId = 0;
1161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            switch (mListFooterMode) {
1162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case LIST_FOOTER_MODE_MORE:
1163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    boolean active = mRefreshManager.isMessageListRefreshing(getMailboxId());
1164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    footerTextId = active ? R.string.status_loading_messages
1165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            : R.string.message_list_load_more_messages_action;
1166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mListFooterProgress.setVisibility(active ? View.VISIBLE : View.GONE);
1167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListFooterText.setText(footerTextId);
1170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Handle a click in the list footer, which changes meaning depending on what we're looking at.
1175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void doFooterClick() {
1177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        switch (mListFooterMode) {
1178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case LIST_FOOTER_MODE_NONE: // should never happen
1179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
1180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            case LIST_FOOTER_MODE_MORE:
1181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onLoadMoreMessages();
1182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                break;
1183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void showSendCommand(boolean show) {
1187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (show != mShowSendCommand) {
1188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mShowSendCommand = show;
1189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mActivity.invalidateOptionsMenu();
1190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateMailboxSpecificActions() {
1194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final boolean isOutbox = (getMailboxId() == Mailbox.QUERY_ALL_OUTBOX)
1195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                || ((mMailbox != null) && (mMailbox.mType == Mailbox.TYPE_OUTBOX));
1196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        showSendCommand(isOutbox && (mListAdapter != null) && (mListAdapter.getCount() > 0));
1197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // A null account/mailbox means we're in a combined view. We show the move icon there,
1199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // even though it may be the case that we can't move messages from one of the mailboxes.
1200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // There's no good way to tell that right now, though.
1201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mShowMoveCommand = (mAccount == null || mAccount.supportsMoveMessages(getActivity()))
1202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                && (mMailbox == null || mMailbox.canHaveMessagesMoved());
1203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Enable mailbox specific actions on the UIController level if needed.
1205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mActivity.invalidateOptionsMenu();
1206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Adjusts message notification depending upon the state of the fragment and the currently
1210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * viewed mailbox. If the fragment is resumed, notifications for the current mailbox may
1211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * be suspended. Otherwise, notifications may be re-activated. Not all mailbox types are
1212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * supported for notifications. These include (but are not limited to) special mailboxes
1213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * such as {@link Mailbox#QUERY_ALL_DRAFTS}, {@link Mailbox#QUERY_ALL_FAVORITES}, etc...
1214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param updateLastSeenKey If {@code true}, the last seen message key for the currently
1216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *                          viewed mailbox will be updated.
1217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void adjustMessageNotification(boolean updateLastSeenKey) {
1219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final long accountId = getAccountId();
1220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final long mailboxId = getMailboxId();
1221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mailboxId == Mailbox.QUERY_ALL_INBOXES || mailboxId > 0) {
1222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (updateLastSeenKey) {
1223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Utility.updateLastSeenMessageKey(mActivity, accountId);
1224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            NotificationController notifier = NotificationController.getInstance(mActivity);
1226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            notifier.suspendMessageNotification(mResumed, accountId);
1227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void startLoading() {
1231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
1232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Log.d(Logging.LOG_TAG, this + " startLoading");
1233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Clear the list. (ListFragment will show the "Loading" animation)
1235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        showSendCommand(false);
1236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        updateSearchHeader(null);
1237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Start loading...
1239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final LoaderManager lm = getLoaderManager();
1240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        lm.initLoader(LOADER_ID_MESSAGES_LOADER, null, LOADER_CALLBACKS);
1241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Timeout to show a warning, since some IMAP searches could take a long time. */
1244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final int SEARCH_WARNING_DELAY_MS = 10000;
1245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void onSearchLoadTimeout() {
1247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // Search is taking too long. Show an error message.
1248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        ViewGroup root = (ViewGroup) getView();
1249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Activity host = getActivity();
1250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (root != null && host != null) {
1251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListPanel.setVisibility(View.GONE);
1252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mWarningContainer = (ViewGroup) LayoutInflater.from(host).inflate(
1253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    R.layout.message_list_warning, root, false);
1254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            TextView title = UiUtilities.getView(mWarningContainer, R.id.message_title);
1255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            TextView message = UiUtilities.getView(mWarningContainer, R.id.message_warning);
1256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            title.setText(R.string.search_slow_warning_title);
1257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            message.setText(R.string.search_slow_warning_message);
1258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            root.addView(mWarningContainer);
1259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Loader callbacks for message list.
1264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private final LoaderManager.LoaderCallbacks<Cursor> LOADER_CALLBACKS =
1266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            new LoaderManager.LoaderCallbacks<Cursor>() {
1267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
1269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final MessageListContext listContext = getListContext();
1270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
1271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Log.d(Logging.LOG_TAG, MessageListFragment.this
1272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        + " onCreateLoader(messages) listContext=" + listContext);
1273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mListContext.isSearch()) {
1276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                final MessageListContext searchInfo = mListContext;
1277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Search results are not primed with local data, and so will usually be slow.
1279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // In some cases, they could take a long time to return, so we need to be robust.
1280bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setListShownNoAnimation(false);
1281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Utility.getMainThreadHandler().postDelayed(new Runnable() {
1282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    @Override
1283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    public void run() {
1284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (mListContext != searchInfo) {
1285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // Different list is being shown now.
1286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            return;
1287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
1288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (!mIsFirstLoad) {
1289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            // Something already returned. No need to do anything.
1290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            return;
1291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
1292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        onSearchLoadTimeout();
1293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
1294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }, SEARCH_WARNING_DELAY_MS);
1295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mIsFirstLoad = true;
1298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return MessagesAdapter.createLoader(getActivity(), listContext);
1299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
1303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
1304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Log.d(Logging.LOG_TAG, MessageListFragment.this
1305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        + " onLoadFinished(messages) mailboxId=" + getMailboxId());
1306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MessagesAdapter.MessagesCursor cursor = (MessagesAdapter.MessagesCursor) c;
1308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Update the list
1310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListAdapter.swapCursor(cursor);
1311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (!cursor.mIsFound) {
1313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mCallback.onMailboxNotFound(mIsFirstLoad);
1314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                return;
1315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Get the "extras" part.
1318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAccount = cursor.mAccount;
1319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMailbox = cursor.mMailbox;
1320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mIsEasAccount = cursor.mIsEasAccount;
1321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mIsRefreshable = cursor.mIsRefreshable;
1322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCountTotalAccounts = cursor.mCountTotalAccounts;
1323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // If this is a search result, open the first message unless we are
1325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // restoring the message position from saved state, in which case,
1326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // mSelectedMessageId was already set and should be respected.
1327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (UiUtilities.useTwoPane(getActivity()) && mIsFirstLoad && mListContext.isSearch()
1328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    && cursor.getCount() > 0
1329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    && UiUtilities.showTwoPaneSearchResults(getActivity())
1330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    && mSelectedMessageId == -1) {
1331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                cursor.moveToFirst();
1332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onMessageOpen(getMailboxId(), cursor.getLong(MessagesAdapter.COLUMN_ID));
1333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Suspend message notifications as long as we're resumed
1336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            adjustMessageNotification(false);
1337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // If this is a search mailbox, set the query; otherwise, clear it
1339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mIsFirstLoad) {
1340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (mMailbox != null && mMailbox.mType == Mailbox.TYPE_SEARCH) {
1341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mListAdapter.setQuery(getListContext().getSearchParams().mFilter);
1342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mSearchedMailbox = ((SearchResultsCursor) c).getSearchedMailbox();
1343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                } else {
1344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mListAdapter.setQuery(null);
1345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mSearchedMailbox = null;
1346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                updateMailboxSpecificActions();
1348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Show chips if combined view.
1350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mListAdapter.setShowColorChips(isCombinedMailbox() && mCountTotalAccounts > 1);
1351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Various post processing...
1354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateSearchHeader(cursor);
1355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            autoRefreshStaleMailbox();
1356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateFooterView();
1357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateSelectionMode();
1358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // We want to make visible the selection only for the first load.
1360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Re-load caused by content changed events shouldn't scroll the list.
1361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            highlightSelectedMessage(mIsFirstLoad);
1362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mIsFirstLoad) {
1364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                UiUtilities.setVisibilitySafe(mWarningContainer, View.GONE);
1365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mListPanel.setVisibility(View.VISIBLE);
1366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Setting the adapter will automatically transition from "Loading" to showing
1368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // the list, which could show "No messages". Avoid showing that on the first sync,
1369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // if we know we're still potentially loading more.
1370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (!isEmptyAndLoading(cursor)) {
1371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    setListAdapter(mListAdapter);
1372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
1373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            } else if ((getListAdapter() == null) && !isEmptyAndLoading(cursor)) {
1374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                setListAdapter(mListAdapter);
1375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Restore the state -- this step has to be the last, because Some of the
1378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // "post processing" seems to reset the scroll position.
1379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mSavedListState != null) {
1380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                getListView().onRestoreInstanceState(mSavedListState);
1381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mSavedListState = null;
1382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mIsFirstLoad = false;
1385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /**
1388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * Determines whether or not the list is empty, but we're still potentially loading data.
1389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * This represents an ambiguous state where we may not want to show "No messages", since
1390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         * it may still just be loading.
1391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook         */
1392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private boolean isEmptyAndLoading(Cursor cursor) {
1393bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mMailbox == null) return false;
1394bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return cursor.getCount() == 0 && mRefreshManager.isMessageListRefreshing(mMailbox.mId);
1395bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1396bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1397bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1398bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onLoaderReset(Loader<Cursor> loader) {
1399bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
1400bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Log.d(Logging.LOG_TAG, MessageListFragment.this
1401bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        + " onLoaderReset(messages)");
1402bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1403bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mListAdapter.swapCursor(null);
1404bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAccount = null;
1405bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMailbox = null;
1406bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSearchedMailbox = null;
1407bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mCountTotalAccounts = 0;
1408bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1409bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    };
1410bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1411bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1412bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Show/hide the "selection" action mode, according to the number of selected messages and
1413bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * the visibility of the fragment.
1414bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Also update the content (title and menus) if necessary.
1415bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1416bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void updateSelectionMode() {
1417bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final int numSelected = getSelectedCount();
1418bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if ((numSelected == 0) || mDisableCab || !isViewCreated()) {
1419bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            finishSelectionMode();
1420bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1421bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1422bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (isInSelectionMode()) {
1423bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateSelectionModeView();
1424bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
1425bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mLastSelectionModeCallback = new SelectionModeCallback();
1426bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            getActivity().startActionMode(mLastSelectionModeCallback);
1427bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1428bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1429bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1430bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1431bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1432bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Finish the "selection" action mode.
1433bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
1434bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Note this method finishes the contextual mode, but does *not* clear the selection.
1435bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * If you want to do so use {@link #onDeselectAll()} instead.
1436bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1437bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void finishSelectionMode() {
1438bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (isInSelectionMode()) {
1439bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mLastSelectionModeCallback.mClosedByUser = false;
1440bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSelectionMode.finish();
1441bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1442bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1443bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1444bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /** Update the "selection" action mode bar */
1445bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void updateSelectionModeView() {
1446bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        mSelectionMode.invalidate();
1447bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1448bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1449bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class SelectionModeCallback implements ActionMode.Callback {
1450bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MenuItem mMarkRead;
1451bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MenuItem mMarkUnread;
1452bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MenuItem mAddStar;
1453bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MenuItem mRemoveStar;
1454bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        private MenuItem mMove;
1455bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1456bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        /* package */ boolean mClosedByUser = true;
1457bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1458bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1459bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1460bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSelectionMode = mode;
1461bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1462bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            MenuInflater inflater = getActivity().getMenuInflater();
1463bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            inflater.inflate(R.menu.message_list_fragment_cab_options, menu);
1464bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMarkRead = menu.findItem(R.id.mark_read);
1465bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMarkUnread = menu.findItem(R.id.mark_unread);
1466bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAddStar = menu.findItem(R.id.add_star);
1467bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRemoveStar = menu.findItem(R.id.remove_star);
1468bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMove = menu.findItem(R.id.move);
1469bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return true;
1470bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1471bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1472bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1473bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1474bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            int num = getSelectedCount();
1475bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Set title -- "# selected"
1476bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSelectionMode.setTitle(getActivity().getResources().getQuantityString(
1477bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    R.plurals.message_view_selected_message_count, num, num));
1478bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1479bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Show appropriate menu items.
1480bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean nonStarExists = doesSelectionContainNonStarredMessage();
1481bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean readExists = doesSelectionContainReadMessage();
1482bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMarkRead.setVisible(!readExists);
1483bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMarkUnread.setVisible(readExists);
1484bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mAddStar.setVisible(nonStarExists);
1485bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mRemoveStar.setVisible(!nonStarExists);
1486bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mMove.setVisible(mShowMoveCommand);
1487bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return true;
1488bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1489bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1490bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1491bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1492bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Set<Long> selectedConversations = mListAdapter.getSelectedSet();
1493bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (selectedConversations.isEmpty()) return true;
1494bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            switch (item.getItemId()) {
1495bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.mark_read:
1496bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    // Note - marking as read does not trigger auto-advance.
1497bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    toggleRead(selectedConversations);
1498bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1499bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.mark_unread:
1500bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mCallback.onAdvancingOpAccepted(selectedConversations);
1501bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    toggleRead(selectedConversations);
1502bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1503bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.add_star:
1504bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.remove_star:
1505bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    // TODO: removing a star can be a destructive command and cause auto-advance
1506bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    // if the current mailbox shown is favorites.
1507bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    toggleFavorite(selectedConversations);
1508bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1509bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.delete:
1510bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    mCallback.onAdvancingOpAccepted(selectedConversations);
1511bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    deleteMessages(selectedConversations);
1512bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1513bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                case R.id.move:
1514bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    showMoveMessagesDialog(selectedConversations);
1515bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    break;
1516bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1517bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return true;
1518bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1519bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1520bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1521bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onDestroyActionMode(ActionMode mode) {
1522bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // Clear this before onDeselectAll() to prevent onDeselectAll() from trying to close the
1523bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // contextual mode again.
1524bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mSelectionMode = null;
1525bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (mClosedByUser) {
1526bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // Clear selection, only when the contextual mode is explicitly closed by the user.
1527bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                //
1528bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // We close the contextual mode when the fragment becomes temporary invisible
1529bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                // (i.e. mIsVisible == false) too, in which case we want to keep the selection.
1530bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                onDeselectAll();
1531bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1532bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1533bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1534bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1535bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private class RefreshListener implements RefreshManager.Listener {
1536bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1537bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onMessagingError(long accountId, long mailboxId, String message) {
1538bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1539bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1540bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        @Override
1541bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        public void onRefreshStatusChanged(long accountId, long mailboxId) {
1542bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            updateListFooter();
1543bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1544bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1545bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1546bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
1547bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Highlight the selected message.
1548bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
1549bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private void highlightSelectedMessage(boolean ensureSelectionVisible) {
1550bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (!isViewCreated()) {
1551bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1552bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1553bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1554bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final ListView lv = getListView();
1555bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (mSelectedMessageId == -1) {
1556bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // No message selected
1557bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            lv.clearChoices();
1558bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            return;
1559bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1560bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
1561bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        final int count = lv.getCount();
1562bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        for (int i = 0; i < count; i++) {
1563bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (lv.getItemIdAtPosition(i) != mSelectedMessageId) {
1564bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                continue;
1565bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1566bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            lv.setItemChecked(i, true);
1567bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (ensureSelectionVisible) {
1568bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                Utility.listViewSmoothScrollToPosition(getActivity(), lv, i);
1569bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
1570bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            break;
1571bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
1572bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
1573bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook}
1574