1f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/*
2f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Copyright (C) 2011 Google Inc.
3f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed to The Android Open Source Project.
4f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
5f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License");
6f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * you may not use this file except in compliance with the License.
7f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * You may obtain a copy of the License at
8f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
9f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *      http://www.apache.org/licenses/LICENSE-2.0
10f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
11f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Unless required by applicable law or agreed to in writing, software
12f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS,
13f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * See the License for the specific language governing permissions and
15f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * limitations under the License.
16f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */
17f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
18f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpackage com.android.ex.photo.adapters;
19f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
20f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Parcelable;
218746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.Fragment;
228746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.FragmentManager;
238746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.FragmentTransaction;
24f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.PagerAdapter;
25f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.util.Log;
26f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.util.LruCache;
27f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.view.View;
28f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
29f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/**
30f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * NOTE: This is a direct copy of {@link FragmentPagerAdapter} with four very important
31f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * modifications.
32f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <p>
33f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <ol>
34f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>The method {@link #makeFragmentName(int, int)} is declared "protected"
35f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * in our class. We need to be able to re-define the fragment's name according to data
36f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * only available to sub-classes.</li>
37f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search
38f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * the entire view hierarchy for the given view.</li>
39f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and
40f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * added to a cache. If the fragment is evicted from the cache, it will be deleted.
41f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * An album may contain thousands of photos and we want to avoid having thousands of
42f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * fragments.</li>
43f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * </ol>
44f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */
45f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpublic abstract class BaseFragmentPagerAdapter extends PagerAdapter {
46f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The default size of {@link #mFragmentCache} */
47f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private static final int DEFAULT_CACHE_SIZE = 5;
48f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private static final String TAG = "FragmentPagerAdapter";
49f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private static final boolean DEBUG = false;
50f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
51f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private final FragmentManager mFragmentManager;
52f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private FragmentTransaction mCurTransaction = null;
53f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private Fragment mCurrentPrimaryItem = null;
54f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** A cache to store detached fragments before they are removed  */
55f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE);
56f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
578746927a945358bb9e515985a37cac7807261026Martin Hibdon    public BaseFragmentPagerAdapter(android.support.v4.app.FragmentManager fm) {
58f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mFragmentManager = fm;
59f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
60f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
61f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
62f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Return the Fragment associated with a specified position.
63f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
64f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public abstract Fragment getItem(int position);
65f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
66f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
67f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void startUpdate(View container) {
68f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
69f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
70f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
71f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Object instantiateItem(View container, int position) {
72f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mCurTransaction == null) {
73f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction = mFragmentManager.beginTransaction();
74f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
75f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
76f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // Do we already have this fragment?
77f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        String name = makeFragmentName(container.getId(), position);
78f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
79f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // Remove item from the cache
80f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mFragmentCache.remove(name);
81f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
82f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        Fragment fragment = mFragmentManager.findFragmentByTag(name);
83f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (fragment != null) {
84f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
85f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction.attach(fragment);
86f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
87f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            fragment = getItem(position);
880ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp            if(fragment == null) {
890ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp                if (DEBUG) Log.e(TAG, "NPE workaround for getItem(). See b/7103023");
900ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp                return null;
910ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp            }
92f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
93f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction.add(container.getId(), fragment,
94f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                    makeFragmentName(container.getId(), position));
95f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
96f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (fragment != mCurrentPrimaryItem) {
97f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            fragment.setMenuVisibility(false);
98f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
99f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
100f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return fragment;
101f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
102f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
103f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
104f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void destroyItem(View container, int position, Object object) {
105f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mCurTransaction == null) {
106f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction = mFragmentManager.beginTransaction();
107f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
108f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object
109f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                + " v=" + ((Fragment)object).getView());
110f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        Fragment fragment = (Fragment) object;
112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        String name = fragment.getTag();
113f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (name == null) {
114f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // We prefer to get the name directly from the fragment, but, if the fragment is
115f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // detached before the add transaction is committed, this could be 'null'. In
116f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // that case, generate a name so we can still cache the fragment.
117f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            name = makeFragmentName(container.getId(), position);
118f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
119f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
120f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mFragmentCache.put(name, fragment);
121f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mCurTransaction.detach(fragment);
122f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
123f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
124f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
125f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void setPrimaryItem(View container, int position, Object object) {
126f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        Fragment fragment = (Fragment) object;
127f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (fragment != mCurrentPrimaryItem) {
128f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (mCurrentPrimaryItem != null) {
129f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mCurrentPrimaryItem.setMenuVisibility(false);
130f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
131f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (fragment != null) {
132f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                fragment.setMenuVisibility(true);
133f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
134f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurrentPrimaryItem = fragment;
135f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
136f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
137f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
138f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
139f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
140f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void finishUpdate(View container) {
141f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mCurTransaction != null) {
142f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction.commitAllowingStateLoss();
143f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mCurTransaction = null;
144f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mFragmentManager.executePendingTransactions();
145f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
146f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
147f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
148f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
149f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public boolean isViewFromObject(View view, Object object) {
150f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // Ascend the tree to determine if the view is a child of the fragment
151f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        View root = ((Fragment) object).getView();
152f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        for (Object v = view; v instanceof View; v = ((View) v).getParent()) {
153f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (v == root) {
154f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                return true;
155f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
156f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
157f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return false;
158f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
159f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
160f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
161f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Parcelable saveState() {
162f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return null;
163f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
164f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
165f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
166f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void restoreState(Parcelable state, ClassLoader loader) {
167f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
168f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
169f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Creates a name for the fragment */
170f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected String makeFragmentName(int viewId, int index) {
171f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return "android:switcher:" + viewId + ":" + index;
172f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
173f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
174f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
175f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * A cache of detached fragments.
176f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
177f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private class FragmentCache extends LruCache<String, Fragment> {
178f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        public FragmentCache(int size) {
179f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            super(size);
180f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
181f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
182f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        @Override
183f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        protected void entryRemoved(boolean evicted, String key,
184f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                Fragment oldValue, Fragment newValue) {
185f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // remove the fragment if it's evicted OR it's replaced by a new fragment
186f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (evicted || (newValue != null && oldValue != newValue)) {
187f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mCurTransaction.remove(oldValue);
188f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
189f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
190f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
191f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein}
192