ConversationPagerController.java revision 87cf662c9c6c87e7f40dab6545c41484a7e7bfd3
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
205895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huangimport android.app.Fragment;
21632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.app.FragmentManager;
22632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.support.v4.view.ViewPager;
235895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huangimport android.support.v4.view.ViewPager.OnPageChangeListener;
2422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huangimport android.view.View;
25632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
26632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.R;
27632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Account;
28632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Conversation;
29632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.providers.Folder;
30632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.ui.AbstractActivityController;
315895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huangimport com.android.mail.ui.ActivityController;
32632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.ui.RestrictedActivity;
335895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huangimport com.android.mail.ui.SubjectDisplayChanger;
34b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
35632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
36632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
37632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang/**
38632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * A simple controller for a {@link ViewPager} of conversations.
39632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * <p>
40632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * Instead of placing a ViewPager in a Fragment that replaces the other app views, we leave a
41632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * ViewPager in the activity's view hierarchy at all times and have this controller manage it.
42632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * This allows the ViewPager to safely instantiate inner conversation fragments since it is not
43632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * itself contained in a Fragment (no nested fragments!).
44632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * <p>
45632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * This arrangement has pros and cons...<br>
46632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * pros: FragmentManager manages restoring conversation fragments, each conversation gets its own
47632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * LoaderManager<br>
48632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * cons: the activity's Controller has to specially handle show/hide conversation view,
49632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * conversation fragment transitions must be done manually
50632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * <p>
51632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * This controller is a small delegate of {@link AbstractActivityController} and shares its
52632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * lifetime.
53632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang *
54632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */
555895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huangpublic class ConversationPagerController implements OnPageChangeListener {
56632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
5722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang    private ViewPager mPager;
58632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private ConversationPagerAdapter mPagerAdapter;
59632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private FragmentManager mFragmentManager;
60090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    private ActivityController mActivityController;
615895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    private SubjectDisplayChanger mSubjectDisplayChanger;
62632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mShown;
63632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
64b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
65632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
66632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
67632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Enables an optimization to the PagerAdapter that causes ViewPager to initially load just the
68632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * target conversation, then when the conversation view signals that the conversation is loaded
69632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * and visible (via onConversationSeen), we switch to paged mode to load the left/right
70632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * adjacent conversations.
71632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * <p>
72632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * Should improve load times. It also works around an issue in ViewPager that always loads item
73632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * zero (with the fragment visibility hint ON) when the adapter is initially set.
74632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
7587cf662c9c6c87e7f40dab6545c41484a7e7bfd3Andy Huang    private static final boolean ENABLE_SINGLETON_INITIAL_LOAD = false;
76632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
77632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public ConversationPagerController(RestrictedActivity activity,
785895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang            ActivityController controller) {
79632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mFragmentManager = activity.getFragmentManager();
8022ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPager = (ViewPager) activity.findViewById(R.id.conversation_pane);
81090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mActivityController = controller;
825895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        mSubjectDisplayChanger = controller.getSubjectDisplayChanger();
83632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
84632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
85632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void show(Account account, Folder folder, Conversation initialConversation) {
86632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mShown) {
87632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "IN CPC.show, but already shown");
88b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang            // optimize for the case where account+folder are the same, when we can just shift
89b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang            // the existing pager to show the new conversation
90b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang            if (mPagerAdapter != null && mPagerAdapter.matches(account, folder)) {
91b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                final int pos = mPagerAdapter.getConversationPosition(initialConversation);
92b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                if (pos >= 0) {
93b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                    mPager.setCurrentItem(pos);
94b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                    return;
95b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang                }
96b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang            }
97b373e3ec0e488914635345627f5734af0f0ef76bAndy Huang            // unable to shift, destroy existing state and fall through to normal startup
98632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            cleanup();
99632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
100632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
10122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPager.setVisibility(View.VISIBLE);
10222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang
10322ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPagerAdapter = new ConversationPagerAdapter(mPager.getResources(), mFragmentManager,
10422ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang                account, folder, initialConversation);
105632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPagerAdapter.setSingletonMode(ENABLE_SINGLETON_INITIAL_LOAD);
106090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mPagerAdapter.setActivityController(mActivityController);
10722ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPagerAdapter.setPager(mPager);
108632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN CPC.show, adapter=%s", mPagerAdapter);
109632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
1105895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        mPager.setOnPageChangeListener(this);
1115895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
112632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "init pager adapter, count=%d initial=%s", mPagerAdapter.getCount(),
113632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                initialConversation.subject);
114632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPager.setAdapter(mPagerAdapter);
115632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
116632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (!ENABLE_SINGLETON_INITIAL_LOAD) {
117632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // FIXME: unnecessary to do this on restore. setAdapter will restore current position
118632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            final int initialPos = mPagerAdapter.getConversationPosition(initialConversation);
119632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.w(LOG_TAG, "*** pager fragment init pos=%d", initialPos);
120632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            mPager.setCurrentItem(initialPos);
121632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
122632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
123632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mShown = true;
124632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
125632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
126632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void hide() {
127632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (!mShown) {
128632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "IN CPC.hide, but already hidden");
129632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            return;
130632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
131632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mShown = false;
13222ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang        mPager.setVisibility(View.GONE);
133632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
1345895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        mSubjectDisplayChanger.clearSubject();
1355895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
136632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        LogUtils.d(LOG_TAG, "IN CPC.hide, clearing adapter and unregistering list observer");
137632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPager.setAdapter(null);
1385895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        mPager.setOnPageChangeListener(null);
139632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        cleanup();
140632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
141632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
142632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onDestroy() {
143632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // need to release resources before a configuration change kills the activity and controller
144632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        cleanup();
145632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
146632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
147632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private void cleanup() {
148632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mPagerAdapter != null) {
149632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // stop observing the conversation list
150090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang            mPagerAdapter.setActivityController(null);
15122ecc77345c86e0515c1f3c3447502c25c8c53d2Andy Huang            mPagerAdapter.setPager(null);
152632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            mPagerAdapter = null;
153632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
154632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
155632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
156632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onConversationSeen(Conversation conv) {
157632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // take the adapter out of singleton mode to begin loading the
158632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // other non-visible conversations
159dd43af4c9b0450350b65bf77fe87f4680b6a786fMindy Pereira        if (mPagerAdapter != null && mPagerAdapter.isSingletonMode()) {
160632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            LogUtils.d(LOG_TAG, "IN pager adapter, finished loading primary conversation," +
161632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                    " switching to cursor mode to load other conversations");
162632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            mPagerAdapter.setSingletonMode(false);
163632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
164632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
1655895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
1665895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    @Override
1675895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
1685895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        // no-op
1695895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    }
1705895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
1715895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    @Override
1725895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    public void onPageSelected(int position) {
1735895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        final Fragment f = mPagerAdapter.getFragmentAt(position);
1745895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        if (f != null) {
1755895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang            mPagerAdapter.setItemVisible(f, true);
1765895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        }
1775895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    }
1785895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
1795895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    @Override
1805895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    public void onPageScrollStateChanged(int state) {
1815895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        // no-op
1825895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang    }
183192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal
184192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal    /**
185192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal     * Stops listening to changes to the adapter. This must be followed immediately by
186192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal     * {@link #hide()}.
187192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal     */
188192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal    public void stopListening() {
189192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        mPagerAdapter.setActivityController(null);
190192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal    }
191632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang}
192