ConversationPagerAdapter.java revision 3825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bb
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;
28632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.view.ViewGroup;
29632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
3022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huangimport com.android.mail.R;
31632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Account;
32632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Conversation;
33632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Folder;
34632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.UIProvider;
35090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huangimport com.android.mail.ui.ActivityController;
36632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.ui.ConversationViewFragment;
37632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.FragmentStatePagerAdapter2;
38b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
39632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
40ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.utils.Utils;
41632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
42632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangpublic class ConversationPagerAdapter extends FragmentStatePagerAdapter2 {
43632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
44632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final DataSetObserver mListObserver = new ListObserver();
45090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private final DataSetObserver mFolderObserver = new FolderObserver();
46090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private ActivityController mController;
47632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final Bundle mCommonFragmentArgs;
48632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private final Conversation mInitialConversation;
49b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    private final Account mAccount;
50b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    private final Folder mFolder;
51632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
52632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * In singleton mode, this adapter ignores the cursor contents and size, and acts as if the
531ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * data set size is exactly size=1, with {@link #getDefaultConversation()} at position 0.
54632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
55632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mSingletonMode = true;
56632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
571ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Similar to singleton mode, but once enabled, detached mode is permanent for this adapter.
581ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
591ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private boolean mDetachedMode = false;
601ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
61632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Adapter methods may trigger a data set change notification in the middle of a ViewPager
62632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * update, but they are not safe to handle, so we have to ignore them. This will not ignore
63632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * pager-external updates; it's impossible to be notified of an external change during
64632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * an update.
65632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     *
66632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * TODO: Queue up changes like this, if there ever are any that actually modify the data set.
67632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Right now there are none. Such a change would have to be of the form: instantiation or
68632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * setPrimary somehow adds or removes items from the conversation cursor. Crazy!
69632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
70632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mSafeToNotify;
7122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    /**
7222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * Need to keep this around to look up pager title strings.
7322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     */
7422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    private Resources mResources;
7522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    /**
7622ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * This isn't great to create a circular dependency, but our usage of {@link #getPageTitle(int)}
7722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * requires knowing which page is the currently visible to dynamically name offscreen pages
7822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * "newer" and "older". And {@link #setPrimaryItem(ViewGroup, int, Object)} does not work well
7922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * because it isn't updated as often as {@link ViewPager#getCurrentItem()} is.
8022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * <p>
8122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * We must be careful to null out this reference when the pager and adapter are decoupled to
8222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     * minimize dangling references.
8322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang     */
8422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    private ViewPager mPager;
85632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
86b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
87632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
881ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private static final String BUNDLE_DETACHED_MODE =
891ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            ConversationPagerAdapter.class.getName() + "-detachedmode";
901ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
9122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    public ConversationPagerAdapter(Resources res, FragmentManager fm, Account account,
9222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang            Folder folder, Conversation initialConversation) {
93632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super(fm, false /* enableSavedStates */);
9422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mResources = res;
95632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mCommonFragmentArgs = ConversationViewFragment.makeBasicArgs(account, folder);
96632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mInitialConversation = initialConversation;
97b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        mAccount = account;
98b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        mFolder = folder;
99b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    }
100b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang
101b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang    public boolean matches(Account account, Folder folder) {
102b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang        return mAccount != null && mFolder != null && mAccount.matches(account)
103b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                && mFolder.equals(folder);
104632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
105632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
106632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setSingletonMode(boolean enabled) {
107632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mSingletonMode != enabled) {
108632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            mSingletonMode = enabled;
109632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            notifyDataSetChanged();
110632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
111632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
112632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
113632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public boolean isSingletonMode() {
1141ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mSingletonMode;
1151ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
1161ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
1171ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    public boolean isPagingDisabled() {
1181ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mSingletonMode || mDetachedMode || getCursor() == null;
119e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang    }
120e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
121e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang    private Cursor getCursor() {
122090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        if (mController == null) {
123192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // Happens when someone calls setActivityController(null) on us. This is done in
124192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // ConversationPagerController.stopListening() to indicate that the Conversation View
125192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            // is going away *very* soon.
1263825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang            LogUtils.i(LOG_TAG, "Pager adapter has a null controller. If the conversation view"
127192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal                    + " is going away, this is fine.  Otherwise, the state is inconsistent");
1288775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return null;
129e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang        }
130e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
131090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        return mController.getConversationListCursor();
132632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
133632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
134632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
135632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public Fragment getItem(int position) {
136632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final Conversation c;
137632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
1381ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (isPagingDisabled()) {
139632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // cursor-less adapter is a size-1 cursor that points to mInitialConversation.
140632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // sanity-check
141632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (position != 0) {
142632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.wtf(LOG_TAG, "pager cursor is null and position is non-zero: %d",
143632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                        position);
144632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
1451ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            c = getDefaultConversation();
146c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal            c.position = 0;
147632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else {
148e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            final Cursor cursor = getCursor();
1498775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            if (cursor == null) {
1508775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang                LogUtils.wtf(LOG_TAG, "unable to get ConversationCursor, pos=%d", position);
1518775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang                return null;
1528775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            }
153e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            if (!cursor.moveToPosition(position)) {
154632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.wtf(LOG_TAG, "unable to seek to ConversationCursor pos=%d (%s)", position,
155e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang                        cursor);
156632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                return null;
157632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
158632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // TODO: switch to something like MessageCursor or AttachmentCursor
159632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // to re-use these models
160e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            c = new Conversation(cursor);
161c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal            c.position = position;
162632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
163632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final Fragment f = ConversationViewFragment.newInstance(mCommonFragmentArgs, c);
164632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.getItem, frag=%s subj=%s", f, c.subject);
165632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return f;
166632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
167632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
168632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
169632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getCount() {
1701ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (isPagingDisabled()) {
1713825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang            LogUtils.d(LOG_TAG, "IN CPA.getCount, returning 1 (effective singleton). cursor=%s",
1723825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang                    getCursor());
1738775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return 1;
1748775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        }
1758775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        final Cursor cursor = getCursor();
1768775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        if (cursor == null) {
1778775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return 0;
1788775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        }
1798775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        return cursor.getCount();
180632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
181632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
182632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
183632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getItemPosition(Object item) {
184632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (!(item instanceof ConversationViewFragment)) {
185632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.wtf(LOG_TAG, "getItemPosition received unexpected item: %s", item);
186632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
187632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
188632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final ConversationViewFragment fragment = (ConversationViewFragment) item;
189632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return getConversationPosition(fragment.getConversation());
190632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
191632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
192632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
193632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setPrimaryItem(ViewGroup container, int position, Object object) {
194632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.setPrimaryItem, pos=%d, frag=%s", position,
195632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                object);
196632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.setPrimaryItem(container, position, object);
197632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
198632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
199632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
200632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public CharSequence getPageTitle(int position) {
20122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        final String title;
20222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        final int currentPosition = mPager.getCurrentItem();
203632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2041ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (isPagingDisabled()) {
20568459a7b3265da5002ff331529f12f6b408c9b03Andy Huang            title = null;
20668459a7b3265da5002ff331529f12f6b408c9b03Andy Huang        } else if (position == currentPosition) {
207090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            int total = getCount();
208090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            if (mController != null) {
209090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                final Folder f = mController.getFolder();
210090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                if (f != null && f.totalCount > total) {
211090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                    total = f.totalCount;
212090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang                }
213090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            }
214090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            title = mResources.getString(R.string.conversation_count, position + 1, total);
21522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        } else {
21668459a7b3265da5002ff331529f12f6b408c9b03Andy Huang            title = mResources.getString(position < currentPosition ?
21722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang                    R.string.conversation_newer : R.string.conversation_older);
21822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        }
21922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        return title;
220632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
221632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
222632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
223632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public Parcelable saveState() {
2241ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.saveState. this=%s", this);
2251ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        Bundle state = (Bundle) super.saveState(); // superclass uses a Bundle
2261ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (state == null) {
2271ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            state = new Bundle();
2281ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
2291ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        state.putBoolean(BUNDLE_DETACHED_MODE, mDetachedMode);
2301ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return state;
231632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
232632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
233632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
234632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void restoreState(Parcelable state, ClassLoader loader) {
2351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        LogUtils.d(LOG_TAG, "IN PagerAdapter.restoreState. this=%s", this);
236632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.restoreState(state, loader);
2371ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (state != null) {
2381ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            Bundle b = (Bundle) state;
2391ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            b.setClassLoader(loader);
2401ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            mDetachedMode = b.getBoolean(BUNDLE_DETACHED_MODE);
2411ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
242632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
243632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
244632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
245632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void startUpdate(ViewGroup container) {
246632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mSafeToNotify = false;
247632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.startUpdate(container);
248632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
249632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
250632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
251632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void finishUpdate(ViewGroup container) {
252632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.finishUpdate(container);
253632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mSafeToNotify = true;
254632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
255632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
256632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
257632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void notifyDataSetChanged() {
258632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (!mSafeToNotify) {
259632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "IN PagerAdapter.notifyDataSetChanged, ignoring unsafe update");
260632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            return;
261632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
2621ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
2631ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        // when the currently visible item disappears from the dataset:
2641ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //   if the new version of the currently visible item has zero messages:
2651ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     notify the list controller so it can handle this 'current conversation gone' case
2661ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     (by backing out of conversation mode)
2671ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //   else
2681ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     'detach' the conversation view from the cursor, keeping the current item as-is but
2691ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        //     disabling swipe (effectively the same as singleton mode)
2701ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (mController != null) {
2711ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            final Conversation currConversation = mController.getCurrentConversation();
2721ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            final int pos = getConversationPosition(currConversation);
2731ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            if (pos == POSITION_NONE) {
2741ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                // enable detached mode and do no more here. the fragment itself will figure out
2751ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                // if the conversation is empty (using message list cursor) and back out if needed.
2761ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                mDetachedMode = true;
2771ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                LogUtils.i(LOG_TAG, "CPA: current conv is gone, reverting to detached mode. c=%s",
2781ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                        currConversation.uri);
2791ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            }
2801ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
2811ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
282632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.notifyDataSetChanged();
2831ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
2841ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        // notify unaffected fragment items of the change, so they can re-render
2851ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        // (the change may have been to the labels for a single conversation, for example)
286632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
287632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
288632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
289632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setItemVisible(Fragment item, boolean visible) {
290632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        super.setItemVisible(item, visible);
291632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final ConversationViewFragment fragment = (ConversationViewFragment) item;
292632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        fragment.setExtraUserVisibleHint(visible);
293632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
294090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        if (visible && mController != null) {
295632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            final Conversation c = fragment.getConversation();
296632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "pager adapter setting current conv: %s (%s)", c.subject, item);
297090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mController.setCurrentConversation(c);
298632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
299632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
300632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
3011ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private Conversation getDefaultConversation() {
3021ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        Conversation c = (mController != null) ? mController.getCurrentConversation() : null;
3031ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (c == null) {
3041ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            c = mInitialConversation;
3051ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        }
3061ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return c;
3071ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
3081ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
309632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public int getConversationPosition(Conversation conv) {
3101ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        if (isPagingDisabled()) {
3117ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang            if (getCursor() == null) {
3127ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang                return POSITION_NONE;
3137ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang            }
3147ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang
3151ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang            if (conv != getDefaultConversation()) {
3161ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang                LogUtils.d(LOG_TAG, "unable to find conversation in singleton mode. c=%s",
317632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                        conv);
318632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                return POSITION_NONE;
319632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
320632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            return 0;
321632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
322632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
323e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang        final Cursor cursor = getCursor();
3248775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        if (cursor == null) {
3258775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang            return POSITION_NONE;
3268775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang        }
327e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang
328e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang        final boolean networkWasEnabled = Utils.disableConversationCursorNetworkAccess(cursor);
329ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
330ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        int result = POSITION_NONE;
331632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        int pos = -1;
332e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang        while (cursor.moveToPosition(++pos)) {
333e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            final long id = cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
334632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (conv.id == id) {
335632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.d(LOG_TAG, "pager adapter found repositioned convo '%s' at pos=%d",
336632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                        conv.subject, pos);
337ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                result = pos;
338ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                break;
339632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
340632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
341ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
342ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        if (networkWasEnabled) {
343e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            Utils.enableConversationCursorNetworkAccess(cursor);
344ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        }
345ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
346ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return result;
347632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
348632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
34922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    public void setPager(ViewPager pager) {
35022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPager = pager;
35122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    }
35222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang
353090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void setActivityController(ActivityController controller) {
354090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        if (mController != null) {
355090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mController.unregisterConversationListObserver(mListObserver);
356860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang            mController.unregisterFolderObserver(mFolderObserver);
357632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
358090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mController = controller;
359090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        if (mController != null) {
360090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mController.registerConversationListObserver(mListObserver);
361860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang            mController.registerFolderObserver(mFolderObserver);
362632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
363e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            notifyDataSetChanged();
364632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else {
365e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            // We're being torn down; do not notify.
366e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            // Let the pager controller manage pager lifecycle.
367632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
368632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
369632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
370090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    // update the pager title strip as the Folder's conversation count changes
371090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private class FolderObserver extends DataSetObserver {
372090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        @Override
373090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        public void onChanged() {
374090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            notifyDataSetChanged();
375090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        }
376090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
377090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
378632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    // update the pager dataset as the Controller's cursor changes
379632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private class ListObserver extends DataSetObserver {
380632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        @Override
381632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        public void onChanged() {
382e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            notifyDataSetChanged();
383632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
384632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        @Override
385632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        public void onInvalidated() {
386632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
387632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
388632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
389632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang}
390