FragmentStatePagerAdapter2.java revision 5895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mail.utils;
18
19import android.app.Fragment;
20import android.app.FragmentManager;
21import android.app.FragmentTransaction;
22import android.os.Bundle;
23import android.os.Parcelable;
24import android.support.v13.app.FragmentCompat;
25import android.support.v13.app.FragmentStatePagerAdapter;
26import android.support.v4.util.SparseArrayCompat;
27import android.support.v4.view.PagerAdapter;
28import android.util.Log;
29import android.view.View;
30import android.view.ViewGroup;
31
32import java.util.ArrayList;
33
34/**
35 * Forked from support lib's {@link FragmentStatePagerAdapter}, with some minor
36 * changes that couldn't be accomplished through subclassing:
37 * <ul>
38 * <li>optionally disable stateful behavior when paging (controlled by {@link #mEnableSavedStates}),
39 * for situations where state save/restore when paging is unnecessary</li>
40 * <li>override-able {@link #setItemVisible(Fragment, boolean)} method for subclasses to
41 * add supplemental handling of visibility hints manually on pre-v15 devices</li>
42 * <li>add support to handle data set changes that cause item positions to change</li>
43 * <li>allow read access to existing Fragments by index ({@link #getFragmentAt(int)})</li>
44 * </ul>
45 */
46public abstract class FragmentStatePagerAdapter2 extends PagerAdapter {
47    private static final String TAG = "FragmentStatePagerAdapter";
48    private static final boolean DEBUG = false;
49
50    private final FragmentManager mFragmentManager;
51    private FragmentTransaction mCurTransaction = null;
52
53    private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
54    private SparseArrayCompat<Fragment> mFragments = new SparseArrayCompat<Fragment>();
55    private Fragment mCurrentPrimaryItem = null;
56
57    private boolean mEnableSavedStates;
58
59    public FragmentStatePagerAdapter2(FragmentManager fm) {
60        this(fm, true);
61    }
62
63    public FragmentStatePagerAdapter2(FragmentManager fm, boolean enableSavedStates) {
64        mFragmentManager = fm;
65        mEnableSavedStates = enableSavedStates;
66    }
67
68    /**
69     * Return the Fragment associated with a specified position.
70     */
71    public abstract Fragment getItem(int position);
72
73    @Override
74    public void startUpdate(ViewGroup container) {
75    }
76
77    @Override
78    public Object instantiateItem(ViewGroup container, int position) {
79        // If we already have this item instantiated, there is nothing
80        // to do.  This can happen when we are restoring the entire pager
81        // from its saved state, where the fragment manager has already
82        // taken care of restoring the fragments we previously had instantiated.
83        final Fragment existing = mFragments.get(position);
84        if (existing != null) {
85            return existing;
86        }
87
88        if (mCurTransaction == null) {
89            mCurTransaction = mFragmentManager.beginTransaction();
90        }
91
92        Fragment fragment = getItem(position);
93        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
94        if (mEnableSavedStates && mSavedState.size() > position) {
95            Fragment.SavedState fss = mSavedState.get(position);
96            if (fss != null) {
97                fragment.setInitialSavedState(fss);
98            }
99        }
100        if (fragment != mCurrentPrimaryItem) {
101            setItemVisible(fragment, false);
102        }
103        mFragments.put(position, fragment);
104        mCurTransaction.add(container.getId(), fragment);
105
106        return fragment;
107    }
108
109    @Override
110    public void destroyItem(ViewGroup container, int position, Object object) {
111        Fragment fragment = (Fragment)object;
112
113        if (mCurTransaction == null) {
114            mCurTransaction = mFragmentManager.beginTransaction();
115        }
116        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
117                + " v=" + ((Fragment)object).getView());
118        if (mEnableSavedStates) {
119            while (mSavedState.size() <= position) {
120                mSavedState.add(null);
121            }
122            mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
123        }
124        mFragments.delete(position);
125
126        mCurTransaction.remove(fragment);
127    }
128
129    @Override
130    public void setPrimaryItem(ViewGroup container, int position, Object object) {
131        Fragment fragment = (Fragment)object;
132        if (fragment != mCurrentPrimaryItem) {
133            if (mCurrentPrimaryItem != null) {
134                setItemVisible(mCurrentPrimaryItem, false);
135            }
136            if (fragment != null) {
137                setItemVisible(fragment, true);
138            }
139            mCurrentPrimaryItem = fragment;
140        }
141    }
142
143    @Override
144    public void finishUpdate(ViewGroup container) {
145        if (mCurTransaction != null) {
146            mCurTransaction.commitAllowingStateLoss();
147            mCurTransaction = null;
148            mFragmentManager.executePendingTransactions();
149        }
150    }
151
152    @Override
153    public boolean isViewFromObject(View view, Object object) {
154        return ((Fragment)object).getView() == view;
155    }
156
157    @Override
158    public Parcelable saveState() {
159        Bundle state = null;
160        if (mEnableSavedStates && mSavedState.size() > 0) {
161            state = new Bundle();
162            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
163            mSavedState.toArray(fss);
164            state.putParcelableArray("states", fss);
165        }
166        for (int i=0; i<mFragments.size(); i++) {
167            final int pos = mFragments.keyAt(i);
168            final Fragment f = mFragments.valueAt(i);
169            if (state == null) {
170                state = new Bundle();
171            }
172            String key = "f" + pos;
173            mFragmentManager.putFragment(state, key, f);
174        }
175        return state;
176    }
177
178    @Override
179    public void restoreState(Parcelable state, ClassLoader loader) {
180        if (state != null) {
181            Bundle bundle = (Bundle)state;
182            bundle.setClassLoader(loader);
183            mFragments.clear();
184            if (mEnableSavedStates) {
185                Parcelable[] fss = bundle.getParcelableArray("states");
186                mSavedState.clear();
187                if (fss != null) {
188                    for (int i=0; i<fss.length; i++) {
189                        mSavedState.add((Fragment.SavedState)fss[i]);
190                    }
191                }
192            }
193            Iterable<String> keys = bundle.keySet();
194            for (String key: keys) {
195                if (key.startsWith("f")) {
196                    int index = Integer.parseInt(key.substring(1));
197                    Fragment f = mFragmentManager.getFragment(bundle, key);
198                    if (f != null) {
199                        setItemVisible(f, false);
200                        mFragments.put(index, f);
201                    } else {
202                        Log.w(TAG, "Bad fragment at key " + key);
203                    }
204                }
205            }
206        }
207    }
208
209    public void setItemVisible(Fragment item, boolean visible) {
210        FragmentCompat.setMenuVisibility(item, visible);
211        FragmentCompat.setUserVisibleHint(item, visible);
212    }
213
214    @Override
215    public void notifyDataSetChanged() {
216        super.notifyDataSetChanged();
217
218        // update positions in mFragments
219        for (int i=0; i<mFragments.size(); i++) {
220            final int oldPos = mFragments.keyAt(i);
221            final Fragment f = mFragments.valueAt(i);
222            final int newPos = getItemPosition(f);
223
224            if (newPos >= 0 && newPos != oldPos) {
225                // move
226                mFragments.removeAt(i);
227                mFragments.put(newPos, f);
228            } else if (newPos == POSITION_NONE) {
229                // remove
230                mFragments.removeAt(i);
231            }
232        }
233    }
234
235    public Fragment getFragmentAt(int position) {
236        return mFragments.get(position);
237    }
238}
239