ConversationPagerAdapter.java revision 2821aeaa8c06011d67344a0232079a69bd003ef8
1632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang/*
2632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Copyright (C) 2012 Google Inc.
3632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Licensed to The Android Open Source Project.
4632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang *
5632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Licensed under the Apache License, Version 2.0 (the "License");
6632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * you may not use this file except in compliance with the License.
7632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * You may obtain a copy of the License at
8632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang *
9632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang *      http://www.apache.org/licenses/LICENSE-2.0
10632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang *
11632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Unless required by applicable law or agreed to in writing, software
12632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * distributed under the License is distributed on an "AS IS" BASIS,
13632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * See the License for the specific language governing permissions and
15632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * limitations under the License.
16632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */
17632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
18632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangpackage com.android.mail.browse;
19632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
20632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.app.Fragment;
21632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.app.FragmentManager;
2222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huangimport android.content.res.Resources;
23632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.database.Cursor;
24632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.database.DataSetObserver;
25632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.os.Bundle;
26632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.os.Parcelable;
2722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huangimport android.support.v4.view.ViewPager;
28589dec902e0df9f4a624aa983db7232309d29730Scott Kennedyimport android.text.TextUtils;
29632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.view.ViewGroup;
30632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
3122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huangimport com.android.mail.R;
32632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Account;
33632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Conversation;
34632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Folder;
35632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.UIProvider;
36f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.ui.AbstractConversationViewFragment;
37090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huangimport com.android.mail.ui.ActivityController;
38632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.ui.ConversationViewFragment;
39351ad4e87e0b0b98df9ca88266a8a63541dc5a81Andy Huangimport com.android.mail.ui.SecureConversationViewFragment;
40632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.FragmentStatePagerAdapter2;
41b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
42632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
43632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
447d64612104fd861474a09994f13768360f92041dAndy Huangpublic class ConversationPagerAdapter extends FragmentStatePagerAdapter2
457d64612104fd861474a09994f13768360f92041dAndy Huang        implements ViewPager.OnPageChangeListener {
46632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
47632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final DataSetObserver mListObserver = new ListObserver();
48090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private final DataSetObserver mFolderObserver = new FolderObserver();
49090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private ActivityController mController;
50632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final Bundle mCommonFragmentArgs;
51632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final Conversation mInitialConversation;
52b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    private final Account mAccount;
53b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    private final Folder mFolder;
54632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
55632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * In singleton mode, this adapter ignores the cursor contents and size, and acts as if the
561ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * data set size is exactly size=1, with {@link #getDefaultConversation()} at position 0.
57632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
58632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mSingletonMode = true;
59632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
601ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Similar to singleton mode, but once enabled, detached mode is permanent for this adapter.
611ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
621ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private boolean mDetachedMode = false;
631ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
64632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Adapter methods may trigger a data set change notification in the middle of a ViewPager
65632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * update, but they are not safe to handle, so we have to ignore them. This will not ignore
66632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * pager-external updates; it's impossible to be notified of an external change during
67632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * an update.
68632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     *
69632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * TODO: Queue up changes like this, if there ever are any that actually modify the data set.
70632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Right now there are none. Such a change would have to be of the form: instantiation or
71632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * setPrimary somehow adds or removes items from the conversation cursor. Crazy!
72632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
73632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mSafeToNotify;
7422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    /**
7522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * Need to keep this around to look up pager title strings.
7622ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     */
7722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    private Resources mResources;
7822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    /**
7922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * This isn't great to create a circular dependency, but our usage of {@link #getPageTitle(int)}
8022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * requires knowing which page is the currently visible to dynamically name offscreen pages
8122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * "newer" and "older". And {@link #setPrimaryItem(ViewGroup, int, Object)} does not work well
8222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * because it isn't updated as often as {@link ViewPager#getCurrentItem()} is.
8322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * <p>
8422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * We must be careful to null out this reference when the pager and adapter are decoupled to
8522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * minimize dangling references.
8622ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     */
8722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    private ViewPager mPager;
88f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp    private boolean mSanitizedHtml;
89632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
90589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy    /**
91589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy     * The URI of the detached conversation. We use this to mark new CVFs as detached if they are
92589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy     * created after this is set. This is to fix b/8185448. This is not needed in UR9 due changes in
93589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy     * CursorStatus.isWaitingForResults().
94589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy     */
95589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy    private String mDetachedUri = null;
96589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy
972821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    private boolean mStopListeningMode = false;
982821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang
992821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    /**
1002821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * After {@link #stopListening()} is called, this contains the last-known count of this adapter.
1012821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * We keep this around and use it in lieu of the Cursor's true count until imminent destruction
1022821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * to satisfy two opposing requirements:
1032821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * <ol>
1042821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * <li>The ViewPager always likes to know about all dataset changes via notifyDatasetChanged.
1052821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * <li>Destructive changes during pager destruction (e.g. mode transition from conversation mode
1062821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * to list mode) must be ignored, or else ViewPager will shift focus onto a neighboring
1072821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * conversation and <b>mark it read</b>.
1082821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * </ol>
1092821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     *
1102821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     */
1112821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    private int mLastKnownCount;
1122821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang
113b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
114632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
1151ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private static final String BUNDLE_DETACHED_MODE =
1161ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            ConversationPagerAdapter.class.getName() + "-detachedmode";
1171ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
11822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    public ConversationPagerAdapter(Resources res, FragmentManager fm, Account account,
11922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang            Folder folder, Conversation initialConversation) {
120632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super(fm, false /* enableSavedStates */);
12122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mResources = res;
122f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        mCommonFragmentArgs = AbstractConversationViewFragment.makeBasicArgs(account, folder);
123632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mInitialConversation = initialConversation;
124b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        mAccount = account;
125b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        mFolder = folder;
126f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        mSanitizedHtml = mAccount.supportsCapability
127f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp                (UIProvider.AccountCapabilities.SANITIZED_HTML);
128b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    }
129b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang
130b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    public boolean matches(Account account, Folder folder) {
131b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        return mAccount != null && mFolder != null && mAccount.matches(account)
132b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                && mFolder.equals(folder);
133632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
134632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
135632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setSingletonMode(boolean enabled) {
136632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mSingletonMode != enabled) {
137632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            mSingletonMode = enabled;
138632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            notifyDataSetChanged();
139632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
140632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
141632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
142632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public boolean isSingletonMode() {
1431ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mSingletonMode;
1441ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
1451ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
14677fb5be70d76f22a64850aa7a40f6a9553960e4emindyp    public boolean isDetached() {
14777fb5be70d76f22a64850aa7a40f6a9553960e4emindyp        return mDetachedMode;
14877fb5be70d76f22a64850aa7a40f6a9553960e4emindyp    }
14977fb5be70d76f22a64850aa7a40f6a9553960e4emindyp
1505440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal    /**
1515440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal     * Returns true if singleton mode or detached mode have been enabled, or if the current cursor
1525440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal     * is null.
1535440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal     * @param cursor the current conversation cursor (obtained through {@link #getCursor()}.
1545440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal     * @return
1555440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal     */
1565440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal    public boolean isPagingDisabled(Cursor cursor) {
1575440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        return mSingletonMode || mDetachedMode || cursor == null;
158e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang    }
159e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
1604de145bb568b6963d374fa0bac199218dc494186Paul Westbrook    private ConversationCursor getCursor() {
161a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (mDetachedMode) {
162a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            // In detached mode, the pager is decoupled from the cursor. Nothing should rely on the
163a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            // cursor at this point.
164a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            return null;
165a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
166090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        if (mController == null) {
167192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // Happens when someone calls setActivityController(null) on us. This is done in
168192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // ConversationPagerController.stopListening() to indicate that the Conversation View
169192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // is going away *very* soon.
1703825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang            LogUtils.i(LOG_TAG, "Pager adapter has a null controller. If the conversation view"
171192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal                    + " is going away, this is fine.  Otherwise, the state is inconsistent");
1728775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return null;
173e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang        }
174e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
175090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        return mController.getConversationListCursor();
176632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
177632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
178632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
179632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public Fragment getItem(int position) {
180632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final Conversation c;
1815440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        final Cursor cursor = getCursor();
182632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
1835440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        if (isPagingDisabled(cursor)) {
184632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // cursor-less adapter is a size-1 cursor that points to mInitialConversation.
185632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // sanity-check
186632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (position != 0) {
187632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.wtf(LOG_TAG, "pager cursor is null and position is non-zero: %d",
188632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                        position);
189632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
1901ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            c = getDefaultConversation();
191c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal            c.position = 0;
192632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else {
193e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            if (!cursor.moveToPosition(position)) {
194632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.wtf(LOG_TAG, "unable to seek to ConversationCursor pos=%d (%s)", position,
195e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang                        cursor);
196632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                return null;
197632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
198632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // TODO: switch to something like MessageCursor or AttachmentCursor
199632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // to re-use these models
200e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            c = new Conversation(cursor);
201c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal            c.position = position;
202632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
203589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy        final AbstractConversationViewFragment f = getConversationViewFragment(c);
204589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy        if (TextUtils.equals(mDetachedUri, c.uri.toString())) {
205589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy            f.onDetachedModeEntered();
206589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy        }
207632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.getItem, frag=%s subj=%s", f, c.subject);
208632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return f;
209632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
210632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
211f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp    private AbstractConversationViewFragment getConversationViewFragment(Conversation c) {
212f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        if (mSanitizedHtml) {
213f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp            return ConversationViewFragment.newInstance(mCommonFragmentArgs, c);
214f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        } else {
215f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp            return SecureConversationViewFragment.newInstance(mCommonFragmentArgs, c);
216f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        }
217f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp    }
218f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp
219632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
220632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getCount() {
2212821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        if (mStopListeningMode) {
2222821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang            return mLastKnownCount;
2232821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        }
2242821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang
2255440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        final Cursor cursor = getCursor();
2265440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        if (isPagingDisabled(cursor)) {
2273825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang            LogUtils.d(LOG_TAG, "IN CPA.getCount, returning 1 (effective singleton). cursor=%s",
2285440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal                    cursor);
2298775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return 1;
2308775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        }
2318775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        return cursor.getCount();
232632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
233632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
234632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
235632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getItemPosition(Object item) {
236f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        if (!(item instanceof AbstractConversationViewFragment)) {
237632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.wtf(LOG_TAG, "getItemPosition received unexpected item: %s", item);
238632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
239632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
240f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        final AbstractConversationViewFragment fragment = (AbstractConversationViewFragment) item;
241632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return getConversationPosition(fragment.getConversation());
242632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
243632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
244632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
245632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setPrimaryItem(ViewGroup container, int position, Object object) {
246632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.setPrimaryItem, pos=%d, frag=%s", position,
247632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                object);
248632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.setPrimaryItem(container, position, object);
249632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
250632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
251632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
252632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public CharSequence getPageTitle(int position) {
25322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        final String title;
25422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        final int currentPosition = mPager.getCurrentItem();
2555440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        final Cursor cursor = getCursor();
2565440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        if (isPagingDisabled(cursor)) {
25768459a7b3265da5002ff331529f12f6b408c9b03Andy Huang            title = null;
25868459a7b3265da5002ff331529f12f6b408c9b03Andy Huang        } else if (position == currentPosition) {
259090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            int total = getCount();
260090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            if (mController != null) {
261090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                final Folder f = mController.getFolder();
262090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                if (f != null && f.totalCount > total) {
263090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                    total = f.totalCount;
264090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                }
265090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            }
266090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            title = mResources.getString(R.string.conversation_count, position + 1, total);
26722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        } else {
26868459a7b3265da5002ff331529f12f6b408c9b03Andy Huang            title = mResources.getString(position < currentPosition ?
26922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang                    R.string.conversation_newer : R.string.conversation_older);
27022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        }
27122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        return title;
272632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
273632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
274632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
275632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public Parcelable saveState() {
2761ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.saveState. this=%s", this);
2771ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        Bundle state = (Bundle) super.saveState(); // superclass uses a Bundle
2781ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (state == null) {
2791ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            state = new Bundle();
2801ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
2811ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        state.putBoolean(BUNDLE_DETACHED_MODE, mDetachedMode);
2821ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return state;
283632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
284632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
285632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
286632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void restoreState(Parcelable state, ClassLoader loader) {
2871ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.restoreState. this=%s", this);
288632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.restoreState(state, loader);
2891ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (state != null) {
2901ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            Bundle b = (Bundle) state;
2911ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            b.setClassLoader(loader);
2921ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            mDetachedMode = b.getBoolean(BUNDLE_DETACHED_MODE);
2931ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
294632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
295632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
296632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
297632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void startUpdate(ViewGroup container) {
298632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mSafeToNotify = false;
299632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.startUpdate(container);
300632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
301632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
302632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
303632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void finishUpdate(ViewGroup container) {
304632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.finishUpdate(container);
305632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mSafeToNotify = true;
306632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
307632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
308632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
309632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void notifyDataSetChanged() {
310632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (!mSafeToNotify) {
311632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "IN PagerAdapter.notifyDataSetChanged, ignoring unsafe update");
312632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            return;
313632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
3141ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
315a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // If we are in detached mode, changes to the cursor are of no interest to us, but they may
316a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // be to parent classes.
317a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
3181ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        // when the currently visible item disappears from the dataset:
3191ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //   if the new version of the currently visible item has zero messages:
3201ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     notify the list controller so it can handle this 'current conversation gone' case
3211ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     (by backing out of conversation mode)
3221ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //   else
3231ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     'detach' the conversation view from the cursor, keeping the current item as-is but
3241ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     disabling swipe (effectively the same as singleton mode)
325b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy        if (mController != null && !mDetachedMode && mPager != null) {
3261ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            final Conversation currConversation = mController.getCurrentConversation();
3271ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            final int pos = getConversationPosition(currConversation);
328631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal            final Cursor cursor = getCursor();
329631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal            if (pos == POSITION_NONE && cursor != null && currConversation != null) {
3301ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                // enable detached mode and do no more here. the fragment itself will figure out
3311ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                // if the conversation is empty (using message list cursor) and back out if needed.
3321ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                mDetachedMode = true;
333a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal                mController.setDetachedMode();
3341ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                LogUtils.i(LOG_TAG, "CPA: current conv is gone, reverting to detached mode. c=%s",
3351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                        currConversation.uri);
33618176786f320b4d4312ad1682da057434f201c13Scott Kennedy
3370558150910ece60b2d8abf1104338e8ffbb0ea17Scott Kennedy                final int currentItem = mPager.getCurrentItem();
338b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy
339b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                final AbstractConversationViewFragment fragment =
340b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                        (AbstractConversationViewFragment) getFragmentAt(currentItem);
341b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy
342b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                if (fragment != null) {
343b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                    fragment.onDetachedModeEntered();
344b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                } else {
345b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                    LogUtils.e(LOG_TAG,
346b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                            "CPA: notifyDataSetChanged: fragment null, current item: %d",
347b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                            currentItem);
348b30604cd52a1bacf4f86626181a3224bafc96b42Scott Kennedy                }
349589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy
350589dec902e0df9f4a624aa983db7232309d29730Scott Kennedy                mDetachedUri = currConversation.uri.toString();
35136280f3c2939bb2dacb4077bd528900346ff4bb9mindyp            } else {
352631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal                // notify unaffected fragment items of the change, so they can re-render
353631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal                // (the change may have been to the labels for a single conversation, for example)
354631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal                final AbstractConversationViewFragment frag = (cursor == null) ? null :
35526d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp                        (AbstractConversationViewFragment) getFragmentAt(pos);
3569d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang                if (frag != null && cursor.moveToPosition(pos) && frag.isUserVisible()) {
35716982472f8431aecfdb5f32aa7a97dce9064ac22mindyp                    // reload what we think is in the current position.
358631abc9c544925fa9c02492382dd567b705bf6feVikram Aggarwal                    final Conversation conv = new Conversation(cursor);
359a825aae102a6112a0b4395eb3c5417c7b0c24262mindyp                    conv.position = pos;
360b2b98ba97983f225eec19dc9bc5333e6ef80ee15mindyp                    frag.onConversationUpdated(conv);
361b2b98ba97983f225eec19dc9bc5333e6ef80ee15mindyp                    mController.setCurrentConversation(conv);
36216982472f8431aecfdb5f32aa7a97dce9064ac22mindyp                }
3631ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            }
3641ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
3651ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
366632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.notifyDataSetChanged();
367632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
368632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
369632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
370632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setItemVisible(Fragment item, boolean visible) {
371632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.setItemVisible(item, visible);
372f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp        final AbstractConversationViewFragment fragment = (AbstractConversationViewFragment) item;
373632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        fragment.setExtraUserVisibleHint(visible);
374632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
375632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
3761ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private Conversation getDefaultConversation() {
3771ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        Conversation c = (mController != null) ? mController.getCurrentConversation() : null;
3781ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (c == null) {
3791ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            c = mInitialConversation;
3801ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
3811ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return c;
3821ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
3831ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
384632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getConversationPosition(Conversation conv) {
3855440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        final ConversationCursor cursor = getCursor();
3865440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        if (isPagingDisabled(cursor)) {
3871ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            if (conv != getDefaultConversation()) {
38849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal                LogUtils.d(LOG_TAG, "unable to find conversation in singleton mode. c=%s", conv);
389632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                return POSITION_NONE;
390632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
391632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            return 0;
392632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
393632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
3945440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        // cursor is guaranteed to be non-null because isPagingDisabled() above checks for null
3955440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        // cursor.
3965440bf50e6b05e1551b375f797ff4bf8ed3f39ceVikram Aggarwal        if (conv == null) {
3978775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return POSITION_NONE;
3988775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        }
399e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
400ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        int result = POSITION_NONE;
401c8f2a3c6755b3b78af810aa73ab78487567cf074Paul Westbrook        final int pos = cursor.getConversationPosition(conv.id);
402c8f2a3c6755b3b78af810aa73ab78487567cf074Paul Westbrook        if (pos >= 0) {
403c8f2a3c6755b3b78af810aa73ab78487567cf074Paul Westbrook            LogUtils.d(LOG_TAG, "pager adapter found repositioned convo '%s' at pos=%d",
404c8f2a3c6755b3b78af810aa73ab78487567cf074Paul Westbrook                    conv.subject, pos);
405c8f2a3c6755b3b78af810aa73ab78487567cf074Paul Westbrook            result = pos;
406ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        }
407ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
408ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return result;
409632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
410632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
41122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    public void setPager(ViewPager pager) {
4127d64612104fd861474a09994f13768360f92041dAndy Huang        if (mPager != null) {
4137d64612104fd861474a09994f13768360f92041dAndy Huang            mPager.setOnPageChangeListener(null);
4147d64612104fd861474a09994f13768360f92041dAndy Huang        }
41522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPager = pager;
4167d64612104fd861474a09994f13768360f92041dAndy Huang        if (mPager != null) {
4177d64612104fd861474a09994f13768360f92041dAndy Huang            mPager.setOnPageChangeListener(this);
4187d64612104fd861474a09994f13768360f92041dAndy Huang        }
41922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    }
42022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang
421090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void setActivityController(ActivityController controller) {
4222821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        if (mController != null && !mStopListeningMode) {
423090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mController.unregisterConversationListObserver(mListObserver);
424860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang            mController.unregisterFolderObserver(mFolderObserver);
425632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
426090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mController = controller;
4272821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        if (mController != null && !mStopListeningMode) {
428090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mController.registerConversationListObserver(mListObserver);
429860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang            mController.registerFolderObserver(mFolderObserver);
430632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
431e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            notifyDataSetChanged();
432632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else {
433e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            // We're being torn down; do not notify.
434e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            // Let the pager controller manage pager lifecycle.
435632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
436632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
437632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
4382821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    /**
4392821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     * See {@link ConversationPagerController#stopListening()}.
4402821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang     */
4412821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    public void stopListening() {
4422821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        // disable the observer, but save off the current count, in case the Pager asks for it
4432821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        // from now until imminent destruction
4442821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        mStopListeningMode = true;
4452821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang
4462821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        if (mController != null) {
4472821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang            mController.unregisterConversationListObserver(mListObserver);
4482821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang            mController.unregisterFolderObserver(mFolderObserver);
4492821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        }
4502821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang        mLastKnownCount = getCount();
4512821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang    }
4522821aeaa8c06011d67344a0232079a69bd003ef8Andy Huang
4537d64612104fd861474a09994f13768360f92041dAndy Huang    @Override
4547d64612104fd861474a09994f13768360f92041dAndy Huang    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
4557d64612104fd861474a09994f13768360f92041dAndy Huang        // no-op
4567d64612104fd861474a09994f13768360f92041dAndy Huang    }
4577d64612104fd861474a09994f13768360f92041dAndy Huang
4587d64612104fd861474a09994f13768360f92041dAndy Huang    @Override
4597d64612104fd861474a09994f13768360f92041dAndy Huang    public void onPageSelected(int position) {
46066db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        if (mController == null) {
46166db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal            return;
46266db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        }
46366db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        final Cursor cursor = getCursor();
46466db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        if (cursor == null || !cursor.moveToPosition(position)) {
46566db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal            // No valid cursor or it doesn't have the position we want. Bail.
46666db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal            return;
4677d64612104fd861474a09994f13768360f92041dAndy Huang        }
46866db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        final Conversation c = new Conversation(cursor);
46966db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        c.position = position;
47066db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        LogUtils.d(LOG_TAG, "pager adapter setting current conv: %s", c.subject);
47166db04cd71b5096b4496bf6d9468dd2ef8dc86f9Vikram Aggarwal        mController.setCurrentConversation(c);
4727d64612104fd861474a09994f13768360f92041dAndy Huang    }
4737d64612104fd861474a09994f13768360f92041dAndy Huang
4747d64612104fd861474a09994f13768360f92041dAndy Huang    @Override
4757d64612104fd861474a09994f13768360f92041dAndy Huang    public void onPageScrollStateChanged(int state) {
4767d64612104fd861474a09994f13768360f92041dAndy Huang        // no-op
4777d64612104fd861474a09994f13768360f92041dAndy Huang    }
4787d64612104fd861474a09994f13768360f92041dAndy Huang
479090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    // update the pager title strip as the Folder's conversation count changes
480090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private class FolderObserver extends DataSetObserver {
481090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        @Override
482090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        public void onChanged() {
483090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            notifyDataSetChanged();
484090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        }
485090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
486090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
487632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    // update the pager dataset as the Controller's cursor changes
488632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private class ListObserver extends DataSetObserver {
489632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        @Override
490632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        public void onChanged() {
491e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            notifyDataSetChanged();
492632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
493632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        @Override
494632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        public void onInvalidated() {
495632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
496632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
497632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
498632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang}
499