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