• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 androidx.legacy.app.FragmentCompat;
25 import androidx.legacy.app.FragmentStatePagerAdapter;
26 import androidx.collection.SparseArrayCompat;
27 import androidx.viewpager.widget.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 
FragmentStatePagerAdapter2(FragmentManager fm)58     public FragmentStatePagerAdapter2(FragmentManager fm) {
59         this(fm, true);
60     }
61 
FragmentStatePagerAdapter2(FragmentManager fm, boolean enableSavedStates)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      */
getItem(int position)70     public abstract Fragment getItem(int position);
71 
72     @Override
startUpdate(ViewGroup container)73     public void startUpdate(ViewGroup container) {
74     }
75 
76     @Override
instantiateItem(ViewGroup container, int position)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
destroyItem(ViewGroup container, int position, Object object)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
setPrimaryItem(ViewGroup container, int position, Object object)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
finishUpdate(ViewGroup container)143     public void finishUpdate(ViewGroup container) {
144         if (mCurTransaction != null) {
145             mCurTransaction.commitAllowingStateLoss();
146             mCurTransaction = null;
147             mFragmentManager.executePendingTransactions();
148         }
149     }
150 
151     @Override
isViewFromObject(View view, Object object)152     public boolean isViewFromObject(View view, Object object) {
153         return ((Fragment)object).getView() == view;
154     }
155 
156     @Override
saveState()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
restoreState(Parcelable state, ClassLoader loader)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 
setItemVisible(Fragment item, boolean visible)208     public void setItemVisible(Fragment item, boolean visible) {
209         FragmentCompat.setMenuVisibility(item, visible);
210         FragmentCompat.setUserVisibleHint(item, visible);
211     }
212 
213     @Override
notifyDataSetChanged()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 
getFragmentAt(int position)233     public Fragment getFragmentAt(int position) {
234         return mFragments.get(position);
235     }
236 }
237