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