content: /*
   * Copyright (C) 2011 Google Inc.
   * Licensed to The Android Open Source Project.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.ex.photo.adapters;
  
  import android.os.Parcelable;
  import androidx.fragment.app.Fragment;
  import androidx.fragment.app.FragmentManager;
  import androidx.fragment.app.FragmentTransaction;
  import androidx.collection.LruCache;
  import androidx.viewpager.widget.PagerAdapter;
  import android.util.Log;
  import android.view.View;
  
  /**
   * NOTE: This is a direct copy of {@link androidx.fragment.app.FragmentPagerAdapter}
   * with four very important modifications.
   * <p>
   * <ol>
   * <li>The method {@link #makeFragmentName(int, int)} is declared "protected"
   * in our class. We need to be able to re-define the fragment's name according to data
   * only available to sub-classes.</li>
   * <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search
   * the entire view hierarchy for the given view.</li>
   * <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and
   * added to a cache. If the fragment is evicted from the cache, it will be deleted.
   * An album may contain thousands of photos and we want to avoid having thousands of
   * fragments.</li>
   * </ol>
   */
  public abstract class BaseFragmentPagerAdapter extends PagerAdapter {
      /** The default size of {@link #mFragmentCache} */
      private static final int DEFAULT_CACHE_SIZE = 5;
      private static final String TAG = "FragmentPagerAdapter";
      private static final boolean DEBUG = false;
  
      private final FragmentManager mFragmentManager;
      private FragmentTransaction mCurTransaction = null;
      private Fragment mCurrentPrimaryItem = null;
      /** A cache to store detached fragments before they are removed  */
      private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE);
  
      public BaseFragmentPagerAdapter(androidx.fragment.app.FragmentManager fm) {
          mFragmentManager = fm;
      }
  
      /**
       * Return the Fragment associated with a specified position.
       */
      public abstract Fragment getItem(int position);
  
      @Override
      public void startUpdate(View container) {
      }
  
      @Override
      public Object instantiateItem(View container, int position) {
          if (mCurTransaction == null) {
              mCurTransaction = mFragmentManager.beginTransaction();
          }
  
          // Do we already have this fragment?
          String name = makeFragmentName(container.getId(), position);
  
          // Remove item from the cache
          mFragmentCache.remove(name);
  
          Fragment fragment = mFragmentManager.findFragmentByTag(name);
          if (fragment != null) {
              if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
              mCurTransaction.attach(fragment);
          } else {
              fragment = getItem(position);
              if(fragment == null) {
                  if (DEBUG) Log.e(TAG, "NPE workaround for getItem(). See b/7103023");
                  return null;
              }
              if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
              mCurTransaction.add(container.getId(), fragment,
                      makeFragmentName(container.getId(), position));
          }
          if (fragment != mCurrentPrimaryItem) {
              fragment.setMenuVisibility(false);
          }
  
          return fragment;
      }
  
      @Override
      public void destroyItem(View container, int position, Object object) {
          if (mCurTransaction == null) {
              mCurTransaction = mFragmentManager.beginTransaction();
          }
          if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object
                  + " v=" + ((Fragment)object).getView());
  
          Fragment fragment = (Fragment) object;
          String name = fragment.getTag();
          if (name == null) {
              // We prefer to get the name directly from the fragment, but, if the fragment is
              // detached before the add transaction is committed, this could be 'null'. In
              // that case, generate a name so we can still cache the fragment.
              name = makeFragmentName(container.getId(), position);
          }
  
          mFragmentCache.put(name, fragment);
          mCurTransaction.detach(fragment);
      }
  
      @Override
      public void setPrimaryItem(View container, int position, Object object) {
          Fragment fragment = (Fragment) object;
          if (fragment != mCurrentPrimaryItem) {
              if (mCurrentPrimaryItem != null) {
                  mCurrentPrimaryItem.setMenuVisibility(false);
              }
              if (fragment != null) {
                  fragment.setMenuVisibility(true);
              }
              mCurrentPrimaryItem = fragment;
          }
  
      }
  
      @Override
      public void finishUpdate(View container) {
          if (mCurTransaction != null && !mFragmentManager.isDestroyed()) {
              mCurTransaction.commitAllowingStateLoss();
              mCurTransaction = null;
              mFragmentManager.executePendingTransactions();
          }
      }
  
      @Override
      public boolean isViewFromObject(View view, Object object) {
          // Ascend the tree to determine if the view is a child of the fragment
          View root = ((Fragment) object).getView();
          for (Object v = view; v instanceof View; v = ((View) v).getParent()) {
              if (v == root) {
                  return true;
              }
          }
          return false;
      }
  
      @Override
      public Parcelable saveState() {
          return null;
      }
  
      @Override
      public void restoreState(Parcelable state, ClassLoader loader) {
      }
  
      /** Creates a name for the fragment */
      protected String makeFragmentName(int viewId, int index) {
          return "android:switcher:" + viewId + ":" + index;
      }
  
      /**
       * A cache of detached fragments.
       */
      private class FragmentCache extends LruCache<String, Fragment> {
          public FragmentCache(int size) {
              super(size);
          }
  
          @Override
          protected void entryRemoved(boolean evicted, String key,
                  Fragment oldValue, Fragment newValue) {
              // remove the fragment if it's evicted OR it's replaced by a new fragment
              if (evicted || (newValue != null && oldValue != newValue)) {
                  mCurTransaction.remove(oldValue);
              }
          }
      }
  }
  