content: /*
   * Copyright (C) 2015 AlexMofer
   *
   * 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 io.github.alexmofer.projectx.widget;
  
  import android.annotation.SuppressLint;
  import android.os.Bundle;
  import android.os.Parcelable;
  import android.util.Log;
  import android.view.View;
  import android.view.ViewGroup;
  
  import java.util.ArrayList;
  
  import androidx.annotation.NonNull;
  import androidx.fragment.app.Fragment;
  import androidx.fragment.app.FragmentManager;
  import androidx.fragment.app.FragmentStatePagerAdapter;
  import androidx.fragment.app.FragmentTransaction;
  import androidx.viewpager.widget.PagerAdapter;
  
  /**
   * Implementation of {@link PagerAdapter} that
   * represents each page as a {@link Fragment} that is persistently
   * kept in the fragment manager as long as the user can return to the page.
   * <p>
   * <p>This version of the pager is best for use when there are a handful of
   * typically more static fragments to be paged through, such as a set of tabs.
   * The fragment of each page the user visits will be kept in memory, though its
   * view hierarchy may be destroyed when not visible.  This can result in using
   * a significant amount of memory since fragment instances can hold on to an
   * arbitrary amount of state.  For larger sets of pages, consider
   * {@link FragmentStatePagerAdapter}.
   * <p>
   * <p>When using FragmentPagerAdapter the host ViewPager must have a
   * valid ID set.</p>
   * <p>
   * <p>Subclasses only need to implement {@link #getItem(int)}
   * and {@link #getCount()} to have a working adapter.
   * <p>
   * <p>Here is an example implementation of a pager containing fragments of
   * lists:
   * <p>
   * {@see frameworks/support/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentPagerSupport.java
   * complete}
   * <p>
   * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
   * <p>
   * {@see frameworks/support/samples/Support4Demos/res/layout/fragment_pager.xml
   * complete}
   * <p>
   * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
   * individual fragment's layout is:
   * <p>
   * {@see frameworks/support/samples/Support4Demos/res/layout/fragment_pager_list.xml
   * complete}
   */
  @SuppressWarnings({"WeakerAccess", "unused"})
  public abstract class FragmentRemovePagerAdapter extends PagerAdapter {
      private static final String TAG = FragmentRemovePagerAdapter.class.getSimpleName();
      private static final boolean DEBUG = false;
  
      private final FragmentManager mFragmentManager;
      private FragmentTransaction mCurTransaction = null;
  
      private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
      private Fragment mCurrentPrimaryItem = null;
      private int mViewGroupId;
  
      public FragmentRemovePagerAdapter(FragmentManager fm) {
          mFragmentManager = fm;
      }
  
      private static String makeFragmentName(int viewId, long id) {
          return "android:switcher:" + viewId + ":" + id;
      }
  
      /**
       * Return the Fragment associated with a specified position.
       */
      public abstract Fragment getItem(int position);
  
      @Override
      public void startUpdate(@NonNull ViewGroup container) {
          mViewGroupId = container.getId();
          if (mViewGroupId == View.NO_ID) {
              throw new IllegalStateException("ViewPager with adapter " + this
                      + " requires a view id");
          }
      }
  
      @SuppressLint("CommitTransaction")
      @NonNull
      @Override
      public Object instantiateItem(@NonNull ViewGroup container, int position) {
          if (mCurTransaction == null) {
              mCurTransaction = mFragmentManager.beginTransaction();
          }
  
          final long itemId = getItemId(position);
  
          // Do we already have this fragment?
          String name = makeFragmentName(container.getId(), itemId);
          Fragment fragment = mFragmentManager.findFragmentByTag(name);
          if (fragment != null) {
              if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
              mCurTransaction.attach(fragment);
          } else {
              fragment = getItem(position);
              if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
              if (mSavedState.size() > position) {
                  Fragment.SavedState fss = mSavedState.get(position);
                  if (fss != null) {
                      fragment.setInitialSavedState(fss);
                  }
              }
              mCurTransaction.add(container.getId(), fragment,
                      makeFragmentName(container.getId(), itemId));
          }
          if (fragment != mCurrentPrimaryItem) {
              fragment.setMenuVisibility(false);
              fragment.setUserVisibleHint(false);
          }
  
          return fragment;
      }
  
      @SuppressLint("CommitTransaction")
      @Override
      public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
          if (mCurTransaction == null) {
              mCurTransaction = mFragmentManager.beginTransaction();
          }
          if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                  + " v=" + ((Fragment) object).getView());
          mCurTransaction.detach((Fragment) object);
      }
  
      @Override
      public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
          Fragment fragment = (Fragment) object;
          if (fragment != mCurrentPrimaryItem) {
              if (mCurrentPrimaryItem != null) {
                  mCurrentPrimaryItem.setMenuVisibility(false);
                  mCurrentPrimaryItem.setUserVisibleHint(false);
              }
              fragment.setMenuVisibility(true);
              fragment.setUserVisibleHint(true);
              mCurrentPrimaryItem = fragment;
          }
      }
  
      @Override
      public void finishUpdate(@NonNull ViewGroup container) {
          if (mCurTransaction != null) {
              mCurTransaction.commitNowAllowingStateLoss();
              mCurTransaction = null;
          }
      }
  
      @Override
      public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
          return ((Fragment) object).getView() == view;
      }
  
      @Override
      public Parcelable saveState() {
          Bundle state = null;
          if (mSavedState.size() > 0) {
              state = new Bundle();
              Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
              mSavedState.toArray(fss);
              state.putParcelableArray("states", fss);
          }
          final int count = getCount();
          for (int position = 0; position < count; position++) {
              final long itemId = getItemId(position);
              String name = makeFragmentName(mViewGroupId, itemId);
              Fragment fragment = mFragmentManager.findFragmentByTag(name);
              if (fragment != null && fragment.isAdded()) {
                  if (state == null) {
                      state = new Bundle();
                  }
                  String key = "f" + itemId;
                  mFragmentManager.putFragment(state, key, fragment);
              }
          }
          return state;
      }
  
      @Override
      public void restoreState(Parcelable state, ClassLoader loader) {
          if (state != null) {
              Bundle bundle = (Bundle) state;
              bundle.setClassLoader(loader);
              Parcelable[] fss = bundle.getParcelableArray("states");
              mSavedState.clear();
              if (fss != null) {
                  for (Parcelable fs : fss) {
                      mSavedState.add((Fragment.SavedState) fs);
                  }
              }
              Iterable<String> keys = bundle.keySet();
              for (String key : keys) {
                  if (key.startsWith("f")) {
                      Fragment f = mFragmentManager.getFragment(bundle, key);
                      if (f != null) {
                          f.setMenuVisibility(false);
                      } else {
                          Log.w(TAG, "Bad fragment at key " + key);
                      }
                  }
              }
          }
      }
  
      /**
       * Return a unique identifier for the item at the given position.
       * <p>
       * <p>The default implementation returns the given position.
       * Subclasses should override this method if the positions of items can change.</p>
       *
       * @param position Position within this adapter
       * @return Unique identifier for the item at position
       */
      public long getItemId(int position) {
          return position;
      }
  
      /**
       * Remove all item
       */
      public void removeAll() {
          if (mCurTransaction == null) {
              mCurTransaction = mFragmentManager.beginTransaction();
          }
          final int count = getCount();
          for (int position = 0; position < count; position++) {
              final long itemId = getItemId(position);
              String name = makeFragmentName(mViewGroupId, itemId);
              Fragment fragment = mFragmentManager.findFragmentByTag(name);
              if (fragment == null)
                  continue;
              if (DEBUG) Log.v(TAG, "Detaching item #" + itemId + ": f=" + fragment
                      + " v=" + fragment.getView());
              while (mSavedState.size() <= getCount()) {
                  mSavedState.add(null);
              }
              mSavedState.set(position, fragment.isAdded()
                      ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
              mCurTransaction.remove(fragment);
          }
          mCurTransaction.commitNowAllowingStateLoss();
          mCurTransaction = null;
      }
  }
  
  