ConversationPagerAdapter.java revision 26d4d2d9c43c499f458808f050ec73ea3c28dec4
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; 35f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.ui.AbstractConversationViewFragment; 36090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huangimport com.android.mail.ui.ActivityController; 37632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.ui.ConversationViewFragment; 38632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.FragmentStatePagerAdapter2; 39b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 40632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils; 41ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.utils.Utils; 42632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 437d64612104fd861474a09994f13768360f92041dAndy Huangpublic class ConversationPagerAdapter extends FragmentStatePagerAdapter2 447d64612104fd861474a09994f13768360f92041dAndy Huang implements ViewPager.OnPageChangeListener { 45632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 46632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private final DataSetObserver mListObserver = new ListObserver(); 47090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang private final DataSetObserver mFolderObserver = new FolderObserver(); 48090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang private ActivityController mController; 49632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private final Bundle mCommonFragmentArgs; 50632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private final Conversation mInitialConversation; 51b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang private final Account mAccount; 52b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang private final Folder mFolder; 53632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang /** 54632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * In singleton mode, this adapter ignores the cursor contents and size, and acts as if the 551ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang * data set size is exactly size=1, with {@link #getDefaultConversation()} at position 0. 56632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */ 57632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private boolean mSingletonMode = true; 58632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang /** 591ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang * Similar to singleton mode, but once enabled, detached mode is permanent for this adapter. 601ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang */ 611ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang private boolean mDetachedMode = false; 621ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang /** 63632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Adapter methods may trigger a data set change notification in the middle of a ViewPager 64632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * update, but they are not safe to handle, so we have to ignore them. This will not ignore 65632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * pager-external updates; it's impossible to be notified of an external change during 66632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * an update. 67632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * 68632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * TODO: Queue up changes like this, if there ever are any that actually modify the data set. 69632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Right now there are none. Such a change would have to be of the form: instantiation or 70632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * setPrimary somehow adds or removes items from the conversation cursor. Crazy! 71632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */ 72632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private boolean mSafeToNotify; 7322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang /** 7422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * Need to keep this around to look up pager title strings. 7522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang */ 7622ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang private Resources mResources; 7722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang /** 7822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * This isn't great to create a circular dependency, but our usage of {@link #getPageTitle(int)} 7922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * requires knowing which page is the currently visible to dynamically name offscreen pages 8022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * "newer" and "older". And {@link #setPrimaryItem(ViewGroup, int, Object)} does not work well 8122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * because it isn't updated as often as {@link ViewPager#getCurrentItem()} is. 8222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * <p> 8322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * We must be careful to null out this reference when the pager and adapter are decoupled to 8422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang * minimize dangling references. 8522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang */ 8622ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang private ViewPager mPager; 87f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private boolean mSanitizedHtml; 88632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 89b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String LOG_TAG = LogTag.getLogTag(); 90632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 911ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang private static final String BUNDLE_DETACHED_MODE = 921ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang ConversationPagerAdapter.class.getName() + "-detachedmode"; 931ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 9422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang public ConversationPagerAdapter(Resources res, FragmentManager fm, Account account, 9522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang Folder folder, Conversation initialConversation) { 96632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super(fm, false /* enableSavedStates */); 9722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang mResources = res; 98f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mCommonFragmentArgs = AbstractConversationViewFragment.makeBasicArgs(account, folder); 99632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mInitialConversation = initialConversation; 100b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang mAccount = account; 101b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang mFolder = folder; 102f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mSanitizedHtml = mAccount.supportsCapability 103f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp (UIProvider.AccountCapabilities.SANITIZED_HTML); 104b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang } 105b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang 106b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang public boolean matches(Account account, Folder folder) { 107b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang return mAccount != null && mFolder != null && mAccount.matches(account) 108b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang && mFolder.equals(folder); 109632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 110632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 111632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void setSingletonMode(boolean enabled) { 112632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang if (mSingletonMode != enabled) { 113632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mSingletonMode = enabled; 114632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang notifyDataSetChanged(); 115632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 116632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 117632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 118632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public boolean isSingletonMode() { 1191ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return mSingletonMode; 1201ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 1211ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 1221ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang public boolean isPagingDisabled() { 1231ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return mSingletonMode || mDetachedMode || getCursor() == null; 124e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang } 125e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang 126e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang private Cursor getCursor() { 127090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang if (mController == null) { 128192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal // Happens when someone calls setActivityController(null) on us. This is done in 129192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal // ConversationPagerController.stopListening() to indicate that the Conversation View 130192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal // is going away *very* soon. 1313825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang LogUtils.i(LOG_TAG, "Pager adapter has a null controller. If the conversation view" 132192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal + " is going away, this is fine. Otherwise, the state is inconsistent"); 1338775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return null; 134e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang } 135e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang 136090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang return mController.getConversationListCursor(); 137632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 138632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 139632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 140632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public Fragment getItem(int position) { 141632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang final Conversation c; 142632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 1431ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (isPagingDisabled()) { 144632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang // cursor-less adapter is a size-1 cursor that points to mInitialConversation. 145632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang // sanity-check 146632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang if (position != 0) { 147632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.wtf(LOG_TAG, "pager cursor is null and position is non-zero: %d", 148632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang position); 149632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 1501ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang c = getDefaultConversation(); 151c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal c.position = 0; 152632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } else { 153e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang final Cursor cursor = getCursor(); 1548775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang if (cursor == null) { 1558775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang LogUtils.wtf(LOG_TAG, "unable to get ConversationCursor, pos=%d", position); 1568775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return null; 1578775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang } 158e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang if (!cursor.moveToPosition(position)) { 159632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.wtf(LOG_TAG, "unable to seek to ConversationCursor pos=%d (%s)", position, 160e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang cursor); 161632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return null; 162632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 163632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang // TODO: switch to something like MessageCursor or AttachmentCursor 164632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang // to re-use these models 165e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang c = new Conversation(cursor); 166c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal c.position = position; 167632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 168f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Fragment f = getConversationViewFragment(c); 169632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.d(LOG_TAG, "IN PagerAdapter.getItem, frag=%s subj=%s", f, c.subject); 170632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return f; 171632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 172632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 173f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private AbstractConversationViewFragment getConversationViewFragment(Conversation c) { 174f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mSanitizedHtml) { 175f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return ConversationViewFragment.newInstance(mCommonFragmentArgs, c); 176f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 177f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return SecureConversationViewFragment.newInstance(mCommonFragmentArgs, c); 178f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 179f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 180f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 181632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 182632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public int getCount() { 1831ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (isPagingDisabled()) { 1843825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang LogUtils.d(LOG_TAG, "IN CPA.getCount, returning 1 (effective singleton). cursor=%s", 1853825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang getCursor()); 1868775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return 1; 1878775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang } 1888775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang final Cursor cursor = getCursor(); 1898775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang if (cursor == null) { 1908775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return 0; 1918775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang } 1928775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return cursor.getCount(); 193632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 194632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 195632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 196632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public int getItemPosition(Object item) { 197f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!(item instanceof AbstractConversationViewFragment)) { 198632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.wtf(LOG_TAG, "getItemPosition received unexpected item: %s", item); 199632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 200632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 201f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final AbstractConversationViewFragment fragment = (AbstractConversationViewFragment) item; 202632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return getConversationPosition(fragment.getConversation()); 203632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 204632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 205632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 206632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void setPrimaryItem(ViewGroup container, int position, Object object) { 207632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.d(LOG_TAG, "IN PagerAdapter.setPrimaryItem, pos=%d, frag=%s", position, 208632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang object); 209632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.setPrimaryItem(container, position, object); 210632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 211632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 212632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 213632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public CharSequence getPageTitle(int position) { 21422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang final String title; 21522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang final int currentPosition = mPager.getCurrentItem(); 216632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 2171ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (isPagingDisabled()) { 21868459a7b3265da5002ff331529f12f6b408c9b03Andy Huang title = null; 21968459a7b3265da5002ff331529f12f6b408c9b03Andy Huang } else if (position == currentPosition) { 220090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang int total = getCount(); 221090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang if (mController != null) { 222090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang final Folder f = mController.getFolder(); 223090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang if (f != null && f.totalCount > total) { 224090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang total = f.totalCount; 225090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang } 226090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang } 227090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang title = mResources.getString(R.string.conversation_count, position + 1, total); 22822ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang } else { 22968459a7b3265da5002ff331529f12f6b408c9b03Andy Huang title = mResources.getString(position < currentPosition ? 23022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang R.string.conversation_newer : R.string.conversation_older); 23122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang } 23222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang return title; 233632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 234632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 235632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 236632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public Parcelable saveState() { 2371ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.d(LOG_TAG, "IN PagerAdapter.saveState. this=%s", this); 2381ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang Bundle state = (Bundle) super.saveState(); // superclass uses a Bundle 2391ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (state == null) { 2401ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang state = new Bundle(); 2411ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 2421ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang state.putBoolean(BUNDLE_DETACHED_MODE, mDetachedMode); 2431ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return state; 244632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 245632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 246632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 247632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void restoreState(Parcelable state, ClassLoader loader) { 2481ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.d(LOG_TAG, "IN PagerAdapter.restoreState. this=%s", this); 249632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.restoreState(state, loader); 2501ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (state != null) { 2511ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang Bundle b = (Bundle) state; 2521ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang b.setClassLoader(loader); 2531ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang mDetachedMode = b.getBoolean(BUNDLE_DETACHED_MODE); 2541ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 255632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 256632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 257632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 258632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void startUpdate(ViewGroup container) { 259632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mSafeToNotify = false; 260632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.startUpdate(container); 261632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 262632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 263632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 264632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void finishUpdate(ViewGroup container) { 265632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.finishUpdate(container); 266632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mSafeToNotify = true; 267632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 268632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 269632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 270632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void notifyDataSetChanged() { 271632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang if (!mSafeToNotify) { 272632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.d(LOG_TAG, "IN PagerAdapter.notifyDataSetChanged, ignoring unsafe update"); 273632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return; 274632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 2751ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 2761ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // when the currently visible item disappears from the dataset: 2771ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // if the new version of the currently visible item has zero messages: 2781ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // notify the list controller so it can handle this 'current conversation gone' case 2791ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // (by backing out of conversation mode) 2801ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // else 2811ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // 'detach' the conversation view from the cursor, keeping the current item as-is but 2821ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // disabling swipe (effectively the same as singleton mode) 2831ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (mController != null) { 2841ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang final Conversation currConversation = mController.getCurrentConversation(); 2851ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang final int pos = getConversationPosition(currConversation); 28637b65641d2e9009135a79d993e76e6984dadb422Paul Westbrook if (pos == POSITION_NONE && getCursor() != null) { 2871ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // enable detached mode and do no more here. the fragment itself will figure out 2881ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // if the conversation is empty (using message list cursor) and back out if needed. 2891ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang mDetachedMode = true; 2901ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.i(LOG_TAG, "CPA: current conv is gone, reverting to detached mode. c=%s", 2911ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang currConversation.uri); 29236280f3c2939bb2dacb4077bd528900346ff4bb9mindyp } else { 2938995554b8c55dd9e573e68572fa41b6417b87f0cmindyp // notify unaffected fragment items of the change, so they can 2948995554b8c55dd9e573e68572fa41b6417b87f0cmindyp // re-render 2958995554b8c55dd9e573e68572fa41b6417b87f0cmindyp // (the change may have been to the labels for a single 2968995554b8c55dd9e573e68572fa41b6417b87f0cmindyp // conversation, for example) 29726d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp final AbstractConversationViewFragment frag = 29826d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp (AbstractConversationViewFragment) getFragmentAt(pos); 29916982472f8431aecfdb5f32aa7a97dce9064ac22mindyp final Cursor cursor = getCursor(); 30016982472f8431aecfdb5f32aa7a97dce9064ac22mindyp if (frag != null && cursor.moveToPosition(pos)) { 30116982472f8431aecfdb5f32aa7a97dce9064ac22mindyp // reload what we think is in the current position. 30216982472f8431aecfdb5f32aa7a97dce9064ac22mindyp frag.onConversationUpdated(new Conversation(cursor)); 30316982472f8431aecfdb5f32aa7a97dce9064ac22mindyp } 3041ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 3051ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 3061ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 307632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.notifyDataSetChanged(); 308632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 309632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 310632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 311632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void setItemVisible(Fragment item, boolean visible) { 312632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang super.setItemVisible(item, visible); 313f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final AbstractConversationViewFragment fragment = (AbstractConversationViewFragment) item; 314632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang fragment.setExtraUserVisibleHint(visible); 315632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 316632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 3171ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang private Conversation getDefaultConversation() { 3181ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang Conversation c = (mController != null) ? mController.getCurrentConversation() : null; 3191ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (c == null) { 3201ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang c = mInitialConversation; 3211ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 3221ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return c; 3231ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 3241ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 325632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public int getConversationPosition(Conversation conv) { 3261ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (isPagingDisabled()) { 3277ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang if (getCursor() == null) { 3287ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang return POSITION_NONE; 3297ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang } 3307ce62d32cec32b94b3e7fd554ed017dc9bd998e7Andy Huang 3311ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (conv != getDefaultConversation()) { 3321ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.d(LOG_TAG, "unable to find conversation in singleton mode. c=%s", 333632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang conv); 334632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return POSITION_NONE; 335632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 336632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return 0; 337632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 338632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 339e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang final Cursor cursor = getCursor(); 3408775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang if (cursor == null) { 3418775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang return POSITION_NONE; 3428775ffa4b8c26965352f6ec284adba3867c651d1Andy Huang } 343e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang 344e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang final boolean networkWasEnabled = Utils.disableConversationCursorNetworkAccess(cursor); 345ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 346ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang int result = POSITION_NONE; 347632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang int pos = -1; 348e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang while (cursor.moveToPosition(++pos)) { 349e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang final long id = cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN); 350632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang if (conv.id == id) { 351632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.d(LOG_TAG, "pager adapter found repositioned convo '%s' at pos=%d", 352632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang conv.subject, pos); 353ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang result = pos; 354ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang break; 355632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 356632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 357ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 358ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang if (networkWasEnabled) { 359e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang Utils.enableConversationCursorNetworkAccess(cursor); 360ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang } 361ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 362ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang return result; 363632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 364632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 36522ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang public void setPager(ViewPager pager) { 3667d64612104fd861474a09994f13768360f92041dAndy Huang if (mPager != null) { 3677d64612104fd861474a09994f13768360f92041dAndy Huang mPager.setOnPageChangeListener(null); 3687d64612104fd861474a09994f13768360f92041dAndy Huang } 36922ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang mPager = pager; 3707d64612104fd861474a09994f13768360f92041dAndy Huang if (mPager != null) { 3717d64612104fd861474a09994f13768360f92041dAndy Huang mPager.setOnPageChangeListener(this); 3727d64612104fd861474a09994f13768360f92041dAndy Huang } 37322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang } 37422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang 375090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang public void setActivityController(ActivityController controller) { 376090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang if (mController != null) { 377090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang mController.unregisterConversationListObserver(mListObserver); 378860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang mController.unregisterFolderObserver(mFolderObserver); 379632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 380090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang mController = controller; 381090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang if (mController != null) { 382090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang mController.registerConversationListObserver(mListObserver); 383860164409d7f9e4f726ed4edb2ed77afc6873ad5Andy Huang mController.registerFolderObserver(mFolderObserver); 384632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 385e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang notifyDataSetChanged(); 386632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } else { 387e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang // We're being torn down; do not notify. 388e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang // Let the pager controller manage pager lifecycle. 389632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 390632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 391632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 3927d64612104fd861474a09994f13768360f92041dAndy Huang @Override 3937d64612104fd861474a09994f13768360f92041dAndy Huang public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 3947d64612104fd861474a09994f13768360f92041dAndy Huang // no-op 3957d64612104fd861474a09994f13768360f92041dAndy Huang } 3967d64612104fd861474a09994f13768360f92041dAndy Huang 3977d64612104fd861474a09994f13768360f92041dAndy Huang @Override 3987d64612104fd861474a09994f13768360f92041dAndy Huang public void onPageSelected(int position) { 399f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final AbstractConversationViewFragment f = 400f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp (AbstractConversationViewFragment) getFragmentAt(position); 4017d64612104fd861474a09994f13768360f92041dAndy Huang if (f != null && mController != null) { 4027d64612104fd861474a09994f13768360f92041dAndy Huang final Conversation c = f.getConversation(); 403dc2a2c1e7500f60e1d1cde1a6c8925a2763ada32mindyp c.position = position; 4047d64612104fd861474a09994f13768360f92041dAndy Huang LogUtils.d(LOG_TAG, "pager adapter setting current conv: %s (%s)", c.subject, f); 4057d64612104fd861474a09994f13768360f92041dAndy Huang mController.setCurrentConversation(c); 4067d64612104fd861474a09994f13768360f92041dAndy Huang } 4077d64612104fd861474a09994f13768360f92041dAndy Huang } 4087d64612104fd861474a09994f13768360f92041dAndy Huang 4097d64612104fd861474a09994f13768360f92041dAndy Huang @Override 4107d64612104fd861474a09994f13768360f92041dAndy Huang public void onPageScrollStateChanged(int state) { 4117d64612104fd861474a09994f13768360f92041dAndy Huang // no-op 4127d64612104fd861474a09994f13768360f92041dAndy Huang } 4137d64612104fd861474a09994f13768360f92041dAndy Huang 414090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang // update the pager title strip as the Folder's conversation count changes 415090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang private class FolderObserver extends DataSetObserver { 416090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang @Override 417090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang public void onChanged() { 418090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang notifyDataSetChanged(); 419090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang } 420090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang } 421090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang 422632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang // update the pager dataset as the Controller's cursor changes 423632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private class ListObserver extends DataSetObserver { 424632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 425632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void onChanged() { 426e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang notifyDataSetChanged(); 427632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 428632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang @Override 429632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public void onInvalidated() { 430632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 431632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 432632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 433632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang} 434